學習契機
接觸 RxJava
有一段時間了,但總感覺對於 RxJava
的使用和理解還在入門階段。一方面和自己沒有去深入學習有關,以為使用了一些基礎的操作符,就敢吹牛說自己掌握 RxJava
。另一方面對RxJava
事件驅動型 的程式設計思想,筆者始終領悟的不好。
我認為單純的學習操作符,實際意義不大。事實上,筆者之前也花費了大量的時間學習操作符,到頭來發現效果不佳,因為我還是不知道何時,該正確的去使用 RxJava
操作符。我便向朋友請教,他說你可以試著去閱讀一些 Rx
開源專案的原始碼,從簡單的入手,去學習 Rx
帶來的便利,和思維方式的改變。
又是這位朋友,向我推薦了 RxActivityResult
。程式碼量不多,很適合我學習。下面,讓我們換種方式,去 startActiviityForResult()
。
簡介
RxActivityResult 是 VictorAlbertos 大神的又一個 Rx
開源力作,該庫不久前的更新,現已全面支援 AndroidX
。當你已經受夠了,從 onActivityResult()
中接受來自系統(如相機),或者自己的回撥資料,不妨嘗試下這個庫,讓你從此告別 onActivityResult()
。簡單介紹下這個庫的特點:
- 傳入
Activity
或Fragment
的有效例項,就可以在任何類開啟一個Intent
。 - 資料被封裝在一個可觀察的
Observable
中返回,意味著可以繼續享受RxJava
操作符的便利。
使用
- 首先在 Project 下的
build.gradle
新增maven
依賴。
allprojects {
repositories {
google()
jcenter()
maven { url "https://jitpack.io" }
}
}
複製程式碼
- 在 app 下的
build.gradle
新增RxActivityResult
和RxJava
的依賴。
implementation 'com.github.VictorAlbertos:RxActivityResult:0.5.0-2.x'
implementation 'io.reactivex.rxjava2:rxjava:2.2.3'
複製程式碼
- 新增完依賴,我們需要在
Application
中註冊RxActivityResult
。
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
RxActivityResult.register(this)
}
}
複製程式碼
- 在
MainActivity
中通過點選按鈕,跳轉至Main2Activity
。以下是程式碼示例
@SuppressLint("CheckResult")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val intent = Intent(this, Main2Activity::class.java)
btnJump
.setOnClickListener {
RxActivityResult
.on(this)
.startIntent(intent)
.map {
it.data()
}
.subscribe {
val extra = it.getStringExtra("resultData")
println(extra)
}
}
}
複製程式碼
- 在
Main2Activity
中,點選按鈕回傳資料。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
btnResult.setOnClickListener {
val intent = intent
intent.putExtra("resultData", "我是回傳的資料")
setResult(Activity.RESULT_OK, intent)
finish()
}
}
複製程式碼
ok,到此一個簡單的使用就完成了。如果搭配 RxBinding
使用,能夠讓自己的程式碼更加 Rx
,保證這一系列操作事件流的完整性。
btnJump
.clicks()
.throttleFirst(500, TimeUnit.MILLISECONDS)
.map { Intent(this, Main2Activity::class.java) }
.flatMap {
RxActivityResult.on(this)
.startIntent(it)
}
.map{ it.data() }
.subscribe {
val stringExtra = it.getStringExtra("resultData")
println(stringExtra)
}
複製程式碼
通過寥寥的幾行程式碼,便告別了 onActivityResult()
,並且可以接收到返回的資料。這裡先丟擲兩個問題:
- 在
Application
中為啥要註冊。 onActivityResult()
的具體實現是誰。
原始碼分析
- 首先我們看下
Application
中的註冊。
class MyApp:Application() {
override fun onCreate() {
super.onCreate()
RxActivityResult.register(this)
}
}
複製程式碼
先簡單介紹下 ActivityLifecycleCallbacks,它是定義在 Application
中的一個介面,可以用來監聽所有 Activity
生命週期的回撥,並且優先於 Activity
生命週期的回撥。使用 ActivityLifecycleCallbacks
也可以判斷當前 App
處於前臺還是後臺。具體的使用請自行查閱。
RxActivityResult.register()
其實返回的是庫作者定義的 ActivitiesLifecycleCallbacks
類。通過檢視原始碼得知,使用了傳入的 Application
物件去註冊監聽 Activity
的生命週期。
到現在也就可以回答提出的第一個問題,在 Application
中註冊 RxActivityResult
,是為了可以監聽到所有 Activity
的生命週期。畢竟在 onPause
之後去 startIntent()
,是沒有意義的。
- 接著看下
Activity
中的具體使用
RxActivityResult
.on(this) // 步驟 1
.startIntent(intent) // 步驟 2
.map { it.data }
.subscribe {
val extra = it.getStringExtra("resultData")
println(extra)
}
複製程式碼
- 呼叫
on()
傳入的this
物件,用於判斷使用者是從Activity
或者Fragment
的操作 startIntent()
方法,最終會呼叫startHolderActivity()
。
@SuppressLint("CheckResult")
private Observable<Result<T>> startHolderActivity(Request request, @Nullable OnPreResult onPreResult) {
OnResult onResult = uiTargetActivity ? onResultActivity() : onResultFragment(); // 判斷從 Activity 或者 Fragment 啟動的 Intent
request.setOnResult(onResult);
request.setOnPreResult(onPreResult);
// 設定請求物件
HolderActivity.setRequest(request);
// 從當前 Activity ,開啟 HolderActivity
activitiesLifecycle.getOLiveActivity().subscribe(new Consumer<Activity>() {
@Override
public void accept(Activity activity) throws Exception {
activity.startActivity(new Intent(activity, HolderActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION));
}
});
// 返回 PublishSubject
return subject;
}
複製程式碼
先看下 onResultActivity()
private OnResult onResultActivity() {
return new OnResult() {
@Override
public void response(int requestCode, int resultCode, Intent data) {
if (activitiesLifecycle.getLiveActivity() == null) return;
//If true it means some other activity has been stacked as a secondary process.
//Wait until the current activity be the target activity
if (activitiesLifecycle.getLiveActivity().getClass() != clazz) {
return;
}
T activity = (T) activitiesLifecycle.getLiveActivity();
// 發射 Result 結果
subject.onNext(new Result<>(activity, requestCode, resultCode, data));
subject.onComplete();
}
@Override
public void error(Throwable throwable) {
subject.onError(throwable);
}
};
}
複製程式碼
建立一個 OnResult
物件,並且在 response()
中發射 Result 結果。
- Subject
Subject
既可以是資料來源Observable
,也可以是資料的訂閱者Observer
。
public abstract class Subject<T> extends Observable<T> implements Observer<T> {
...
}
複製程式碼
通過檢視原始碼可以看到,Subject
實際上還是 Observable
,只不過它實現了 Observer
介面,可以通過 onNext
、onComplete
、onError
方法發射和終止發射資料。作者在發射 Result
的時候,使用了 PublischSubject
,PublischSubject
的特點是: Observer
只接受被訂閱之後發射的資料。
- 看下
HolderActivity
中的操作
-
在
onCreate()
中開啟真正的Intent
物件 -
在
onActivityResult
中關閉HolderActivity
,並且在onDestroy
回傳資料
@Override
protected void onDestroy() {
super.onDestroy();
if (onResult != null)
onResult.response(requestCode, resultCode, data);
}
複製程式碼
總結下大體的邏輯,好比我們購物的流程,從京東下單買書 (startActivityForResult
),店主交待員工小王(HoldrActivity
)去倉庫查詢書籍,並打包傳送快遞(PublischSubject.onNext(Result())
)。快遞員送貨上門,顧客核對購買的資訊(intent.getStringExtra("resultData")
),資訊無誤的話獲取購買的書籍。
總結與思考
通過對 RxActivityResult
庫的簡單分析,瞭解了 ActivityLifecycleCallbacks
和 PublischSubject
在三方庫中的具體使用。也認識了一些 RxJava
的操作符,如 takeWhile
過濾操作符。更重要的是,在沒有遇見 RxActivityResult
時,筆者通常都是按部就班的在 onActivityResult()
中獲取資料,而作者的這種方式,打破了我之前的認識,原來還可以這樣處理 onActivityResult()
資料。
當我對目前這種學習方式(使用-原始碼分析-總結),所帶來的收穫沾沾自喜的時候。朋友的一番話,又警醒了我。閱讀原始碼,只是進階的第一步。更重要的是,對思想的掌握,站在更高的角度去思考,為什麼這樣設計。而不應該只滿足於基礎的原始碼分析,背後的設計思想才是精髓。
如果只是對原始碼進行分析,按照作者的思路去撥開雲霧,得到的進步是有限的。需要再進一步的 思考,這將是我接下來需要學習的地方。