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

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

Get Email Updates!

Signup now receive an email once I publish new content. I will never give away, trade or sell your email address. You can unsubscribe at any time.

Join 911 other subscribers

This Post Has 6 Comments

  1. Thanks a lot bro Pawan Kumar.
    Have a great day!

  2. Source code didnot work for me.

Leave a Reply

Close Menu
%d bloggers like this: