RxActivityResult 突破固有思維,獲取 onActivityResult 資料

13kmsteady發表於2019-01-08

學習契機

接觸 RxJava 有一段時間了,但總感覺對於 RxJava 的使用和理解還在入門階段。一方面和自己沒有去深入學習有關,以為使用了一些基礎的操作符,就敢吹牛說自己掌握 RxJava。另一方面對RxJava 事件驅動型 的程式設計思想,筆者始終領悟的不好。

我認為單純的學習操作符,實際意義不大。事實上,筆者之前也花費了大量的時間學習操作符,到頭來發現效果不佳,因為我還是不知道何時,該正確的去使用 RxJava 操作符。我便向朋友請教,他說你可以試著去閱讀一些 Rx 開源專案的原始碼,從簡單的入手,去學習 Rx 帶來的便利,和思維方式的改變。

又是這位朋友,向我推薦了 RxActivityResult。程式碼量不多,很適合我學習。下面,讓我們換種方式,去 startActiviityForResult()

簡介

RxActivityResultVictorAlbertos 大神的又一個 Rx 開源力作,該庫不久前的更新,現已全面支援 AndroidX。當你已經受夠了,從 onActivityResult() 中接受來自系統(如相機),或者自己的回撥資料,不妨嘗試下這個庫,讓你從此告別 onActivityResult()。簡單介紹下這個庫的特點:

  • 傳入 ActivityFragment 的有效例項,就可以在任何類開啟一個 Intent
  • 資料被封裝在一個可觀察的 Observable 中返回,意味著可以繼續享受RxJava 操作符的便利。

使用

  1. 首先在 Project 下的 build.gradle 新增 maven 依賴。
allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
    }
}
複製程式碼
  1. 在 app 下的 build.gradle 新增 RxActivityResultRxJava 的依賴。
implementation 'com.github.VictorAlbertos:RxActivityResult:0.5.0-2.x'
implementation 'io.reactivex.rxjava2:rxjava:2.2.3'
複製程式碼
  1. 新增完依賴,我們需要在 Application 中註冊 RxActivityResult
class MyApp : Application() {

    override fun onCreate() {
        super.onCreate()
        RxActivityResult.register(this)
    }
}
複製程式碼
  1. 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)
                    }
            }
    }
複製程式碼
  1. 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() 的具體實現是誰。

原始碼分析

  1. 首先我們看下 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() ,是沒有意義的。

  1. 接著看下 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介面,可以通過 onNextonCompleteonError 方法發射和終止發射資料。作者在發射 Result 的時候,使用了 PublischSubjectPublischSubject 的特點是: Observer 只接受被訂閱之後發射的資料。

  1. 看下 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 庫的簡單分析,瞭解了 ActivityLifecycleCallbacksPublischSubject 在三方庫中的具體使用。也認識了一些 RxJava 的操作符,如 takeWhile 過濾操作符。更重要的是,在沒有遇見 RxActivityResult 時,筆者通常都是按部就班的在 onActivityResult() 中獲取資料,而作者的這種方式,打破了我之前的認識,原來還可以這樣處理 onActivityResult() 資料。

當我對目前這種學習方式(使用-原始碼分析-總結),所帶來的收穫沾沾自喜的時候。朋友的一番話,又警醒了我。閱讀原始碼,只是進階的第一步。更重要的是,對思想的掌握,站在更高的角度去思考,為什麼這樣設計。而不應該只滿足於基礎的原始碼分析,背後的設計思想才是精髓。

如果只是對原始碼進行分析,按照作者的思路去撥開雲霧,得到的進步是有限的。需要再進一步的 思考,這將是我接下來需要學習的地方。

相關文章