Kotlin 协程上手实践

前言

上一篇文章,我们算是讲的理论知识,因为上次给的是伪代码,并没有实际的进行代码的编写,这一篇文章着重给大家演示实际的使用,没有看过上一篇文章的同学,建议先去看看哈~

文章地址:一文教你快速上手Kotlin协程的使用

注意:本文并未针对网络请求过程中的网络异常和服务器数据异常进行处理,请同学们悉知。

搞事情

准备工作的描述

  • 新建一个 Kotlin 项目:这样 AndroidStudio 就会自动给我们添加上相关的插件配置和依赖
  • 联网权限:因为我们要请求网络数据呀,所以这一条必不可少
  • 安卓 API 27 以上的网络明文流量配置:不会的同学请看康师傅的这篇文章
  • DataBinding、ViewBinding 的配置(非必要):请同学们自行查找相关资料,这里我就不讲啦(这条并不是必须的,同学们还是可以使用 findViewById 进行等价替换的)
  • APP 模块的 Gradle 依赖添加:见下方配置

APP 模块的 Gradle 依赖添加

1
2
3
4
5
6
7
8
9
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// 协程
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.0"
// Kotlin
implementation "androidx.activity:activity-ktx:1.2.0"

项目使用到的网络 API 概述

我从网上找了一个获取 Google Maven 仓库包名的接口供大家使用:

https://wanandroid.com/maven_pom/package/json

返回的 Json 格式如下所示(数据进行了删减,减少文章篇幅)

1
2
3
4
5
6
7
8
{
"data": [
"android.arch.core",
"android.arch.lifecycle"
],
"errorCode": 0,
"errorMsg": ""
}

创建 Json 数据所对应的实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
import com.google.gson.annotations.SerializedName

/**
* Maven 依赖信息的实体类
*/
data class MavenDepend(
@SerializedName("data")
val `data`: List<String>,
@SerializedName("errorCode")
val errorCode: Int,
@SerializedName("errorMsg")
val errorMsg: String
)

创建 ServiceCreator 单例类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object ServiceCreator {

private const val BASE_URL = "https://www.amap.com/"

private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()

fun <T> create(serviceClass: Class<T>): T = retrofit.create(serviceClass)

inline fun <reified T> create(): T = create(T::class.java)
}

创建 MavenDependApi 接口并定义相关请求方法

1
2
3
4
5
6
7
8
9
10
import cn.cqautotest.imui.model.MavenDepend
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Url

interface MavenDependApi {

@GET
fun getMavenDepend(@Url url: String): Call<MavenDepend>
}

创建 Repository 单例类,用于请求数据

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
import cn.cqautotest.imui.model.MavenDepend
import cn.cqautotest.imui.network.ServiceCreator
import cn.cqautotest.imui.network.request.api.MavenDependApi
import retrofit2.await

object Repository {

/**
* 协程
*/
suspend fun getMavenPackageListByCoroutine(): MavenDepend {
val mavenDependApi = ServiceCreator.create<MavenDependApi>()
return mavenDependApi.getMavenDepend(url = "https://wanandroid.com/maven_pom/package/json")
.await()
}

/**
* 同步请求
*/
fun getMavenPackageListBySynchronize(): MavenDepend {
val mavenDependApi = ServiceCreator.create<MavenDependApi>()
return mavenDependApi.getMavenDepend(url = "https://wanandroid.com/maven_pom/package/json")
.execute().body()!!
}
}

创建 fragment_coroutine.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
41
42
43
44
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".ui.fragment.CoroutineFragment">

<data>

</data>

<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">

<Button
android:id="@+id/coroutineQueryBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="使用Kotlin协程" />

<Button
android:id="@+id/synchronizeQueryBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="使用同步请求" />

<TextView
android:id="@+id/resultTv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:hint="返回结果将在这里显示" />

</LinearLayout>

</androidx.core.widget.NestedScrollView>

</layout>

创建 BaseFragment 抽象类

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
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment

abstract class BaseFragment : Fragment() {

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(getLayoutResId(), container, false)
}

open fun getLayoutResId(): Int = 0

open fun callAllInit() {
initView()
initData()
initEvent()
initPresenter()
}

open fun initView() {

}

open fun initData() {

}

open fun initEvent() {

}

open fun initPresenter() {

}

open fun onBackPress(): Boolean = false
}

创建 CoroutineFragment 类继承自 BaseFragment 并编写相关代码

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import android.annotation.SuppressLint
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import cn.cqautotest.imui.R
import cn.cqautotest.imui.base.BaseFragment
import cn.cqautotest.imui.databinding.FragmentCoroutineBinding
import cn.cqautotest.imui.model.MavenDepend
import cn.cqautotest.imui.network.request.Repository
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class CoroutineFragment : BaseFragment() {

private var _binding: FragmentCoroutineBinding? = null
private val mBinding get() = _binding!!
private val mMainHandler = Handler(Looper.getMainLooper())

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = DataBindingUtil.inflate(
layoutInflater,
R.layout.fragment_coroutine,
container,
false
)
return mBinding.root
}

override fun onDestroy() {
super.onDestroy()
_binding = null
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
callAllInit()
}

@SuppressLint("SetTextI18n")
override fun initEvent() {
mBinding.run {
coroutineQueryBtn.setOnClickListener {
lifecycleScope.launch {
val json = withContext(Dispatchers.IO) {
val mavenPackage: MavenDepend = Repository.getMavenPackageListByCoroutine()
Gson().toJson(mavenPackage)
}
mBinding.resultTv.text = "协程请求结果:$json"
Log.d(TAG, "query result: ===>$json")
}
}
synchronizeQueryBtn.setOnClickListener {
getThreadHandler().post {
val mavenPackage: MavenDepend? = Repository.getMavenPackageListBySynchronize()
val json = Gson().toJson(mavenPackage)
mMainHandler.post {
// 如果当前 Fragment 已经被销毁了,则不更新 UI ,只打印日志
if (lifecycle.currentState != Lifecycle.State.DESTROYED) {
mBinding.resultTv.text = "同步请求结果:$json"
}
Log.d(TAG, "query result: ===>$json")
}
}
}
}
}

private fun getThreadHandler(): Handler {
val handlerThread = HandlerThread("thread")
handlerThread.start()
return Handler(handlerThread.looper)
}

companion object {
private const val TAG = "CoroutineFragment"
}
}

总结

效果我就不演示给大家看啦,请同学们自行根据文章所提供的源码进行测试吧~

请同学们点赞、评论、打赏+关注啦~


Kotlin 协程上手实践
https://blog.funkt.cn/2021/03/13/1370579727227637760/
作者
anjiemo
发布于
2021年3月13日
许可协议
未经作者授权,禁止转载