How to fetch data from an api using ViewModel and LiveData in Java
August 10, 2020
Tags: Android, Android Basics, Github Repository App, MVVM, Model-View-ViewModel, ViewModel,
Introduction
We are using 3 components here: ViewModel, LiveData, Repository.
- Repository is a class that handles fetching the data.
- ViewModel is where the data is stored.
- LiveData is a data holder class which is observable, used in both ViewModel and Repository.
- We first create repository which is a normal java class, we fetch data here asynchronously and save it in a MutableLiveData object. We are using MutableLiveData here because we change the data here, a LiveData object can only be observed.
- We then create our ViewModel with LiveData objects, instantiate our repository inside our ViewModel. Using the repository we instantiate our LiveData objects, in our repository class we create functions which returns the MutableLiveData objects we created casted as LiveData objects.
- We instantiate our ViewModel object in our Activity/Fragment and use it to get our data and observe for changes in it.
Fetching data from Github API
Creating Repository
First I create the Repository Class which has MutableLiveData<GithubRepository[]>
and a function getRepos()
to have access to this data in the ViewModel. I have also created a searchGithub()
function that I will be using from the MainActivity whenever the search button is clicked.
public class Repository {
private MutableLiveData<GithubRepository[]> mRepos = new MutableLiveData<>();
private Thread mThread;
/**
* @return mRepo A MutableLiveData object casted as
* a LiveData object.
*/
public LiveData<GithubRepository[]> getRepos(){
return mRepos;
}
/**
* Creates a new background thread and queries the Github API.
* @param url
*/
public void searchGithub(final URL url){
Runnable fetchJsonRunnable = new Runnable() {
@Override
public void run() {
queryGithubSearchApi(url);
}
};
// Stop the thread if its initialized
// If the thread is not working interrupt will do nothing
// If its working it stops the previous work and starts the
// new runnable
if (mThread != null){
mThread.interrupt();
}
mThread = new Thread(fetchJsonRunnable);
mThread.start();
}
/**
* A method which queries the Github API to fetch the results
* for the term we searched. Does not run asynchronously and
* needs to be called in a thread off the main UI thread.
* Updates mRepo.
*
* @param url
*/
private void queryGithubSearchApi(URL url) {
try {
String response = NetworkUtils.getResponseFromHttpUrl(url);
JSONObject json = new JSONObject(response);
JSONArray repos = json.getJSONArray("items");
GithubRepository[] githubRepositories = new GithubRepository[repos.length()];
for(int i = 0; i < repos.length(); i++){
JSONObject repo = repos.getJSONObject(i);
githubRepositories[i] = JSONUtils.parseRepositoryJSON(repo);
}
// Set value of out LiveData(Mutable)
mRepos.postValue(githubRepositories);
}catch (IOException | JSONException e){
e.printStackTrace();
}
}
}
Creating ViewModel
This is very similar to our repository class, I have initialized a repository class which I use further to initialize mRepos
which is a LiveData object here because we don’t change the data in ViewModel and just pass it on to our Activity. The main advantage of using a ViewModel is that it exists outside of our Activity, so configuration changes like orientation changes does not affect it.
public class MainViewModel extends AndroidViewModel {
private Repository repository = new Repository();
private LiveData<GithubRepository[]> mRepos = repository.getRepos();
public MainViewModel(@NonNull Application application) {
super(application);
}
public LiveData<GithubRepository[]> getRepos() {
return mRepos;
}
public void searchGithub(URL url) {
repository.searchGithub(url);
}
}
Using ViewModel in the Activity
We first create an instance of our ViewModel using new ViewModelProvider(this).get(MainViewModel.class)
. Next we get the LiveData object, call observe()
on it and fill it out with whatever needs to be done when the data changes, in our case here we need to change the data in out RecyclerView adapter and hide status bar.
viewModel = new ViewModelProvider(this).get(MainViewModel.class);
viewModel.getRepos().observe(this, new Observer<GithubRepository[]>() {
@Override
public void onChanged(GithubRepository[] githubRepositories) {
mRepos = githubRepositories;
mGithubRepositoryAdapter.setmAllRepositories(githubRepositories);
mRepositoryRecyclerView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
}
});
Conclusion
Although I use a custom class here called GithubRepository
these few steps can be used to fetch data of any type and store it. This is a result of exploration I did to refactor my code to get rid of AsyncTask. Please let me know if you find any errors. You can get the source code here.