4

I am new to android architecture components and I am little confused with viewmodel. I am building an app which get a list of items from the server and display as a list in the layout. I have implemented the network call in the Repository class.

Repository.java:

//Get list of top rated movies
    public LiveData<NetworkResponse> getTopRatedMovies() {
        final MutableLiveData<NetworkResponse> result = new MutableLiveData<>();
        ApiService api = retrofit.create(ApiService.class);
        Call<MovieData> call = api.getTopRateMovies("api_key");
        call.enqueue(new Callback<MovieData>() {
            @Override
            public void onResponse(Call<MovieData> call, Response<MovieData> response) {
                result.postValue(new NetworkResponse(response.body()));
            }

            @Override
            public void onFailure(Call<MovieData> call, Throwable t) {
                Log.e(TAG, t.getLocalizedMessage());
                result.postValue(new NetworkResponse(t));
            }
        });
        return result;
    }

Now in the ViewModel class I am doing this:

public class MovieListViewModel extends ViewModel {


    public LiveData<NetworkResponse> result, topRatedMovies;
    public LiveData<List<MovieEntity>> favoriteMovies;

    private Repository repository;

    public MovieListViewModel() {
        repository = new Repository(MyApplication.getInstance());
    }

    public void getTopRatedMovieList() {
        topRatedMovies = repository.getTopRatedMovies();
    }

}

Now in the MainActivity.java:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        ((MyApplication) getApplication()).getComponent().inject(this);
        movieListViewModel = ViewModelProviders.of(this).get(MovieListViewModel.class);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
        adapter = new MovieListAdapter(this);
        movieListViewModel.getTopRatedMovieList();
        observeTopRatedMovies();

    }
private void observeTopRatedMovies() {
        movieListViewModel.topRatedMovies.observe(this, new Observer<NetworkResponse>() {
            @Override
            public void onChanged(@Nullable NetworkResponse networkResponse) {
                if (networkResponse.getPostData() != null) {
                    Log.e(TAG, "Successful");
                    topRatedData = networkResponse.getPostData();
                    adapter.addData(networkResponse.getPostData().getResults());
                    recyclerView.setAdapter(adapter);
                } else {
                    Log.e(TAG, "failure");
                }
            }
        });
    }

Now everything works fine and I am able to see the list. But if I rotate the phone the viewmodel makes the network call again. How can I avoid the network call again on screen orientation change?

Mycoola
  • 1,135
  • 1
  • 8
  • 29
sagar suri
  • 4,351
  • 12
  • 59
  • 122
  • Possible duplicate of [Android Arch Components ViewModel and LiveData trigger after screen rotation](https://stackoverflow.com/questions/51450215/android-arch-components-viewmodel-and-livedata-trigger-after-screen-rotation) – ivan8m8 Jan 24 '19 at 08:33

3 Answers3

7

You can initialize live data only once. That should be enough:

public class MovieListViewModel extends ViewModel {


    public LiveData<NetworkResponse> result, topRatedMovies;
    public LiveData<List<MovieEntity>> favoriteMovies;

    private Repository repository;

    public MovieListViewModel() {
        repository = new Repository(MyApplication.getInstance());
        topRatedMovies = repository.getTopRatedMovies();
    }

}
egoldx
  • 1,490
  • 1
  • 9
  • 14
2

I suggest you to use headless-fragment design pattern. A headless fragment is a fragment that retain his configuration and it doesn't inflate any xml. If you rotate your app the fragment continue with his logic and configuration and is very useful when you have to do async task or async call (like you in retrofit)

define your fragment:

public class YourFragment extends Fragment {

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true); // <--------- the fragment retain his configuration
    }

    public void yourLogic(){
        // do your logic
    }

}

in your MainActivity class create the fragment or get the istance fragment if it already exists:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {

     super.onCreate(savedInstanceState);

     _yourHeadLessFragment= (YourFragment) getSupportFragmentManager().findFragmentByTag(HEADLESS_FRAGMENT);

     if (_yourHeadLessFragment== null) {
         _yourHeadLessFragment= new YourFragment();
         _yourHeadLessFragment.setListener(this); // if you want a callback
                getSupportFragmentManager().beginTransaction().add(_yourHeadLessFragment, HEADLESS_FRAGMENT).commit();

     }
     else{
         _yourHeadLessFragment.setListener(this); // refresh the callbacks if a rotation happened
     }
   }
}
Alessandro Verona
  • 1,157
  • 9
  • 23
  • Even this is a nice approach. But I am looking into this viewmodel and livedata stuff currently. Thanks for this approach as well. :) – sagar suri Aug 21 '18 at 07:12
0

you can put network call in the init block of ViewModel

  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 08 '22 at 02:54