Networking has always been necessary in any app. So I decided to write about doing network calls using Kotlin. So let’s start by setting up the project.
I am using Android Studio 3.0 for this tutorial, so if you want to follow along you’ll want to have that as well.
Create a new project and tick on Include Kotlin support.
Now before doing anything else, let’s setup our dependencies that we’re going to use in our project. For Networking I am going to use Fuel. Its a Networking library written completely in Kotlin, and for parsing JSON into object model, I am going to use Klaxon. I am also going to add dependencies for using Recycler View and CardView. By the end of this tutorial, we’ll be able to understand how to do a basic network request. We’re going to use this Placeholder API for that purpose.
Dependencies
1 2 3 4 |
compile 'com.github.kittinunf.fuel:fuel-android:1.10.0' compile 'com.beust:klaxon:0.30' implementation 'com.android.support:cardview-v7:26.1.0' implementation 'com.android.support:recyclerview-v7:26.1.0' |
1 |
<uses-permission android:name="android.permission.INTERNET"></uses-permission> |
Views
It’s time we start creating our layouts. In your layout folder, create a layout called recycler_view_row.xml. Which is going to represent single row of the list. Paste the following code inside your newly created xml.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" > <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" card_view:cardCornerRadius="4dp" android:layout_margin="5dp" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/title_text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Title" android:gravity="center" android:textAlignment="center" android:textSize="16sp" android:textStyle="bold" /> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="Body" android:id="@+id/body_text_view" /> </LinearLayout> </android.support.v7.widget.CardView> </LinearLayout> |
Now in your activity_main.xml, do the following.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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="com.kodesnippets.aaqibhussain.kotlinfuel.MainActivity"> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:id="@+id/recycler_view" > </android.support.v7.widget.RecyclerView> </RelativeLayout> |
Model
There are two things we need to create, an Adapter for the RecyclerView and the Model which will help us populate the adapter.
Basically our JSON which we are fetching from the server consists of array of JSONObjects. Inside each JSONObject contains keys; id, userId, body and title. So our model will be quite simple.
Create a class and name it PostModel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package com.kodesnippets.aaqibhussain.kotlinfuel import com.beust.klaxon.JsonObject import com.beust.klaxon.int import com.beust.klaxon.string /** * Created by aaqibhussain on 24/9/17. */ class PostModel { var id : Int? = null var userId : Int? = null var body : String? = null var title : String? = null constructor(jsonObject: JsonObject){ id = jsonObject.int("id") userId = jsonObject.int("userId") body = jsonObject.string("body") title = jsonObject.string("title") } } |
Create another class and name it PostAdapter.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package com.kodesnippets.aaqibhussain.kotlinfuel import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView /** * Created by aaqibhussain on 24/9/17. */ class PostAdapter(dataSet: List<PostModel>) : RecyclerView.Adapter<PostAdapter.ViewHolder>(){ val dataSet : List<PostModel> = dataSet final var onClick : (View)-> Unit = {} class ViewHolder(view: View) : RecyclerView.ViewHolder(view){ val titleTextView = view.findViewById<TextView>(R.id.title_text_view) as TextView val bodyTextView = view.findViewById<TextView>(R.id.body_text_view) as TextView } override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent!!.context).inflate(R.layout.recycler_view_row, parent,false) view.setOnClickListener(View.OnClickListener { view -> this.onClick(view) }) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder?, position: Int) { holder?.let { holder -> holder.titleTextView.text = this.dataSet[position].title holder.bodyTextView.text = this.dataSet[position].body } } override fun getItemCount(): Int { return this.dataSet.size } } |
In your MainActivity, create the following variables.
1 2 3 4 |
lateinit var recyclerView : RecyclerView lateinit var recycletViewAdapter : RecyclerView.Adapter<PostAdapter.ViewHolder> lateinit var recyclerViewLayoutManager : RecyclerView.LayoutManager var adapter: PostAdapter? = null |
After that we’ll setup our RecyclerView in onCreate.
1 2 3 4 5 6 7 |
fun setupViews(){ recyclerView = findViewById(R.id.recycler_view) recyclerViewLayoutManager = LinearLayoutManager(applicationContext) recyclerView.layoutManager = recyclerViewLayoutManager } |
and for fetching the JSON, I have written a generic function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
fun getRequest(url: String, success: (String) -> Unit, failure: (FuelError) -> Unit) { Fuel.get(url).responseString() { request, response, result -> val (data, error) = result if (error != null) { Log.v("Error", error!!.toString()) failure(error) } else { val onSuccess = data ?: return@responseString success(onSuccess) } } } |
How to use this function? It’s pretty straight forward. Just provide a URL and it’ll return you a JSON string or an error respectively.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
fun getData(){ val posts = "https://jsonplaceholder.typicode.com/posts" getRequest(posts,success = { response -> val parser = Parser() val stringBuilder = StringBuilder(response) val model = parser.parse(stringBuilder) as JsonArray<JsonObject> val postModel = model.map { PostModel(it)}.filterNotNull() this.adapter = PostAdapter(postModel) recycletViewAdapter = adapter!! recyclerView.adapter = recycletViewAdapter recycletViewAdapter.notifyDataSetChanged() adapter?.onClick = { view -> val itemPosition = recyclerView.getChildLayoutPosition(view) Log.d("ITEM:POSITION",postModel[itemPosition].body) } Log.d("code",postModel.first().body) Log.d("Mapped::",postModel.first().title) Log.d("dode",postModel.first().userId.toString()) Log.d("Mapped::",postModel.first().id.toString()) },failure ={ error -> } ) } |
I will explain the above code a little bit. What it does is that when you get the response string, we create a string builder and pass it to the Parser class object’s function parse, provided by the Klaxon library which easily cast it to JsonArray of JsonObjects, which we can cast into our model class object which is PostModel, that’s what is being done here using the .map lambda function of the JsonArray, after the mapping of objects a function named filterNotNull() is called so that all the objects which are not null can be filtered out in an array, which can then be further passed into the adapter.
NOTE: if you use the default Java classes of JSONObject and JSONArray, you won’t be able to use functions like map, flatMap, or filterNotNull.
After everything is done, call both of the functions in your onCreate. It should look something like this.
1 2 3 4 5 6 7 8 |
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setupViews() getData() } |
Now run your app and see the magic. You might get the result similar to the following screenshot.
So that’s all for this tutorial. Hope you guys liked it.
You can find the complete source code for this tutorial here.
Adiós!
1 Comment