18

I'm using the MVVM architecture. I have an activity and a few fragments, I would like to make a request in the API in the activity, and then using ViewModel, thanks to the obtained data, to display them in the fragment. How should I do this? My current solution that doesn't work:

Activity:

viewModelRoutesFragment = new ViewModelProvider(this).get(ViewModelRoutesFragment.class);
viewModelRoutesFragment.init();

Fragment:

viewModelRoutesFragment = new ViewModelProvider(this).get(ViewModelRoutesFragment.class);
viewModelRoutesFragment.getRoutes().observe(getActivity(), new Observer<List<RoutesResponse>>() {
    @Override
    public void onChanged(List<RoutesResponse> routes) {

                //Show data
    }
});

Repository:

public class RemoteRepository {

private ApiRequest apiRequest;
private MutableLiveData<List<RoutesResponse>> routes = new MutableLiveData<>();

public RemoteRepository() {
    apiRequest = RetrofitRequest.getInstance().create(ApiRequest.class);
}

public MutableLiveData<List<RoutesResponse>> getRoutes() {

    apiRequest.getRoutes()
            .enqueue(new Callback<List<RoutesResponse>>() {
                @Override
                public void onResponse(Call<List<RoutesResponse>> call, Response<List<RoutesResponse>> response) {

                    if (response.isSuccessful())
                        routes.setValue(response.body());
                }

                @Override
                public void onFailure(Call<List<RoutesResponse>> call, Throwable t) {
                    Log.i("Failure", "Fail!");
                }
            });

        return routes;
    }
}

ViewModel:

public class ViewModelRoutesFragment extends AndroidViewModel {

private RemoteRepository remoteRepository;
private LiveData<List<RoutesResponse>> routes;

public ViewModelRoutesFragment(@NonNull Application application) {
    super(application);
}

public void init() {
    remoteRepository = new RemoteRepository();
    routes = remoteRepository.getRoutes();
}

public LiveData<List<RoutesResponse>> getRoutes() {
        return routes;
    }
}

Currently getting a null error. How can I avoid it properly?

java.lang.NullPointerException: Attempt to invoke virtual method 'void androidx.lifecycle.LiveData.observe(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Observer)' on a null object reference
mikoxy
  • 231
  • 1
  • 2
  • 5
  • Here is the answer https://stackoverflow.com/questions/60885459/how-to-share-data-between-activity-and-fragment-via-viewmodel-class-in-android – Waris Ali Rehmani Sep 05 '21 at 17:39

3 Answers3

10

in Fragment Use

viewModelRoutesFragment = new ViewModelProvider(requireActivity()).get(ViewModelRoutesFragment.class);

instead of

viewModelRoutesFragment = new ViewModelProvider(this).get(ViewModelRoutesFragment.class);
Yernat Mussin
  • 101
  • 1
  • 4
6

Basically, we are trying to share the viewmodel across the activity and fragment. so while during the activity creation we have to create the instance of viewmodel

viewModelRoutesFragment = new ViewModelProvider(requireActivity()).get(ViewModelRoutesFragment.class);
viewModelRoutesFragment.init();

In fragment also we need to reuse the ViewModelRoutesFragment so in onViewCreated() get the instance of the ViewModel and observe the live data

viewModelRoutesFragment = new ViewModelProvider(requireActivity()).get(ViewModelRoutesFragment.class);
viewModelRoutesFragment.getRoutes().observe(getActivity(), new Observer<List<RoutesResponse>>() {
    @Override
    public void onChanged(List<RoutesResponse> routes) {
       // updation of UI
    }
});
1

You don't need your view model reference in the activity. You should have an instance of fragments inside the activity. Your fragment already holding a reference to the ViewModel. Delete these line from the activity -> :

viewModelRoutesFragment = new ViewModelProvider(this).get(ViewModelRoutesFragment.class);
viewModelRoutesFragment.init();

Make sure you initialize your fragment in activity. Your activity is just a holder block, which actually replace the fragment using fragment manager. It doenst required any viewmodel if you are using a fragment with it.

Also, call this method inside your fragment viewModelRoutesFragment.init(); below this line

 viewModelRoutesFragment = new ViewModelProvider(this).get(ViewModelRoutesFragment.class);
K P
  • 182
  • 9