Kotlin Coroutines simplify asynchronous programming, leaving all the complications inside the libraries.

I created a simple project: Retrofit Service, 1 Activity, 1 Adapter, 1 Model.

Here’s what I have in the app level of build.gradle: RecyclerView, Coroutines, Retrofit:

dependencies {
    // ...
    implementation 'com.android.support:recyclerview-v7:28.0.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.26.1'
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
    implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-experimental-adapter:1.0.0'
}

kotlin {
    experimental {
        coroutines "enable"
    }
}

androidExtensions {
    experimental = true
}

Model: (I will use the old good JSONPlaceholder):

data class Post(
        val userId: Long,
        val id: Long,
        val title: String,
        val body: String
): Serializable

Adapter and holder (by the way, an interesting solution with LayoutContainer that allows you to import View elements through Kotlin Extensions, for this, by the way, you need the androidExtensions -> experimental = true block in build.gradle)

class MainAdapter(var items: List<Post>): RecyclerView.Adapter<MainAdapter.PostHolder>() {
    override fun getItemCount() = items.size
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = PostHolder(LayoutInflater.from(parent.context).inflate(R.layout.post_item, parent, false))
    override fun onBindViewHolder(holder: PostHolder, position: Int) { holder.bind(items[position]) }

    inner class PostHolder(override val containerView: View): RecyclerView.ViewHolder(containerView), LayoutContainer {
        fun bind(item: Post) {
            postId.text = item.id.toString()
            postTitle.text = item.title
            postBody.text = item.body
        }
    }
}

Retrofit API interface:

interface JsonPlaceholderApi {

    @GET("/posts")
    fun getPosts(): Deferred<Response<List<Post>>>

    companion object {
        private const val BASE_URL = "https://jsonplaceholder.typicode.com"
        fun getApi(): JsonPlaceholderApi = Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(MoshiConverterFactory.create())
                    .addCallAdapterFactory(CoroutineCallAdapterFactory())
                    .build()
                    .create(JsonPlaceholderApi::class.java)
}

And, finally, Activity and layouts:

class MainActivity : Activity() {

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

        val adapter = MainAdapter(listOf())
        recycler.adapter = adapter

        GlobalScope.launch(Dispatchers.Main) {
            progress.visibility = View.VISIBLE
            val postsRequest = JsonPlaceholderApi.getApi().getPosts()
            val postsResponse = postsRequest.await()
            progress.visibility = View.GONE
            if (postsResponse.isSuccessful) {
                adapter.items = postsResponse.body() ?: listOf()
                adapter.notifyDataSetChanged()
            } else {
                Toast.makeText(this@MainActivity, "Error ${postsResponse.code()}", Toast.LENGTH_SHORT).show()
            }
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
    >

    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:visibility="gone"
        />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager"
        tools:listitem="@layout/post_item"
        />

</android.support.constraint.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    >

    <TextView
        android:id="@+id/postId"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="12sp"
        android:textColor="@android:color/darker_gray"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        tools:text="id"
        />

    <TextView
        android:id="@+id/postTitle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textSize="16sp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/postId"
        android:layout_marginEnd="10dp"
        tools:text="title"
        />

    <TextView
        android:id="@+id/postBody"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        app:layout_constraintTop_toBottomOf="@id/postTitle"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="5dp"
        tools:text="body"
        />

</android.support.constraint.ConstraintLayout>

Enjoy! All code (with minor improvements) as always on GitHub.

Leave a Reply