🏝️
Thread({
…
}).start()
可以看到,和 Java 一样也摆脱不了直接使用 Thead
的那些困难和不方便:
- 线程什么时候执行结束
- 线程间的相互通信
- 多个线程的管理
我们可以用 Java 的 Executor
线程池来进行线程管理:
🏝️
val executor = Executors.newCachedThreadPool()
executor.execute({
…
})
用 Android 的 AsyncTask
来解决线程间通信:
🏝️
object : AsyncTask<T0, T1, T2> {
override fun doInBackground(vararg args: T0): String { … }
override fun onProgressUpdate(vararg args: T1) { … }
override fun onPostExecute(t3: T3) { … }
}
AsyncTask
是 Android 对线程池 Executor
的封装,但它的缺点也很明显:
- 需要处理很多回调,如果业务多则容易陷入「回调地狱」。
- 硬是把业务拆分成了前台、中间更新、后台三个函数。
看到这里你很自然想到使用 RxJava 解决回调地狱,它确实可以很方便地解决上面的问题。
RxJava,准确来讲是 ReactiveX 在 Java 上的实现,是一种响应式程序框架,我们通过它提供的「Observable」的编程范式进行链式调用,可以很好地消除回调。
使用协程,同样可以像 Rx 那样有效地消除回调地狱,不过无论是设计理念,还是代码风格,两者是有很大区别的,协程在写法上和普通的顺序代码类似。
这里并不会比较 RxJava 和协程哪个好,或者讨论谁取代谁的问题,我这里只给出一个建议,你最好都去了解下,因为协程和 Rx 的设计思想本来就不同。
下面的例子是使用协程进行网络请求获取用户信息并显示到 UI 控件上:
🏝️
launch({
val user = api.getUser() // 👈 网络请求(IO 线程)
nameTv.text = user.name // 👈 更新 UI(主线程)
})
这里只是展示了一个代码片段,launch
并不是一个顶层函数,它必须在一个对象中使用,我们之后再讲,这里只关心它内部业务逻辑的写法。
launch
函数加上实现在 {}
中具体的逻辑,就构成了一个协程。
通常我们做网络请求,要不就传一个 callback,要不就是在 IO 线程里进行阻塞式的同步调用,而在这段代码中,上下两个语句分别工作在两个线程里,但写法上看起来和普通的单线程代码一样。
这里的 api.getUser
是一个挂起函数,所以能够保证 nameTv.text
的正确赋值,这就涉及到了协程中最著名的「非阻塞式挂起」。这个名词看起来不是那么容易理解&