Android Network Call using Retrofit for Beginners.

Here, we are going to learn about what is Network Call and how to use the Retrofit in your Android application using Kotlin.

Network calls is defined as redeem or adjust API data from a server. This is a regular task in Android development for retrieve data from server.

The application which is connected with the internet mostly uses the HTTP to send and receive data from the server. DefaultHttpClient and HttpUrlConnection require to parse the data manually from input stream and execute the request asynchronously. Instead of this manual management, we use Retrofit.

NOTE: HTTP request is not secure. Now-a-days HTTPS which means (https://) are permitted in android P. if you want to disable the clear text permission it can be done by using the userClearTextTraffic attribute in your AndroidManifest.xml file:

Retrofit is a REST Client for Java, Kotlin, and Android, then It makes it easy to upload and retrieve JSON (or other structured) via REST-based web services. OkHttp library is used for requests by Retrofit.

So, without any further delays, let us get into the new project in Android Studio.

  1. Go to File ⇒ New Project. When it bring on you select the default and Empty Activity to proceed.
  2. Add Retrofit, RecyclerView, Gson dependencies in build.gradle like this.
dependencies {

...

//Retrofit
implementation 'com.squareup.retrofit2:retrofit:latestversion'
implementation 'com.squareup.retrofit2:converter-gson:latestversion'
...
}

3. Add INTERNET permissions in the AndroidManifest.xml file like this.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.augray.combatcorona">
<!--internet access permission-->
<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.CombatCorona">
<activity
android:name=".testcenterpage.TestCenterActivity"
android:configChanges="orientation"
android:screenOrientation="portrait"
android:label="@string/testcenter" >
<intent-filter>
<action android:name = "android.intent.action.MAIN"/>
<category android:name =
"android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

4. Next, Create a class(POJOS class) named StatResponse.kt under a model package like this.

import java.io.Serializable

class StatResponse: Serializable {
var status: Boolean? = null
var data: ArrayList<DataStat?>? = null

}

Create an inner class named DataStat.kt under the model package for accessing the ArrayList DataStat like this.

class DataStat : Serializable {
var updated: Any? = null
var country: String? = null
var countryInfo: CountryInfo? = null
var cases: Int? =null
var todayCases : Int? = null
var deaths : Int? = null
var todayDeaths : Int? = null
var recovered : Int? = null
var todayRecovered : Int? =null
var active : Int? = null
var critical : Int? = null
var casesPerOneMillion : Double? = null
var deathsPerOneMillion : Double? = null
var tests : Int? = null
var testsPerOneMillion : Int? = null
var population : Int? = null
var continent : String? = null
var oneCasePerPeople : Int? = null
var oneDeathPerPeople : Int? = null
var oneTestPerPeople : Int? = null
var undefined : Double? = null
var activePerOneMillion : Double? = null
var recoveredPerOneMillion : Double? = null
var criticalPerOneMillion : Double? = null

var isActive : Boolean? = false
}

5. Create the Retrofit Instance

We need to create an instance using () class and configure it with a base URL to request the Rest-API.

Create a class ApiClient.kt under the network package. Here BASE_URL is the basic URL of our API where we will make a call.

object ApiClient {
var retrofit: Retrofit? = null
get() {
val gson = GsonBuilder().setLenient().create()
if (field == null) {
field = Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
}
return field
}
private set
}

6. Define the Endpoints

Create a class IAPIService.kt under network package. Here the endpoints are defined inside an interface using retrofit annotations to encode details about the parameters and request and response method.

interface IAPIService {

@GET("stats")
fun statsCountryCases(): Call<StatResponse?>?

}

7. Create a custom adapter for Binding data with RecyclerView. Create a class named CountryCasesAdapter.kt under adapter package like this.

class CountryCasesAdapter(var context: Context, private var countrycaselist: ArrayList<DataStat?>?, listener : CountryCasesInterface, var nFilteredList: ArrayList<DataStat?>? = null):RecyclerView.Adapter<CountryCasesAdapter.MyViewHolder>(), Filterable {

var listener: CountryCasesInterface? = null

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context)
.inflate(R.layout.country_cases_rv_row, parent, false)
return MyViewHolder(itemView)
}

override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = countrycaselist!![position]
if (currentItem!!.isActive == true) {
holder.expand_view.visibility = View.VISIBLE
holder.arrow_down_.visibility = View.GONE
holder.arrow_up_.visibility = View.VISIBLE

} else {
holder.expand_view.visibility = View.GONE
holder.arrow_down_.visibility = View.VISIBLE
holder.arrow_up_.visibility = View.GONE
}

