前言
首先,我是个Kotlin的重度使用者。我用Kotlin写过后台应用,写过前端,写过近10个Android项目。
我个人觉得,Kotlin充满了现代化的软件开发所需的语言特色,在我用过的所有语言中(ES6,Python,Go,Java)是最舒服最自然的;JetBrain做了多年IDE,最懂开发者的尿性。如果你会Java,几乎没有学习成本。除了其他优秀的语法外,我也格外喜欢Coroutine,下面就来吹一波。
先吹一波
Coroutine,也叫协程,或者微线程(或者随便你怎么叫它);它是可暂停可恢复的比线程更小的任务单元。目前我所用到的支持协程序的语言有NodeJS,Go,Python;Java并不支持。从原理上看,它们的核心实现原理大都是OS Thread Pool + 状态机,就是靠系统线程池来调度,靠状态机来控制状态。只不过有些是语言天然就支持的,比如Go;而Kotlin是用编译技术来实现的。
不管如何实现,它们一般有这样2个好处:
- 更低资源消耗和更好的调度性能
- 异步代码变同步
至于第一点在Web应用上很实用,因为Web大多是IO密集,它在低配置的机器上可以带来更高的并发,资源消耗还少。但是在Andorid上然并卵,就算是多线程下载的场景,一般并发任务也就5个左右,根本体现不出它的作用。
第二点在Android上就很实用了,我们经常会遇到先执行一段耗时操作,在执行一段后续逻辑的场景。不然Andorid也不会搞出AsyncTask,IntentService,HandlerThread了。它可以完全消除我们的callback。
场景
先看一个真实的发布动态场景。动态中包含文字,用户拍摄或选择的9张图片。整个动态的发布流程是这样:
- 先异步对9张图片进行压缩
- 然后异步将9张图片上传到Server,拿到9个图片Id
- 将文字和图片Id一块上传
第1步,第2步和第3步本身都是需要异步,但1,2,3步之间又是同步顺序执行的关系。
Java中可以用FutureTask实现。
用RxJava来做大概是大量的Callback流配合map操作符来完成,代码虽然比FetureTask好很多,但是并不美观。
用Kotlin Coroutine来做就是:
GlobalScope.launch {
// 其他参数
val params = hashMapOf(
"content" to content,
"longitude" to longitude,
"latitude" to latitude,
"title" to title
)
//1. 构建压缩图片的task
val compressedTasks = paths.map { compressImage(it) } // paths是用户的图片地址集合
//2. 构建上传图片的task,上传任务就是http请求
val uploadTasks = compressedTasks.map { uploadImage(it.await()) } //压缩任务并发执行
//3. 执行上传图片task拿到结果
val imageIds = uploadTasks.map { it.await()!!.data.id } // 上传图片任务并发执行
params["images"] = imageIds.toJson()
val result = "$BASEURL/weibo/create".http(this)
.headers(...)
.params(params)
.post<HttpResult<Dynamic>>().await()
// 更新LiveData
weiboCreateData.postValue(result)
}
复制代码
几个构建task的代码在这里:
/**
* 构建压缩任务
*/
fun compressImage(path: String): Deferred<File>{
val deferred = CompletableDeferred<File>()
// 这里是使用Luban库来压缩图片,具体逻辑可以忽略
deferred.complete(Luban.with(App.context)
.load(path)
.ignoreBy(100)
.get()[0])
return deferred
}
/**
* 构建上传图片的任务
*/
fun uploadImage(file: File): Deferred<HttpResult<List<UploadData>>?> {
return "$BASEURL/weibo/upload".http(this)
.headers(createCommonHeaders(null))
.params("file" to file)
.post<HttpResult<List<UploadData>>>()
}
复制代码
可以看到这种复杂逻辑下,全部代码也就30行左右,并且没有一个Callback,是不是比RxJava好。
上面的网络请求是来自于我的一个库:github.com/li-xiaojun/…
异步逻辑之后通知UI,我建议用LiveData,省去很多界面销毁的判断,而且监听可以自动解除注册,RxJava还要手动dispose。
如果你就想在UI中执行一段异步逻辑然后回到UI线程更新UI,那可以用anko库的封装。
最后
其实,我并不用RxJava,从看到大量的Callback时就放弃了。
我对RxJava并没有深入了解,对于上面的场景,如果RxJava能做的更好,烦请指出。