Skip to main content

WP Android App using REST and Volley with LiveData and ViewModel Part3

In last two part of our WP Android App using REST volley tutorial we have learned about how to fetch the post using REST api from wordpress website. In this part of the article we are going to implement the ViewModel and LiveData component to avoid the refresh every time when any configuration change happening in your app. When configuration change happen in any android application. A common event which trigger the configuration change is the screen rotation. So whenever any configuration change event happen then application will fetch the details from server. Which is creating unnecessary call to backend server and network.

Solution of the Configuration Change Data reset issue



As explained  above whenever any configuration change happen data is getting reset and application try to fetch the details from the server. As solution of this problem we are going to implement the ViewModel and LiveData architecture component to our application.

First of all we are explaining here what we all need to change in our earlier application. You can check the last two part of WP tutorial below.



List of change to implement the LiveData and View Model for WP App




  • We need to create a ViewModel

  • Create LiveData object with type of Post data type

  • Migrate the Vollery function from Main Activity to LiveData

  • Set the Observer on the LiveData to update the UI

  • Some other minor change we will do for refresh layout for manually triggering the refresh of the data



Create View Model for WP App



If have no idea about the ViewModel then let me explain in short. It is a library developed recently published by Google and benefit of this app is that it is life cycler aware class and it prevent the data loss of the UI even your activity are re-created due to any reason.

public class PostModel extends AndroidViewModel {

@Nullable
private JsonLiveData postsList;
private int index;

public MutableLiveData getRefresh() {
return refresh;
}

public void setRefresh(MutableLiveData refresh) {
this.refresh = refresh;
}

private MutableLiveData<Integer> refresh=new MutableLiveData<>();

public PostModel(@NonNull Application application) {
super(application);
if(postsList==null)
postsList=new JsonLiveData(application);

}

public MutableLiveData<List<Posts>> getPostsList() {
return postsList;
}

public int getChangeIndex(){
return index;
}
public void RefreshData(){
refresh.setValue(0);
postsList=new JsonLiveData(this.getApplication());
}
}


You can see that I have use the AndroidViewModel instead of ViewModel. It is because ViewModel don't allow us to use the context inside the class but Android View Model provide the application context that we can use in our Livedata to fetch the details from server over network.

Inside this view model we have an construction using which we can get the application context.

public PostModel(@NonNull Application application) {
super(application);
//Initialize the LiveData
if(postsList==null)
postsList=new JsonLiveData(application);
}


The RefershData method we are using here to manually triggering the LiveData to fetch the details from server when user use the Swipe to Refresh function and getRefresh method we are using to notify the main thread about that refresh has been done and will stop the setRefressing(false) from main activity.

However, Update we are updating the view only if change in the actual Data.

private MutableLiveData<Integer> refresh=new MutableLiveData<>();
public MutableLiveData getRefresh() {
return refresh;
}


Create LiveData for WP App



Next part is the creating the our LiveData class. In side LiveData class we will move the code from our main activity to fetch the post details from server.

We can create the LiveData class by extending the LiveData class. So here we are creating the JsonLiveData class using the below code.

public class JsonLiveData extends MutableLiveData<List<Posts>>{
public JsonLiveData(Context context){
this.context=context;
}
}


Migrate the Volley function from Main Activity to LiveData



For migrating the code of Volley function from Main Activity to LiveData class. Comment the code in main fragment for getPost or create the new method with same code inside LiveData as given below.

