【问题标题】:exception handling through mvvm android通过 mvvm android 处理异常
【发布时间】:2019-04-25 17:39:06
【问题描述】:

我正在使用 MVVM 架构通过在 android studio 中进行改造来实现 Web 服务。我已经在我的视图类中处理了服务的响应。但我面临的问题是如何处理异常并将它们传递给我的视图类。一种方法是在我的 Bean 类中创建构造函数并将响应和错误都传递给它并更新 UI。但我想要更优化的方式来处理 UI 内的异常。

这是我的存储库代码:

final MutableLiveData<MyBeanClass> myBeanClass = new MutableLiveData<>();
   ApiInterface apiInterface = ApiClient.getClientAuthentication().create(ApiInterface.class);
    Call<MyBeanClass> call = apiInterface.getData(id);
    call.enqueue(new Callback<MyBeanClass>() {
        @Override
        public void onResponse(Call<MyBeanClass> call, Response<MyBeanClass> response) {
            if(response.body()!=null) {
                myBeanClass.setValue(response.body());
            }
        }

        @Override
        public void onFailure(Call<MyBeanClass> call, Throwable t) {
         //How to handle exceptions here and pass the exception to UI without making constructor in bean class
        }
    });

    return myBeanClass;

【问题讨论】:

  • 另一种方法是获取另一个实时数据的错误,将错误设置为它并在您的 UI 上观察它。
  • 你能通过一些代码解释一下吗?
  • 嗨,Ashmeet,你是​​怎么处理的?我也面临同样的问题,不确定处理不同错误+异常发生的过程

标签: android mvvm exception-handling retrofit


【解决方案1】:

这是带有错误处理的 mvvm 的完整实现。 首先创建一个具有 UI 状态和资源的类。

public class Resource<T> {

    @NonNull public final Status status;
    @Nullable public final T data;
    @Nullable public final String message;

    private Resource(@NonNull Status status, @Nullable T data,
                     @Nullable String message) {
        this.status = status;
        this.data = data;
        this.message = message;
    }

    public static <T> Resource<T> success(@NonNull T data) {
        return new Resource<>(Status.SUCCESS, data, null);
    }

    public static <T> Resource<T> error(String msg, @Nullable T data) {
        return new Resource<>(Status.ERROR, data, msg);
    }

    public static <T> Resource<T> loading(@Nullable T data) {
        return new Resource<>(Status.LOADING, data, null);
    }


    public enum Status { SUCCESS, ERROR, LOADING }
}

在你的 Repository 类上做

public LiveData<Resource<MyBeanClass>> getDetail(String movieId) {
        final MutableLiveData<Resource<MyBeanClass>> myBeanClass = new MutableLiveData<>();
        ApiInterface apiInterface = new ApiClient().getClient().create(ApiInterface.class);

   Call<MyBeanClass> call = apiInterface.getData(id);
        call.enqueue(new Callback<MyBeanClass>() {

            @Override
            public void onResponse(Call<MyBeanClass> call, Response<MyBeanClass> response) {
                if (response.body() != null) {
                    MyBeanClass body = response.body();
                    myBeanClass.setValue(Resource.success(body));
                }
            }

            @Override
            public void onFailure(Call<MyBeanClass> call, Throwable t) {
                myBeanClass.setValue(Resource.error(t.getMessage(),null));
            }
        });

        return myBeanClass;
    }

在你的 viewModel 类上

public class MyViewModel extends ViewModel {

    private LiveData<Resource<MyBeanClass>> myLiveData;

    public void init(String id) {
        movieLiveData = new MyRepository().getInstance().getDetail(id);

    }

    
    public LiveData<Resource<MyBeanClass>> getMyLiveData() {
        return myLiveData;
    }
}

在你的活动课上

final MyViewModel viewModel =
                ViewModelProviders.of(this).get(MyViewModel.class);
        viewModel.init(id);
        viewModel.getMyLiveData().observe(this, finalData -> {

            switch (finalData.status) {
                case SUCCESS:
                    loadDetail(finalData.data);
                    break;
                case LOADING:
                    break;
                case ERROR:
                    Toast.makeText(this, "no Internet", Toast.LENGTH_SHORT).show();
                    break;
            }

        });

如果有帮助,别忘了投票。快乐编码