if(currentItem!=null)
holder.country.text = currentItem.country
holder.confirmCaseNum.text = currentItem!!
.cases.toString()
holder.totalDeathsNum.text = currentItem
.deaths.toString()
holder.totalRecoveredNum.text = currentItem
.recovered.toString()
Picasso.get().load(currentItem.countryInfo!!.flag)
.into(holder.countryImage)
holder.expand_view_click.setOnClickListener {
listener!!.expandView(currentItem) }
}
class MyViewHolder(itemView: View):RecyclerView.ViewHolder
(itemView) {
var countryImage: ImageView
var country: TextView
var cases: TextView
var totalDeaths: TextView
var totalRecovered: TextView
var confirmCaseNum: TextView
var totalDeathsNum: TextView
var totalRecoveredNum: TextView
var confirmCasesColour: TextView
var totalDeathColour: TextView
var totalRecoveredcolour: TextView
var arrow_down_ : ImageView
var arrow_up_ : ImageView
var expand_view : LinearLayout
var expand_view_click : LinearLayout

init {
countryImage = itemView.findViewById(R.id.CountryImage_id)
country = itemView.findViewById(R.id.countryName_id)
cases=itemView.findViewById(R.id.Confirmed_Cases_Heading_id)
totalDeaths = itemView
.findViewById(R.id.TotalDeaths_Cases_Heading_id)
totalRecovered = itemView
.findViewById(R.id.TotalRecovered_Cases_Heading_id)
confirmCaseNum = itemView
.findViewById(R.id.Confirmed_Cases_Number_id)
totalDeathsNum = itemView
.findViewById((R.id.TotalDeaths_Cases_Number_id))
totalRecoveredNum = itemView
.findViewById(R.id.TotalRecovered_Cases_Number_id)
confirmCasesColour = itemView
.findViewById(R.id.Confimed_colour_id)
totalDeathColour = itemView
.findViewById(R.id.Total_colour_id)
totalRecoveredcolour = itemView
.findViewById(R.id.Total_Recovered_colour_id)
arrow_down_ = itemView
.findViewById(R.id.id_arrow_down_iv1)
arrow_up_ = itemView
.findViewById(R.id.id_arrow_up_iv1)
expand_view = itemView
.findViewById(R.id.expand_view_tc1)
expand_view_click = itemView
.findViewById(R.id.expand_view_click1)
}
}
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence): FilterResults {
val searchString = constraint.toString()
val searchList = ArrayList<DataStat?>()
if (searchString.length == 0 || searchString.isEmpty()) {
searchList.addAll(nFilteredList!!)
}
for (searchDetails in nFilteredList!!) {
if (searchDetails!!.country!!.toLowerCase().contains(searchString)) {
searchList.add(searchDetails)
}
if (searchDetails!!.continent!!.toLowerCase().contains(searchString)) {
searchList.add(searchDetails)
}
}
val filterResults = FilterResults()
filterResults.values = searchList
return filterResults
}

override fun publishResults(constraint: CharSequence, results: FilterResults) {
countrycaselist!!.clear()
countrycaselist!!.addAll((results.values as Collection<DataStat?>))
notifyDataSetChanged()
}
}
}
override fun getItemCount(): Int {
return countrycaselist!!.size
}

init {
this.nFilteredList = ArrayList(countrycaselist)
this.listener = listener
}
}

On the above code, we have to inflate the country_cases_rv_row.xml to fetch the row layout in RecyclerView.

8. Final step

Inside the onCreate() method of the MainActivity.kt, we initialize an instance of the IAPIService interface, the RecyclerView, and the adapter. Finally, we call the generateDataList() method.

import android.R
import android.app.ProgressDialog
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.grocistore.Networking.APIClient
import com.example.grocistore.Networking.ApiInterface
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.util.ArrayList


class MainActivity : AppCompatActivity() {
private var adapter: CountryCasesAdapter? = null
private var recyclerView: RecyclerView? = null
var progressDoalog: ProgressDialog? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
progressDoalog = ProgressDialog(this@MainActivity)
progressDoalog!!.setMessage("Loading....")
progressDoalog!!.show()

/*Create handle for the RetrofitInstance interface*/
apiService = ApiClient.retrofit!!
.create(IAPIService::class.java)
val call = apiService.statsCountryCases()

call!!.enqueue(object: Callback<StatResponse?> {
override fun onResponse(call: Call<StatResponse?>, response: Response<StatResponse?>) {
if (response.isSuccessful) {
statResponse = response.body()
countrycaselist = statResponse!!.data!!
generateDataList(countrycaselist);


}
}
override fun onFailure(call: Call<StatResponse?>, t: Throwable) {
Log.e("TAG", "Response = $t")
viewCallback!!.displayError("throwable =" + t.localizedMessage)
}
})
}

/*Method to generate List of data using RecyclerView with custom adapter*/
private fun generateDataList(countrycaselist_: ArrayList<DataStat?>?) {
countryCasesAdapter = CountryCasesAdapter(applicationContext, countrycaselist_, this@CountryCasesActivity, nFilterList)
recyclerView = findViewById<View>(R.id.Recyclerview_Country_cases_id) as RecyclerView
recyclerView!!.setHasFixedSize(true)
recyclerView!!.layoutManager = LinearLayoutManager(this@CountryCasesActivity)
recyclerView!!.adapter = countryCasesAdapter
}
/*expand view function*/
override fun expandView(item: DataStat) {
item.isActive = item.isActive != true
countryCasesAdapter!!.notifyDataSetChanged()
}
}

8. Understanding enqueue()

enqueue() Asynchronously send the request and notify callback of its response or if an error occurred talking to the server, creating the request, or processing the response. Since this request is asynchronous, Retrofit handles it on a background thread so that the main UI thread isn't blocked or interfered with.

To use enqueue(), you have to implement two callback methods:

  • onResponse()
  • onFailure()

9. Fire up the app

Finally, you can fire up the app and see a nice list like this.

And that’s it. Thank you for sticking with me till the end. I hope this was helpful in some way to you.

Did you like to read this? Don’t forget to like and share. See more by following me and keep supporting my blog when they are published. Thank you ♥♥.

Android Developer