private void LoadData() {

final RequestQueue requestQueue = Volley.newRequestQueue(context);
JsonArrayRequest getRequest = new JsonArrayRequest(Request.Method.GET, Config.base_url+"wp/v2/posts/?page="+page, null,
new Response.Listener<JSONArray>()
{
@Override
public void onResponse(JSONArray response) {
// display response
Log.d(TAG, response.toString() + "Size: "+response.length());
for(int i=0;i<response.length();i++){
final Posts post=new Posts();
try {
Log.d(TAG,"Object at " + i+ response.get(i));
JSONObject obj=response.getJSONObject(i);
post.setId(obj.getInt("id"));
post.setCreatedAt(obj.getString("date"));
post.setPostURL(obj.getString("link"));
JSONObject titleObj=obj.getJSONObject("title");
post.setTitle(titleObj.getString("rendered"));
//Get excerpt
JSONObject exerptObj=obj.getJSONObject("excerpt");
post.setExcerpt(exerptObj.getString("rendered"));
// Get content
JSONObject contentObj=obj.getJSONObject("content");
post.setContent(exerptObj.getString("rendered"));


// getting URL of the Post fetured Image
JSONObject featureImage=obj.getJSONObject("_links");
JSONArray featureImageUrl=featureImage.getJSONArray("wp:featuredmedia");
JSONObject featureImageObj=featureImageUrl.getJSONObject(0);
String fiurl=featureImageObj.getString("href");
if(fiurl!=null) {
// post.setPostImg(fiurl);
Log.d(TAG, featureImageObj.getString("href"));

JsonObjectRequest getMedia = new JsonObjectRequest(Request.Method.GET,
fiurl, null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
try {
// JSONObject obj=response.getJSONObject(0);
Log.d(TAG, response.getString("source_url"));
post.setPostImg(response.getString("source_url"));
index=mPosts.indexOf(post);
postValue(mPosts);

} catch (JSONException e) {
e.printStackTrace();
}
// post.setPostImg();


}


}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, error.toString());
}
}

);
requestQueue.add(getMedia);

}

mPosts.add(post);



} catch (JSONException e) {
e.printStackTrace();
}

}
setValue(mPosts);
refresh.postValue(1);

}

},
new Response.ErrorListener()
{
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, error.toString());
}
}
) ;

requestQueue.add(getRequest);

}


Set the Observer on the LiveData to update the UI



Now we have do modification in our main thread to observe the changes on LiveData. So that in case of change in the data our will get updated automatically.

Get the instance of the ViewModel under which we have implemented the LiveData.

postModel= ViewModelProviders.of(getActivity()).get(PostModel.class);


Set the observer on the post post LiveData, so that in case of change in the data our UI get updated automatically.

postModel.getPostsList().observe(this, new Observer<List<Posts>>() {
@Override
public void onChanged(@Nullable List<Posts> posts) {
postAdapter.setData(posts);
postAdapter.notifyItemChanged(postModel.getChangeIndex());
Log.d(TAG,"On Changed method called");
swipeRefreshLayout.setRefreshing(false);
}
});


Using below code we can set the observer to stop the refreshing view in case user swipe on the screen.

postModel.getRefresh().observe(this, new Observer<Integer>() {

@Override
public void onChanged(@Nullable Integer integer) {
swipeRefreshLayout.setRefreshing(false);
}
});


 

Full Source code of ViewModel



Below is the full code of the out ViewModel class.

package com.nplix.wpapp;

import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModel;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

import static com.nplix.wpapp.ListFragment.TAG;

/**
* Created by PK on 1/10/2018.
*/

