ChandraSaiMohan bhupathi
5 min readJul 10, 2018

Android Architecture Patterns Model View View Model — Part 2

MVVM with Retrofit

Step by Step implementation of Retrofit in MVVM

Step1:

Create an Activity as a container

public class RetroMVVM extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_retro_mvvm);
getSupportFragmentManager().beginTransaction().replace(R.id.container_pagination,new RetroFitPostFragment()).commit();
}
}

Layout for this Activity

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".PaginationMVVM.RetroMVVM"
>

<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="55"
android:id="@+id/container_pagination"
></FrameLayout>

</LinearLayout>

Step2: Create Retrofit Model

(1) Create Retrofit interface

public interface APIService {
@GET("posts")
Call<String> makeRequest();
}

(2)Create a class with the constant webservice URL

public class APIUrl {
public static final String BASE_URL = "https://jsonplaceholder.typicode.com/";
}

(3)Create a model class for Retrofit

public class ResultModel
{
@SerializedName("id")
private int id;
@SerializedName("body")
private String body;
@SerializedName("title")
private String title;
@SerializedName("userId")
private String userId;

public int getId ()
{
return id;
}

public void setId (int id)
{
this.id = id;
}

public String getBody ()
{
return body;
}

public void setBody (String body)
{
this.body = body;
}

public String getTitle ()
{
return title;
}

public void setTitle (String title)
{
this.title = title;
}

public String getUserId ()
{
return userId;
}

public void setUserId (String userId)
{
this.userId = userId;
}

@Override
public String toString()
{
return "ClassPojo [id = "+id+", body = "+body+", title = "+title+", userId = "+userId+"]";
}
}

(4)WebServiceRepository class that does webservice information

public class WebServiceRepository {

Application application;
private static OkHttpClient providesOkHttpClientBuilder(){

OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
return httpClient.readTimeout(1200, TimeUnit.SECONDS)
.connectTimeout(1200, TimeUnit.SECONDS).build();

}


List<ResultModel> webserviceResponseList = new ArrayList<>();
public LiveData<List<ResultModel>> providesWebService() {
final MutableLiveData<List<ResultModel>> data = new MutableLiveData<>();

String response = "";
try {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(APIUrl.BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(providesOkHttpClientBuilder())
.build();

//Defining retrofit api service
APIService service = retrofit.create(APIService.class);
// response = service.makeRequest().execute().body();
service.makeRequest().enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
Log.d("Repository","Response::::"+response.body());
webserviceResponseList = parseJson(response.body());
data.setValue(webserviceResponseList);
}

@Override
public void onFailure(Call<String> call, Throwable t) {
Log.d("Repository","Failed:::");
}
});
}catch (Exception e){
e.printStackTrace();
}

// return retrofit.create(ResultModel.class);
return data;

}


private List<ResultModel> parseJson(String response) {

List<ResultModel> apiResults = new ArrayList<>();

JSONObject jsonObject;

JSONArray jsonArray;

try {
jsonArray = new JSONArray(response);

for (int i = 0; i < jsonArray.length(); i++) {
JSONObject object = jsonArray.getJSONObject(i);

ResultModel mMovieModel = new ResultModel();
//mMovieModel.setId(object.getString("id"));
mMovieModel.setId(Integer.parseInt(object.getString("id")));
mMovieModel.setTitle(object.getString("title"));
mMovieModel.setBody(object.getString("body"));

apiResults.add(mMovieModel);
}


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

Log.i(getClass().getSimpleName(), String.valueOf(apiResults.size()));
return apiResults;

}
}

This class returns mutableLiveData of webservice results.

MutableLiveData:

MutableLiveData is a subclass of LiveData which is used for some of it’s properties (setValue/postValue) and using these properties we can easily notify the ui when onChange() is called. Only using LiveData object we can’t do this. So, we have to convert the target object to MutableLiveData for notifying on each time of changing data.

Step 3: RetroViewModel creates ViewModel for our app

public class RetroViewModel extends AndroidViewModel {
private final LiveData<List<ResultModel>> retroObservable;
WebServiceRepository webServiceRepository ;
public RetroViewModel(Application application){
super(application);
webServiceRepository = new WebServiceRepository();
retroObservable = webServiceRepository.providesWebService();
}


public LiveData<List<ResultModel>> getProjectRetroListObservable() {
return retroObservable;
}
}

The above code fetches results from WebService Repository and returns to View.

Step4 : View

public class RetroFitPostFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";

// TODO: Rename and change types of parameters
private
String mParam1;
private String mParam2;
View view = null;
RetroViewModel retroViewModel;
public RetroFitPostFragment() {
// Required empty public constructor
}

/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
*
@param param1 Parameter 1.
*
@param param2 Parameter 2.
*
@return A new instance of fragment RetroFitPostFragment.
*/
//
TODO: Rename and change types and number of parameters
public static
RetroFitPostFragment newInstance(String param1, String param2) {
RetroFitPostFragment fragment = new RetroFitPostFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
retroViewModel = ViewModelProviders.of(getActivity()).get(RetroViewModel.class);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
view = inflater.inflate(R.layout.fragment_retro_fit_post, container, false);
initViews(view);
setAdapter();
retroViewModel.getProjectRetroListObservable().observe(this, new Observer<List<ResultModel>>() {
@Override
public void onChanged(@Nullable final List<ResultModel> users) {
// Update the cached copy of the words in the adapter.
adapter.setWords(users);
}
});

return view;
}

