Android官方推荐使用协程来处理异步问题。
协程的特点:
首先工程中需要引入 Kotlin 与协程。然后再使用协程发起网络请求。
引入: Android 工程中引入 Kotlin,参考 Android 项目使用 kotlin
有了Kt后,引入协程
|
1
2
3 |
dependencies {
implementation
"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
// 协程} |
启动协程
不同于 Kotlin 工程直接使用 GlobalScope,这个示例在
ViewModel中使用协程。需要使用
viewModelScope。
下面的 CorVm1 继承了 ViewModel。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
import
androidx.lifecycle.ViewModelimport
androidx.lifecycle.viewModelScope
// 引入import
kotlinx.coroutines.Dispatchersimport
kotlinx.coroutines.launchclass
CorVm1 : ViewModel() {
companion object {
const
val TAG =
"rfDevCorVm1"
}
fun cor1() {
viewModelScope.launch { Log.d(TAG,
"不指定dispatcher ${Thread.currentThread()}"
) }
}} |
在按钮的点击监听器中调用 cor1() 方法,可以看到协程是在主线程中的。
|
1 |
不指定dispatcher Thread[main,
5
,main] |
由于此协程通过
viewModelScope启动,因此在 ViewModel 的作用域内执行。如果 ViewModel 因用户离开屏幕而被销毁,则
viewModelScope会自动取消,且所有运行的协程也会被取消。
launch() 方法可以指定运行的线程。可以传入
Dispatchers来指定运行的线程。
先简单看一下
kotlinx.coroutines包里的 Dispatchers ,它有4个属性:
Default,默认Main,Android中指定的是主线程Unconfined,不指定线程IO,指定IO线程都通过点击事件来启动
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 |
// CorVm1.ktfun ioCor() {
viewModelScope.launch(Dispatchers.IO) {
Log.d(TAG,
"IO 协程 ${Thread.currentThread()}"
)
}}fun defaultCor() {
viewModelScope.launch(Dispatchers.Default) {
Log.d(TAG,
"Default 协程 ${Thread.currentThread()}"
)
}}fun mainCor() {
viewModelScope.launch(Dispatchers.Main) { Log.d(TAG,
"Main 协程 ${Thread.currentThread()}"
) }}fun unconfinedCor() {
viewModelScope.launch(Dispatchers.Unconfined) {
Log.d(TAG,
"Unconfined 协程 ${Thread.currentThread()}"
)
}} |
运行log
|
1
2
3
4 |
IO 协程 Thread[DefaultDispatcher-worker-
1
,
5
,main]Main 协程 Thread[main,
5
,main]Default 协程 Thread[DefaultDispatcher-worker-
1
,
5
,main]Unconfined 协程 Thread[main,
5
,main] |
从上面的比较可以看出,如果想利用后台线程,可以考虑
Dispatchers.IO。
Default用的也是
DefaultDispatcher-worker-1线程。
模拟网络请求
主线程中不能进行网络请求,我们把请求放到为IO操作预留的线程上执行。一些信息用 MutableLiveData 发出去。
|
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 |
// CorVm1.ktval info1LiveData: MutableLiveData<String> = MutableLiveData()private
fun reqGet() {
info1LiveData.value =
"发起请求"
viewModelScope.launch(Dispatchers.IO) {
try
{
val conn = url.openConnection() as HttpURLConnection
conn.requestMethod =
"GET"
conn.connectTimeout =
10
*
1000
conn.setRequestProperty(
"Cache-Control"
,
"max-age=0"
)
conn.doOutput =
true
val code = conn.responseCode
if
(code ==
200
) {
val baos = ByteArrayOutputStream()
val inputStream: InputStream = conn.inputStream
val inputS = ByteArray(
1024
)
var len: Int
while
(inputStream.read(inputS).also { len = it } > -
1
) {
baos.write(inputS,
0
, len)
}
val content = String(baos.toByteArray())
baos.close()
inputStream.close()
conn.disconnect()
info1LiveData.postValue(content)
Log.d(TAG,
"net1: $content"
)
}
else
{
info1LiveData.postValue(
"网络请求出错 $conn"
)
Log.e(TAG,
"net1: 网络请求出错 $conn"
)
}
}
catch
(e: Exception) {
Log.e(TAG,
"reqGet: "
, e)
}
}} |
看一下这个网络请求的流程
reqGet()函数viewModelScope.launch(Dispatchers.IO)在协程上发出网络请求1. 协程基础
2. 取消与超时
3. 通道
4. 组合挂起函数
5. 协程上下文与调度器
6. 异常处理
7. select 表达式
8. 共享的可变状态与并发
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70008155/viewspace-2846080/ ,如需转载,请注明出处,否则将追究法律责任。