【讨论】:

    【解决方案2】:

    而不是创建两个 Mutable 类。 您可以为错误和成功状态甚至加载状态创建一个包装器对象

    data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
        companion object {
            fun <T> success(data: T?): Resource<T> {
                return Resource(SUCCESS, data, null)
            }
    
            fun <T> error(msg: String, data: T?): Resource<T> {
                return Resource(ERROR, data, msg)
            }
    
            fun <T> loading(data: T?): Resource<T> {
                return Resource(LOADING, data, null)
            }
        }
    }
    

    然后使用 MutableLive 数据作为这个类型

    final MutableLiveData<Resource<MyBeanClass>> myBeanClass = new MutableLiveData<>();
               ApiInterface apiInterface = ApiClient.getClientAuthentication().create(ApiInterface.class);
                Call<MyBeanClass> call = apiInterface.getData(id);
                call.enqueue(new Callback<MyBeanClass>() {
                    @Override
                    public void onResponse(Call<MyBeanClass> call, Response<MyBeanClass> response) {
                        if(response.body()!=null) {
                        myBeanClass.setValue(Resource<MyBeanClass>.success(response.body));
                        }
                    }
    
                    @Override
                    public void onFailure(Call<MyBeanClass> call, Throwable t) {
         myBeanClass.setValue(Resource<MyBeanClass>.error(t.getLocalizedMessage()));
                    }
                });
    
                return myBeanClass;
    

    您可以查看这个 google 示例 https://github.com/googlesamples/android-architecture-components/tree/master/GithubBrowserSample

    【讨论】:

    • 是的,我这样做了..现在我的问题是如何处理 UI 中的异常..?
    • 在你的对象中放入一个整数作为属性来指示资源对象(Mybeanclass)的状态,例如,如果资源是成功的,它将为 1,如果资源失败,它将为 0 case你可以检查对象的整数值是1还是0并处理它
    • 但我的意思是如何将特定异常的名称从存储库中获取到 UI?
    • 使用包装类的情况下无法获取改造数据,破坏了数据响应的结构
    【解决方案3】:

    您可以在此处进行交互。从 onFailure 调用接口方法并从 UI 端提供该接口的实现,因此每当出现错误时,yiu 将在您的 UI 端

    【讨论】:

      【解决方案4】:

      查看如何从您的存储库代码中完成:

      //Take it globally in your repository class, and provide getter for it.
      final MutableLiveData<MyBeanClass> myBeanClass = new MutableLiveData<>();
      final MutableLiveData<Throwable> error = new MutableLiveData<>();
      
      public void someApiCallMethod() {
          // In your method call for API
          ApiInterface apiInterface = 
          ApiClient.getClientAuthentication().create(ApiInterface.class);
          Call<MyBeanClass> call = apiInterface.getData(id);
          call.enqueue(new Callback<MyBeanClass>() {
              @Override
              public void onResponse(Call<MyBeanClass> call, Response<MyBeanClass> response) {
                  if(response.body()!=null) {
                      myBeanClass.setValue(response.body());
                  }
                  // Even you can handle your response error if it's in your response.
              }
      
              @Override
              public void onFailure(Call<MyBeanClass> call, Throwable t) {
                  //Set your error live data from here
                  error.setValue(t);
              }
          });
      }
      

      从您的 ViewModel 类中,创建一个调用您的 repo API 方法的方法和另一个提供您的实时数据以在您的 UI 上观察的方法。

      希望对你有帮助!

      【讨论】:

      • 是的,我直到存储库才理解它..但是我在视图模型中遇到问题...你能告诉我如何在视图模型中编码以获得两种不同类型的数据,即错误/响应
      • 实际上答案已经很老了,最近我开始了解如何从这里包装这两个东西:developer.android.com/jetpack/docs/guide#addendum。让我知道它是否满足您的情况,以便我相应地编辑我的答案。
      • 我无法从此链接获得解决方案..我想通过视图模型获取我在存储库类中获取的特定异常的名称。我已经采取我的 repo 类通过视图模型对我的视图的响应..但是在任何异常的情况下如何完成..如何将该异常带到我的视图........
      【解决方案5】:
      public MutableLiveData<String> responseMessage = new MutableLiveData<>();
      public MutableLiveData<String> errorMessage = new MutableLiveData<>();                                           ApiInterface apiInterface = ApiClient.getClientAuthentication().create(ApiInterface.class);
      Call<MyBeanClass> call = apiInterface.getData(id);
      call.enqueue(new Callback<MyBeanClass>() {
          @Override
          public void onResponse(Call<MyBeanClass> call, Response<MyBeanClass> response) {
              if(response.body()==null) {
                //pick server error message
                JSONObject jObjError = new JSONObject(response.errorBody().string());
                Log.d("Error",jObjError.getString("message"))
              }else{
              myBeanClass.setValue(Resource<MyBeanClass>.success(response.body));
              }
          }
      
          @Override
          public void onFailure(Call<MyBeanClass> call, Throwable t) {
           Log.d("Error", t.getMessage());
           errorMessage.setValue(t.getMessage());
          }
      });
      

      只需使用两个 MutableLiveData 一个用于成功响应,另一个用于 errorResponse 并根据这些响应更新您的 UI。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-14
        • 1970-01-01
        • 2011-09-14
        • 2017-02-24
        • 2010-12-05
        • 2020-09-03
        • 2023-03-10
        相关资源
        最近更新 更多