How to use Kotlin Coroutines with volley

How to use API connection with Kotlin Coroutines + Volley
When start developing API with Volley in Kotlin, you want to use Kotlin Coroutine instead of AsyncTask. We see how to use API connection using Volley and Coroutine as an example.
First of all, How can I take advantage of coroutines so I can write my code in concise manner:
Here's an example to Coroutine calling API Android

Volley overview

Volley is an HTTP library that makes networking for Android apps easier and most importantly, faster. Volley is available on GitHub. Volley is an HTTP client developed by google for android development. You can use suspendCancellableCoroutine to make the Volley API request.

NetworkUtility.kt
package com.example.myapplication

import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.Response
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.Volley
import kotlinx.coroutines.suspendCancellableCoroutine
import org.json.JSONObject
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

class NetworkUtility {
 
    companion object {
        val mRequestQueue: RequestQueue by lazy {
            Volley.newRequestQueue(MainApp.get())
        }

        suspend fun APIrequest(url: String): JSONObject {

            return suspendCancellableCoroutine { continuation ->
                try {
                    // Sucess Listner
                    val success = Response.Listener<JSONObject> { response ->
                        if (continuation.isActive) {
                            continuation.resume(response)
                        }
                    }

                    // Error Listner
                    val error = Response.ErrorListener { error ->
                        if (continuation.isActive) {
                            continuation.resume(JSONObject())
                        }
                    }

                    val jsonObjectRequest =
                        JsonObjectRequest(Request.Method.GET, url, null, success, error)

                    mRequestQueue.add(jsonObjectRequest)

                } catch (e: Exception) {
                    e.printStackTrace()
                    if (continuation.isActive) {
                        if (continuation.isActive) {
                            continuation.resumeWithException(e)
                        }
                    }
                }
            }
        }
    }
 
}

MainViewModel.kt
package com.example.myapplication

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject

class MainViewModel : ViewModel() {

    var usersList: MutableLiveData<ArrayList<User>> = MutableLiveData(arrayListOf())

    var isLoadingData = true
    var showProgress = MutableLiveData<Boolean>()


    fun getData(urlStr: String) {
        showProgress.value = true

        viewModelScope.launch(Dispatchers.IO) {
            val rss = NetworkUtility.APIrequest(urlStr)

            withContext(Dispatchers.Main) {
                // call to UI thread
                isLoadingData = false
                showProgress.value = false
                usersList.value?.addAll(parseJsonString(rss.toString()))
            }
        }
    }

    fun parseJsonString(str: String): ArrayList<User> {
        val jsonOb = JSONObject(str)
        val list: ArrayList<User> = arrayListOf()
        val jsonArray = jsonOb.getJSONArray("data");

        for (j in 0..jsonArray.length() - 1) {
            val user = User(
                jsonArray.getJSONObject(j).getString("id"),
                jsonArray.getJSONObject(j).getString("email"),
                jsonArray.getJSONObject(j).getString("first_name"),
                jsonArray.getJSONObject(j).getString("last_name"),
                jsonArray.getJSONObject(j).getString("avatar")
            )
            list.add(user)
        }
        return list
    }

}


MainActivity.kt
package com.example.myapplication

import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.myapplication.databinding.ActivityMainBinding
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.toolbar.*

class MainActivity : AppCompatActivity() {
    lateinit var viewModel: MainViewModel
    lateinit var userAdapter: UserRVAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding: ActivityMainBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_main)

        setSupportActionBar(toolbar)
        getSupportActionBar()?.setDisplayHomeAsUpEnabled(true)

        viewModel = ViewModelProviders.of(this)[MainViewModel::class.java]
        binding.mainVM = viewModel
        binding.setLifecycleOwner(this)

        viewModel.getData("https://reqres.in/api/users?page=1&per_page=12")

        viewModel.showProgress.observe(this, Observer {
            if (it == true) {
                progressBar.visibility = View.VISIBLE
            } else {
                progressBar.visibility = View.GONE
            }
        })

        viewModel.usersList.observe(this, Observer {
            Log.e("ResSize", "" + it.size + "::" + it)
            userAdapter.addList(it)
        })

        // Creates a vertical Layout Manager
        rv_users.layoutManager = LinearLayoutManager(this)
        userAdapter = UserRVAdapter(this)
        rv_users.adapter = userAdapter
    }
}

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>
        <variable name="mainVM" type="com.example.myapplication.MainViewModel" />

    </data>
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    tools:context=".MainActivity">

    <include layout="@layout/toolbar" />

    <TextView
        app:layout_constraintTop_toBottomOf="@+id/toolbar"
        android:id="@+id/tv_select_user"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Users"
        android:layout_margin="7dp"
        android:textColor="@android:color/black"
        app:layout_constraintLeft_toLeftOf="parent"
        android:textSize="20sp"/>


    <androidx.recyclerview.widget.RecyclerView
        android:layout_marginTop="10dp"
        app:layout_constraintTop_toBottomOf="@id/tv_select_user"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:id="@+id/rv_users"
        android:layout_width="match_parent"
        android:layout_height="0dp"/>

    <ProgressBar
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        android:id="@+id/progressBar"
        android:visibility="gone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

UserRVAdapter.kt
package com.example.myapplication

import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions
import kotlinx.android.synthetic.main.ly_item_user.view.*

class UserRVAdapter(val context: Context): RecyclerView.Adapter<UserRVAdapter.ViewHolder>() {

    var items : ArrayList<User> = arrayListOf()

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val posObj=items.get(position);

        holder.tv_name1?.text =posObj.first_name +" "+posObj.last_name
        holder.tv_email1?.text = posObj.email

        GlideApp.with(holder.iv_profile1.context)
            .load(posObj.avatar)
            .diskCacheStrategy(DiskCacheStrategy.ALL)
            .apply(RequestOptions.circleCropTransform())
            .error(R.drawable.ic_launcher_background)
            .placeholder(R.drawable.ic_launcher_background)
            .into(holder.iv_profile1)
    }

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

    fun addList(itemsNew : ArrayList<User>){
            this.items=itemsNew
            notifyDataSetChanged()
            Log.e("ResSize:ItemsIF", "" + items.size)
    }

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

    class ViewHolder (view: View) : RecyclerView.ViewHolder(view) {
        // Holds the TextView that will add each animal to
        val tv_name1 = view.tv_name
        val tv_email1 = view.tv_email
        val iv_profile1 = view.iv_profile
    }
}

ly_item_user.xml
<?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"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/iv_profile"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_width="50dp"
        android:layout_margin="10dp"
        android:layout_height="50dp"/>

    <TextView
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toRightOf="@id/iv_profile"
        app:layout_constraintBottom_toTopOf="@+id/tv_email"
        android:id="@+id/tv_name"
        android:textSize="20sp"
        android:text="Pradip"
        android:layout_marginLeft="10dp"
        android:textColor="@android:color/black"
        app:layout_constraintVertical_chainStyle="packed"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView
        android:layout_marginLeft="10dp"
        app:layout_constraintLeft_toRightOf="@id/iv_profile"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_name"
        android:id="@+id/tv_email"
        android:text="Tilala"
        android:textColor="@android:color/darker_gray"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <View
        app:layout_constraintTop_toBottomOf="@id/iv_profile"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_width="match_parent"
        android:background="#f3f3f3"
        android:layout_margin="10dp"
        android:layout_height="2dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Best reference : API connection with Volley + Coroutine

No comments:

Post a Comment

Popular Posts