Retrofit With MVVM And Repository Pattern.

Resources
Raw data of comments

jsonschema2pojo

Generate Plain Old Java Objects from JSON or JSON-Schema.
Preview
<uses-permission android:name="android.permission.INTERNET"/>
dependencies {

implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

//kotlin coroutine
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'

//RetroFit Dependencies
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.3.1'

//Lifecycle
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-common:2.3.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
xmlns:tools="http://schemas.android.com/tools">
<TextView
android:id="@+id/tv_postid"
tools:text="post id"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

<TextView
android:id="@+id/tv_id"
tools:text="user id"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

<TextView
android:id="@+id/tv_name"
tools:text="user name"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

<TextView
android:id="@+id/tv_email"
tools:text="user email"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_body"
tools:text="user body"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

class Comment {
@SerializedName("postId")
@Expose
var postId: Int? = null

@SerializedName("id")
@Expose
var id: Int? = null

@SerializedName("name")
@Expose
var name: String? = null

@SerializedName("email")
@Expose
var email: String? = null

@SerializedName("body")
@Expose
var body: String? = null
}
object Constants {
const val BASE_URL = "https://jsonplaceholder.typicode.com/"
val COMMENT = "comments"
}
import retrofit2.http.GET

interface ApiInterface {
@GET("comments")
suspend fun getAllComments():List<Comment>
}

class ApiHelper (private val apiInterface: ApiInterface) {
suspend fun getAllComments() = apiInterface.getAllComments()
}
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object RetrofitClientCalling {

var mHttpLoggingInterceptor = HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY)

var mOkHttpClient = OkHttpClient
.Builder()
.addInterceptor(mHttpLoggingInterceptor)
.build()

var mRetrofit: Retrofit? = null


val client: Retrofit?
get() {
if(mRetrofit == null){
mRetrofit = Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.client(mOkHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return mRetrofit
}

}
import android.app.Application
import com.example.retrofit.RetrofitClientCalling.client

class AppCreator : Application() {
companion object {

private var mApiHelper:ApiHelper? = null
fun getApiHelperInstance():ApiHelper{
if(mApiHelper==null){
mApiHelper = ApiHelper(client!!.create(ApiInterface::class.java))
}
return mApiHelper!!
}

}
}
class CommentRepo(private val apiHelper: ApiHelper) {

suspend fun getAllComments() = apiHelper.getAllComments()

}
enum class Status {
SUCCESS,
FAILURE,
LOADING
}
data class Resource<out T>
(val status: Status, val data:T?, val message:String?){

companion object{

fun <T> success(data:T): Resource<T> =
Resource(status = Status.SUCCESS, data = data, message = null)

fun <T> error(data:T?, message: String?):Resource<T> =
Resource(status = Status.FAILURE, data = data, message = message)

fun <T> loading(data:T?):Resource<T> =
Resource(status = Status.LOADING, data = data, message = null)
}

}
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData

class CommentViewModel(private val mCommentRepo: CommentRepo) : ViewModel(){

fun getAllComments() = liveData {
emit(Resource.loading(null))
try{
emit(Resource.success(mCommentRepo.getAllComments()))
} catch (e:Exception){
emit(Resource.error(null,e.message.toString()))
}
}

}
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import java.lang.IllegalArgumentException
@Suppress("UNCHECKED_CAST")
class ViewModelFactory(private val apiHelper: ApiHelper) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if(modelClass.isAssignableFrom(CommentViewModel::class.java)){
return CommentViewModel(CommentRepo(apiHelper)) as T
}
throw IllegalArgumentException("Class not found")
}
}
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class CommentRecyclerAdapter
: RecyclerView.Adapter<CommentRecyclerAdapter.ViewHolder>() {

private val mCommentList = ArrayList<Comment>()

//view holder class which binds the view with respective data that came to adaper.
class ViewHolder(itemView: View)
: RecyclerView.ViewHolder(itemView) {

private val mTvPostId : TextView
private val mTvId : TextView
private val mTvName : TextView
private val mTvEmail : TextView
private val mTvBody : TextView

init {
mTvPostId = itemView.findViewById(R.id.tv_postid)
mTvId = itemView.findViewById(R.id.tv_id)
mTvName = itemView.findViewById(R.id.tv_name)
mTvEmail = itemView.findViewById(R.id.tv_email)
mTvBody = itemView.findViewById(R.id.tv_body)
}

fun bind(comment: Comment){
mTvPostId.text = comment.postId.toString()
mTvId.text = comment.id.toString()
mTvName.text = comment.name
mTvEmail.text = comment.email
mTvBody.text = comment.body
}

}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater
.from(parent.context)
.inflate(R.layout.recycler_item_layout,parent,false)
)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(mCommentList[position])
}

override fun getItemCount(): Int {
return mCommentList.size
}

fun updateRecyclerData(postList:List<Comment>){
mCommentList.clear()
mCommentList.addAll(postList)
notifyDataSetChanged()
}

}
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.observe
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

private lateinit var mCommentViewModel:CommentViewModel
private lateinit var mRecyclerAdapter: CommentRecyclerAdapter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

initData()
setRecycler()
obtainListFromServer()

}

//constantly observing the data came from the API service, also listening to the status
//of the end result as can see through the when cases.
private fun obtainListFromServer() {
mCommentViewModel.getAllComments().observe(this) {
when(it.status){
Status.SUCCESS -> {
mRecyclerAdapter.updateRecyclerData(it.data!!)
}
Status.FAILURE -> {
Toast.makeText(
this,
"Failed to load the data ${it.message}",
Toast.LENGTH_LONG
).show()
}
Status.LOADING -> {
Toast.makeText(
this,
"Loading...",
Toast.LENGTH_LONG
).show()
}
}
}
}

private fun setRecycler() {
rv.layoutManager = LinearLayoutManager(this)
rv.setHasFixedSize(false)
rv.adapter = mRecyclerAdapter
}

private fun initData() {
//initialization of viewmodel instance,
mCommentViewModel = ViewModelProvider(
this,
ViewModelFactory(AppCreator.getApiHelperInstance())
).get(CommentViewModel::class.java)

mRecyclerAdapter = CommentRecyclerAdapter()

}

}

--

--

--

Android Developer || WomenTech Global Ambassador at WomenTech Network. || Yoga Teacher || Member @WTM .

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Automate Your Android Application’s Publish Process Via AppGallery Connect Publishing API

Simple Jetpack Compose Navigation Example

Java Vs Kotlin: Which is best for Android App Development

Publishing your first Android library to Bintray

What is new in Flutter 2.8?

Lessons learned on Jetpack Compose UI Testing: Robot Pattern

Fixing unit test issue of RxJava Chain and Kotlin

How Kotlin By Variable Delegate Helps Me Avoid Anti-Pattern

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Harshita Bambure

Harshita Bambure

Android Developer || WomenTech Global Ambassador at WomenTech Network. || Yoga Teacher || Member @WTM .

More from Medium

Global renaming in an Android project

Testing using Idling Resources in Espresso

Tips for Developing the Image Storage and Sharing Functions for Your App

Multiple Image Selecter in android studio with Matisse