如果你觉得写callbacks成本也不大?嗯,那可能被callbacks毒打的还不够,需要丢个王炸出来💣.
无可厚非,每一次新的变革到来之前,我们都会习惯性将旧的规则当作信仰,直到先驱者为他们打开新大陆.
想一想:当Callbacks方法变多 + 嵌套层数变多的时候,耳熟能详的“回调地狱”就出来了👇:
实际需求
想象一下,如果我们有这么一个需求:查找某个用户的好友列表中,第N个好友的最新动态.
翻译成程序步骤:
首先,步骤如右👉:查询该用户信息 —> 查找该用户的好友列表 —> 查找第N个好友的最新动态.
其中,每一步出问题了,我们都需要把对应的信息在UI线程中展现出来(切换线程).
Callbacks方案
- 观感:回调地狱,迷失在代码中.
-
问题:回调代码的处理分散;线程任务管理复杂,可能产生泄漏. 有回调的世界里,我们可能一直要问自己:
- 这个函数会不会产生一个后台任务?
- 这个函数虽然返回了,但是它所产生的后台任务可能还在运行,它什么时候会结束?
- 作为调用者,我应该在哪里、以怎样的方式处理回调?
- 我需要保持这个函数用到的资源吗?后台任务会自动去持有这些资源吗?我需要自己去释放它们吗?
- 派发出去的任务会不会再去派发别的任务?别的这些任务会被正确管理吗?如果取消了这个派发出去的任务,那些被二次派发的任务也会被正确取消吗?
//主线程中执行
fun onCreate() {
//step1: 查询该用户的信息
getUserInfo(uid, object: CallBack() {
override fun onSuccess(user: User) {
//step2:获取该用户的好友列表
getFriendList(user, object: CallBack() {
override fun onSuccess(friendList: List<User>) {
//step3:获取第n个用户的最新动态
getFeedList(friendList[n], object: CallBack() {
override fun onSuccess(feed: List<String>) {
//展示Feed
runOnUiThread {
showFeedList(feed)
}
}
override fun onFail(error: String) {
//展示错误toast
runOnUiThread {
showErrorToast(error)
}
}
});
}
override fun onFail(error: String) {
//展示错误toast
runOnUiThread {
showErrorToast(error)
}
}
});
}
override fun onFail(error: String) {
//展示错误toast
runOnUiThread {
showErrorToast(error)
}
}
});
}
fun onDestory() {
//question: 如何取消三个嵌套callback里的异步线程任务?嵌套的线程任务多了,哪种方式成本都不小. 实现不好,还容易出问题.
}
Coroutines方案
- 观感:顺流而下,一目了然.
- 便利:没有回调,可以统一管理Coroutines,不易产生泄漏.
//主线程中执行
val job: Job? = null
fun onCreate() {
job = coroutineScope(Dispatchers.Main) {
try {
//step1: 查询该用户的信息
val user = getUserInfo(uid)
//step2:获取该用户的好友列表
val friendList = getFriendList(user)
//step3:获取第一个用户的最新动态
val feedList = getFeedList(friendList[0].uid)
//展示Feed
withContext(Dispatchers.Main) {
showFeedList(feed)
}
} catch(e: Exception) {
//展示错误toast
withContext(Dispatchers.Main) {
showErrorToast(e.message)
}
}
}
}
fun onDestroy() {
//一键取消所有的下属Coroutines任务,且不依赖线程的取消.
job?.cancel()
}
-
How?
- 使用上多添加一行suspend关键字即可.
suspend fun getUserInfo(uid: String): User {
return withContext(Dispatcher.IO) {
//network request code
}
}
suspend fun getFriendList(user: User): List<User> {
return withContext(Dispatcher.IO) {
//network request code
}
}
suspend fun getFeedList(uid: String): List<Feed> {
return withContext(Dispatcher.IO) {
//network request code
}
}