這一系列的文章將會用來進行Coroutines和Rxjava在解決非同步程式設計方面的對比。
範例一:為一個快速啟動的App建立複雜的物件
如果希望自己的應用程式能夠快速啟動,那麼處理建立物件的過程非常重要,如果對於複雜的物件建立發生在UI執行緒的話,將會引起丟幀的情況。
所以對於這種情況我們我們需要在後台執行緒中做這些操作。
RxJava
我們如何使用Rxjava來初始化我們想要的物件?
fun initializeObjectsAsync(): Completable{
return Completable.create{ emitter ->
try{
heavyInitialzation()
if(!emitter?.isDisposed){
emitter?.onComplete()
}
}catch(e:Exception){
if (!emitter?.isDisposed) {
emitter?.onError(e)
}
}
}
}
正如你所見,我們建立了一個方法回傳Completable物件,在方法內部,我們透過Completable.create建立了一個Completable,他將使用一個發射器(這個物件是可以被訂閱的)。
執行完複雜的初始化後,我們將通知成功,如果有錯誤發生,我們將通知所發生的錯誤,這是因為發射器的型別是CompletableEmitter,而onComplete和onError是可用於將結果傳遞給訂閱伺服器的方法。
另一種方法是使用Completable.fromCallable()
fun initializeObjectsAsync(): Completable {
return Completable.fromCallable({
heavyInitialization()
})
}
我們如何消費該方法呢?
Observables和Completables在Rxjava中都是冷冰冰的,這意味著我們衹有訂閱時,才會執行Completable.create中的程式碼,需要記住的是,每次訂閱都會執行。
我們必須訂閱我們在上面建立的initializeObjectsAsync函式中建立的Completable。
fun initializeObjects() {
initializeObjectsAsync()
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
// The initialization succeeded!
// We can perform UI changes here
}, {
// An Error occurred!
})
}
我們如何告訴RxJava我們想要在後台執行緒中執行初始化操作?我們使用subscribeOn運算子來告訴RxJava,我們希望Completable.create中的程式碼能夠在後台執行緒上執行。
當執行完畢時,我們需要對UI進行更新操作,我們可以使用observableOn運算子來告訴RxJava我們想在Android主執行緒中監聽結果。
衹有當你訂閱Completable時,才會執行Completable.create中的程式碼
定義執行緒之後,我們需要啟動訂閱才能在完成時接收到通知,使用.subscribe方法來做到這一點,我們需要傳遞兩個程式碼塊,一個是接收成功的方法,一個是接收失敗的方法。
如果想要上述程式碼被執行,我們需要建立一個Android Activity,例如,我們可以在onCreate方法中呼叫這個方法。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initializeObjects()
}
Kotlin Coroutines
使用Coroutines的話會更簡單,從概念上講,協程和執行緒類似,我們可以撰寫在特定執行緒上執行的程式碼。
CoroutineBuilder是一個可以用來建立Coroutine的方法,執行一段程式碼,並以某種形式訪問其結果,CoroutineBuilder的範例有:launch,async,runBlocking...
假設我們要像在onCreate方法中那樣呼叫heavyInitialization方法,我們可以使用CoroutineBuilder啟動建立一個Coroutine,並在要執行的程式碼塊執行大量初始化的程式碼。
fun initializeObjects() {
launch(CommonPool) {
heavyInitialization()
}
}
CommonPool類似於RxJava中的Schedulers.computation(),將會在後台執行緒中執行程式碼。
讓我們模仿RxJava中構建的範例,我們想知道它什麼時候完成初始化複雜物件和錯誤的處理。
fun initializeObjects() {
launch(CommonPool) {
try {
heavyInitialization()
// The initialization succeeded!
withContext(UI) {
// We can perform UI changes here
}
} catch (e: Exception) {
// An Error occurred!
}
}
}
由於Coroutine中的程式碼被順序執行,因此在初始化成功程式碼將接著初始化的程式碼執行。
和以前一樣,我們可以將呼叫封裝在try catch塊中進行拋錯與例外處理。
我們如何切換UI執行緒來通知UI更新,這裡有一個方法withContext,使用另一個CoroutineContext來執行其中的程式碼塊。
我們在範例中看到的CoroutineContext不是標準Kotlin Coroutines庫中的一部分,由於它是Android專用的,因此可以在其他庫中使用:org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version
範例二:在後台執行緒執行斐波那契數列
區別於在後台執行一些操作,我們也需要回傳一個值,現在我們在使用者點選按鈕是計算斐波那契數列。
想像一下我們使用下面的程式碼來計算斐波那契數列:
private fun fib(n: Long): Long {
return if (n <= 1) n
else fib(n - 1) + fib(n - 2)
}
我們如何在後台執行緒中計算他並且回傳結果呢?
RxJava
這次我們使用Single來實現。
fun fibonacciAsync(number: Long): Single =
Single.create({ emitter ->
val result = fib(number)
if (!emitter?.isDisposed) {
emitter.onSuccess(result)
}
})
你也可以使用fromCallback方法:
fun fibonacciAsync(number: Long): Single =
Single.fromCallable({
return fib(number)
})
我們把我們想要的數值作為該函式的引數傳遞,這也將在Single.create程式碼塊中使用,例如,我們可以從EditText中取得該號碼。
@OnClick(R.id.my_button)
fun onButtonClicked() {
fibonacciAsync(numberInputEditText.text.toString().toLong())
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ fibonacciNumber ->
//Update UI with the result
myTextView.text = fibonacciNumber
},{
// Error happened
})
}
使用者每次點選按鈕,我們都將計算一個新的斐波那契數值,如果使用者修改了Edittext中的值,結果也將會變得不同。
Kotlin Coroutines
這個範例將會和上面的一樣簡單,當使用者點選按鈕,我們就開啟一個Coroutine來計算斐波那契數列。
@OnClick(R.id.my_button)
fun onButtonClicked(){
launch(CommonPool) {
val result = fibonacciAsync (
numberInputEditText.text.toString().toLong()
)
withContext(UI){
fibonacciResultTextView.text = result
}
}
}
下篇文章將介紹取消執行。
如何取消Observable和Coroutine。不要錯過。