public class PostModel extends AndroidViewModel {

@Nullable
private JsonLiveData postsList;
private int index;

public MutableLiveData getRefresh() {
return refresh;
}


private MutableLiveData<Integer> refresh=new MutableLiveData<>();

public PostModel(@NonNull Application application) {
super(application);
if(postsList==null)
postsList=new JsonLiveData(application);

}

public MutableLiveData<List<Posts>> getPostsList() {
return postsList;
}

public int getChangeIndex(){
return index;
}
public void RefreshData(){
refresh.setValue(0);
postsList=new JsonLiveData(this.getApplication());
}
/*
private void setPostsList(@Nullable List<Posts> postsList) {
Log.d("ViewModel", "Setting data to ViewModel");
this.postsList = postsList;


}*/


public class JsonLiveData extends MutableLiveData<List<Posts>>{
private List<Posts> mPosts=new ArrayList<Posts>();
private final Context context;
private int page=1;

public JsonLiveData(Context context){
this.context=context;
LoadData();
}

private void LoadData() {

final RequestQueue requestQueue = Volley.newRequestQueue(context);
JsonArrayRequest getRequest = new JsonArrayRequest(Request.Method.GET, Config.base_url+"wp/v2/posts/?page="+page, null,
new Response.Listener<JSONArray>()
{
@Override
public void onResponse(JSONArray response) {
// display response
Log.d(TAG, response.toString() + "Size: "+response.length());
for(int i=0;i<response.length();i++){
final Posts post=new Posts();
try {
Log.d(TAG,"Object at " + i+ response.get(i));
JSONObject obj=response.getJSONObject(i);
post.setId(obj.getInt("id"));
post.setCreatedAt(obj.getString("date"));
post.setPostURL(obj.getString("link"));
JSONObject titleObj=obj.getJSONObject("title");
post.setTitle(titleObj.getString("rendered"));
//Get excerpt
JSONObject exerptObj=obj.getJSONObject("excerpt");
post.setExcerpt(exerptObj.getString("rendered"));
// Get content
JSONObject contentObj=obj.getJSONObject("content");
post.setContent(exerptObj.getString("rendered"));
// getting URL of the Post fetured Image
JSONObject featureImage=obj.getJSONObject("_links");
JSONArray featureImageUrl=featureImage.getJSONArray("wp:featuredmedia");
JSONObject featureImageObj=featureImageUrl.getJSONObject(0);
String fiurl=featureImageObj.getString("href");
if(fiurl!=null) {
// post.setPostImg(fiurl);
Log.d(TAG, featureImageObj.getString("href"));
JsonObjectRequest getMedia = new JsonObjectRequest(Request.Method.GET,
fiurl, null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
try {
// JSONObject obj=response.getJSONObject(0);
Log.d(TAG, response.getString("source_url"));
post.setPostImg(response.getString("source_url"));
index=mPosts.indexOf(post);
postValue(mPosts);
} catch (JSONException e) {
e.printStackTrace();
}
// post.setPostImg();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, error.toString());
}
}
);
requestQueue.add(getMedia);
}
mPosts.add(post);
} catch (JSONException e) {
e.printStackTrace();
}

}
setValue(mPosts);
refresh.postValue(1);
}
},
new Response.ErrorListener()
{
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, error.toString());
}
}
) ;
requestQueue.add(getRequest);
}
}
}


Collection of change inside ListFragment.java file as given below.

package com.nplix.wpapp;


import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


/**
* A simple {@link Fragment} subclass.
*/
public class ListFragment extends BackHandledFragment {
public static String TAG="postFrag";
public List<Posts> mPosts;
public Button btnGetPost;
public RecyclerView recyclerView;
public PostAdapter postAdapter;
public int page=1;
public SwipeRefreshLayout swipeRefreshLayout;
public File file;

public ListFragment() {
// Required empty public constructor
}

private PostModel postModel;
private Observer<List<Posts>> postsObserver;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment

return inflater.inflate(R.layout.fragment_list, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
recyclerView= (RecyclerView) getActivity().findViewById(R.id.recyclerHome);
postAdapter=new PostAdapter(mPosts,getContext(),false,false);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
swipeRefreshLayout= (SwipeRefreshLayout) getActivity().findViewById(R.id.swipeRefreshLayout);
//swipeRefreshLayout.setRefreshing(true);
swipeRefreshLayout.setColorSchemeResources(R.color.accent_green,R.color.md_red_800,R.color.md_blue_500,R.color.purple);
mPosts = new ArrayList<Posts>();
recyclerView.setAdapter(postAdapter);
postModel= ViewModelProviders.of(getActivity()).get(PostModel.class);
swipeRefreshLayout.setEnabled(true);
postModel.getPostsList().observe(this, new Observer<List<Posts>>() {
@Override
public void onChanged(@Nullable List<Posts> posts) {
postAdapter.setData(posts);
postAdapter.notifyItemChanged(postModel.getChangeIndex());
Log.d(TAG,"On Changed method called");
swipeRefreshLayout.setRefreshing(false);
}
});
postModel.getRefresh().observe(this, new Observer<Integer>() {

@Override
public void onChanged(@Nullable Integer integer) {
swipeRefreshLayout.setRefreshing(false);
}
});

swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
swipeRefreshLayout.setRefreshing(true);
postModel.RefreshData();


}
});


}