RecyclerView recyclerView;
private void initViews(View view){
recyclerView = (RecyclerView)view.findViewById(R.id.post_list);
}


RetroPostListAdapter adapter = null;
private void setAdapter(){
adapter = new RetroPostListAdapter(getActivity());
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
}

}

The above code creates viewmodel instance in onCreate() and fetches LiveData in onCreateView() method which is highlighted in the above code.

Kotlin Version for Retro MVVM

Step 1: Create Activity

class RetroKotlinActivity: AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_retro_mvvm)
supportFragmentManager.beginTransaction().replace(R.id.container_pagination, RetrofitKotlinFragment()).commit()
}
}

Layout for Activity

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="55"
android:id="@+id/container_pagination"
></FrameLayout>

</LinearLayout>

Step2 : Model

(1) Interface for Retrofit

public interface APIService {
@GET("posts")
abstract fun makeRequest(): Call<String>
}

(2) Class that make webservice call and returns results

public class APIServiceFactory {
private fun providesOkHttpClientBuilder(): OkHttpClient {

val httpClient = OkHttpClient.Builder()
return httpClient.readTimeout(1200, TimeUnit.SECONDS)
.connectTimeout(1200, TimeUnit.SECONDS).build()

}

fun providesWebService(): LiveData<List<ResultModel>> {
val data = MutableLiveData<List<ResultModel>>()
var webserviceResponseList: List<ResultModel>

val response = ""
try
{
val retrofit = Retrofit.Builder()
.baseUrl(APIURL.BASE_URL)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(providesOkHttpClientBuilder())
.build()

//Defining retrofit api service
val service = retrofit.create(APIService::class.java)
// response = service.makeRequest().execute().body();
service.makeRequest().enqueue(object : Callback<String> {
override fun onResponse(call: Call<String>, response: Response<String>) {
Log.d("Repository", "Response::::" + response.body()!!)
webserviceResponseList = parseJson(response.body())
data.setValue(webserviceResponseList)
}

override fun onFailure(call: Call<String>, t: Throwable) {
Log.d("Repository", "Failed:::")
}
})
} catch (e: Exception) {
e.printStackTrace()
}

return data

}

private fun parseJson(response: String?): List<ResultModel> {

val apiResults = ArrayList<ResultModel>()

val jsonObject: JSONObject

val jsonArray: JSONArray

try {
jsonArray = JSONArray(response)

for (i in 0 until jsonArray.length()) {
var jsonInfo:JSONObject = jsonArray.getJSONObject(i)

val mMovieModel = ResultModel()
//mMovieModel.setId(object.getString("id"));
mMovieModel.setId(Integer.parseInt(jsonInfo.getString("id")))
mMovieModel.setTitle(jsonInfo.getString("title"))
mMovieModel.setBody(jsonInfo.getString("body"))

apiResults.add(mMovieModel)
}


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

Log.i(javaClass.simpleName, apiResults.size.toString())
return apiResults

}


}

(3) Class in which webservice URL is declared

public class APIURL {
companion object {
const val BASE_URL = "https://jsonplaceholder.typicode.com/"
}
// val BASE_URL = "https://jsonplaceholder.typicode.com/"
}

Step3: ViewModel for MVVM Retrofit

class RetroViewModel: AndroidViewModel {
private var retroRepository: APIServiceFactory
private val retroObservable: LiveData<List<ResultModel>>

constructor(application: Application): super(application){
retroRepository = APIServiceFactory()
retroObservable = retroRepository.providesWebService()
}

fun getProjectRetroListObservable(): LiveData<List<ResultModel>> {
return retroObservable
}

}

Step4 : View for Retro MVVM

public class RetrofitKotlinFragment: Fragment() {

lateinit var retroFitView: View
lateinit var retrofitRecyclerView: RecyclerView
lateinit var retroViewModel: RetroViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retroViewModel = ViewModelProviders.of(activity!!).get(RetroViewModel::class.java)
}


override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
retroFitView = inflater.inflate(R.layout.fragment_retro_fit_post,container,false)
initViews(retroFitView)
setAdapter()
retroViewModel.getProjectRetroListObservable().observe(this,object :Observer<List<ResultModel>>{
override fun onChanged(t: List<ResultModel>?) {
userPostAdapter.setListItems(t)
}

})

/*
userViewModel.getAllUsers().observe(this, object : Observer <List<User>> {
override fun onChanged(users: List<User>?) {
// Update the cached copy of the words in the adapter.
userAdapter.setListItems(users)
}
})
*/

return retroFitView
}


private fun initViews(view: View){
retrofitRecyclerView = view.findViewById(R.id.post_list)as RecyclerView
}
lateinit var userPostAdapter:UserPostAdapter
private fun setAdapter(){
userPostAdapter = UserPostAdapter()
retrofitRecyclerView.layoutManager = LinearLayoutManager(activity, LinearLayout.VERTICAL, false)
retrofitRecyclerView.adapter = userPostAdapter

}

}