前言
首先,我是個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能做的更好,煩請指出。