public void setData(List<Posts> posts){
recyclerView.setAdapter(postAdapter);
postAdapter.setData(mPosts);
swipeRefreshLayout.setRefreshing(false);
postAdapter.notifyDataSetChanged();
}
@Override
public String getTagText() {
return TAG;
}

@Override
public boolean onBackPressed() {
return true;
}

@Override
public void RefreshLayout() {

}
}


That's all to we need to implement the LiveData and ViewModel to our WP Android App. Now you can test the app by rotating the screen and you will see that there is no network call is happening. Data will be only updated if user manually refresh the view or load the data.

WP Android App

Comments

  1. […] LiveData and ViewModel for WP Android App using REST and volley part3 […]

    ReplyDelete
  2. Source code didnot work for me.

    ReplyDelete
  3. Hi Rakha,

    you can find the source code at below link.
    https://github.com/debugandroid/WPApp

    Let me know in case of any further issue.

    Thanks,

    ReplyDelete
  4. […] LiveData and ViewModel for WP Android App using REST and volley part3 […]

    ReplyDelete
  5. Thanks a lot bro Pawan Kumar.
    Have a great day!

    ReplyDelete
  6. Hi. Are we doing the volley options on the main thread or the background thread? There is no instance of which I can make sure that the network request will be done in the backgroound thread.

    ReplyDelete
  7. Hi Partik,

    All network request performed by Volley is done in a background thread. So there is no need to perform a request on a different thread, since that's already happening in the background when you are using the volley.

    But yes if your data processing is taking much time inside the listener then you can move that part in a background thread/ async task.

    Thanks,

    ReplyDelete

Post a Comment

Popular posts from this blog

Flutter Theme Creation, Programmatic Way

Flutter and Dart is an excellent combination for creating the mobile app with material design with nice GUI. Its lots of widget which help the developer for creating the app UI very fast with respect to the other language. You may face some problem in starting due to comm, curly brace and semicolon but after some time you will find the way how to work with these all stuff and it becomes very easy for you. But in this article, we are not going to discuss all these. In this article, we will handle a very interesting topic on how to create a custom theme or multiple themes and let's user decide which theme to use.



Create a flutter project



So let's create a flutter project by selecting the appropriate option from the File menu in android studio or Visual Code. If you are new to Flutter and Dart you can check out our recent post to get started Creating Cross-platform app using flutter.



Once your project is created you will get the default counter app and you try running the app in the…

Flutter How to Start Android Activity from Flutter View

Flutter and Dart is an excellent combination for creating the UI, but for accessing the platform-specific service we need to open platform-specific activity. So lets in this article we will explore how to start an android activity and access the service from Flutter View.

Create a Project for this Android Activity Flutter View Demo Create a Project From File menu select the New Flutter Project Enter the project name Select the AndroidX support and click on next After the above, we step click on Finish We will have the following project structure created. Create the Second Activity in Android
Just go to the android folder and open it in separate windows. We will have the following project structure.









Create the Activity Just right click on the Kotlin folder and create a blank activity from the menu. If you create the activity then you may be required to upgrade the Gradle and do some import. So Just click on update and wait for the project sync completed.
Update the project build config …

Flutter State Management With Provider and ChangeNotifier

Flutter state management with provider library and why it's so important? There are lots of topics in flutter which are very important but out of those state management of your flutter application is the most important and little bit complicated as well. So in this article, I will try to explain how to use the provider package for managing the state of flutter and dart application and how to access the data down the hierarchy of the widget tree without recreating the whole UI.



Basic knowledge of the state management and data flow in the Flutter



Before starting the coding I would like to explain some basics about the Flutter application so that you can understand, why it's important to manage the state of your application in the correct way.



In Flutter, everything is a widget and whenever the state of your application changes the UI is recreated the UI with the new data. Let's list out the topic or problem that we are going to explore about state management.



Challenge of St…