Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

exception handling through mvvm android

I am using MVVM architecture to hit a web service through retrofit in android studio. I have handled the response of the service in my view class. But the problem i am facing is how to handle the exceptions and pass them to my view class. One way is to make constructor in my Bean class and pass both the response and error to it and update UI. But i want more optimised way to handle exceptions inside UI.

Here is my repository code :

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;
like image 378
Ashmeet Arora Avatar asked Dec 03 '22 19:12

Ashmeet Arora


2 Answers

Here is the full implementation of the mvvm with error handling. Firstly create a class with UI State and Resource.

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 }
}

On your Repository class do

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;
    }

On your viewModel class

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;
    }
}

And on your Activity class

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;
            }

        });

If it helps, don't forget to vote. Happy coding

like image 88
Tony Lip Avatar answered Dec 06 '22 10:12

Tony Lip


Instead of creating two Mutable classes. You can just create a wrapper object for both error and success state or even loading state

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)
        }
    }
}

And then use MutableLive data as this type

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;

You can check out this google sample https://github.com/googlesamples/android-architecture-components/tree/master/GithubBrowserSample

like image 34
Kaung Khant Thu Avatar answered Dec 06 '22 10:12

Kaung Khant Thu