RXResult 優雅的封裝Android業務流程

一、簡介

日常開發中,我們經常要處理,請求響應式的業務。而這種業務往往無法通過一個Activity或者一個模塊完成,在組件化類型項目中會變的尤爲明顯。那麼如何優雅地構建易維護、可複用的 Android 業務流程 就成爲了一個剛需。

RxResult是一款基於RxJava的編程範式,框架分爲兩個部分

其中Lib部分主要實現了將原生:

public void startActivityForResult( Intent intent ,int requestCode); 

public void onActivityResult(int requestCode, int resultCode, Intent data);

封裝爲RxJava鏈式調用的形式,即請求和響應在一起,這樣更容易對業務的整體流程進行理解(如:請求註冊-》註冊成功返回賬戶):

 Observable.just(v).compose(RegisterActivity.register(this))
                    .subscribe(user -> {
                        callback.registerCallback(user);
                    });

而Demo部分主要展示了,如何基於RxResult對流程性業務進行封裝,體現編程範式-如何優雅地構建易維護、可複用的 Android 業務流程。

二、 如何使用:

2.1Fragment管理工具類ProcessHelper

2.1.1 創建Fragment

ProcessHelper processHelper = new ProcessHelper(this, R.id.frame_layout);
LoginByPwdFragment loginByPwdFragment = ProcessHelper.findOrCreateFragment(LoginByPwdFragment.class, this);

2.1.2 Fragment跳轉

processHelper.push(loginByPwdFragment);

2.2使用 RxResult.startActivityForResult() 進行跳轉


public static void startActivityForResult(AppCompatActivity activity, Intent intent, int requestCode)

public static void startActivityForResult(Fragment fragment, Intent intent, int requestCode)

public static void startActivityForResult(AppCompatActivity activity, Intent intent, int requestCode, Bundle requestContextData)

public static void startActivityForResult(Fragment fragment, Intent intent, int requestCode, Bundle requestContextData)

四個參數分別爲:上下文,請求體,請求碼,請求參數。

使用示例請求註冊:

public static <T> ObservableTransformer<T, User> register(Fragment fragment) {
        return upstream -> {
            upstream.subscribe(it -> {

                RXResult.startActivityForResult(fragment,
                        IntentBuilder.newInstance(fragment.getContext(), RegisterActivity.class).build(),
                        REQUEST_CODE_LOGIN);

            });

            return RXResult.obtainActivityResult(fragment)
                    .filter(result -> result.requestCode == REQUEST_CODE_LOGIN)
                    .filter(result -> result.resultCode == Activity.RESULT_OK)
                    .map(result -> {
                        return (User) result.getData().getSerializableExtra("user");
                    });
        };
    }

2.3使用RxResult.setResult()進行返回

public static void setResult(AppCompatActivity activity, int resultCode) 

public static void setResult(AppCompatActivity activity, int resultCode, Intent resultData) 

返回三個參數:上下文,返回碼,返回參數(包含請求參數)

使用示例(註冊成功返回):

pwdSetFragment.setCallback(user -> {
            RXResult.setResult(RegisterActivity.this,
                    Activity.RESULT_OK,
                    IntentBuilder.newInstance().putExtra("user", user).build());

            finish();
        });

2.4 鏈式封裝業務流程:

itemCommentClicks
                //Map變化將傳遞過來的index轉換成bundle
                .map(index -> BundleBuilder.newInstance().putInt("index", index).build())
                //體現 邏輯 流  是否登錄-》是否驗證-》開始評論-》評論成功回調
                .compose(LoginActivity.ensureLogin(MainActivity.this, REQUEST_CODE_LOGIN_FOR_COMMENT))
                .compose(AuthActivity.ensureAuth(MainActivity.this))
                .compose(CommentActivity.startComment(MainActivity.this))
                //Map變化將傳遞出去的index轉換回來
                .map(bundle -> bundle.getInt("index"))
                //根據返回值處理業務
                .subscribe(index -> {
                    MainItemBean itemBean = adapter.getItem(index);
                    itemBean.commentCount++;
                    adapter.notifyItemChanged(index);
                });

注:當需要獲取請求時傳遞的參數時,只需要根據返回的bundle使用請求時的key即可。
如上面例子中,是由一個頁面的列表發出,顯然返回的時候需要知道是返回給哪個Item。
那麼請求時附帶參數“index”,請求響應時會把請求的參數帶回來,此時解析返回bundle中的’'index’即可獲取到請求時傳遞的參數。

三、做了哪些優化

3.1 這裏給出原作者的帖子鏈接:

https://juejin.im/post/5b0a7088f265da0db721cf73

https://juejin.im/post/5b6ede81f265da0f9c67d1c2

3.2 RxResult是在原基礎上優化了:

1.由於Fragment,以及FragmentActivity的不統一,在原作基礎上增加了AndroidX的支持。

2.原作中侵入性較強,對於業務載體Activity需要繼承框架中提供的Activity,這樣做缺點有兩個:

  • 顯然對於引入框架的項目如果存在自己的BaseActivity,這個時候需要對BaseActivity做修改。

  • 雖然業務流程都在Fragment中,但不代表Activity自身不需要頁面(較爲致命的缺陷)。

爲了解決以上問題,將父類改爲幫助者的方式實現對流程載體Fragment的控制。

問題二的體現可以查看Demo中的註冊流程。

3.優化流程控制,增加流程中斷時是返回上一級還是整體結束方法。

4.這裏將原作翻譯成Java版, 原作中Demo使用的是Kotlin開發,這樣可以方便使用Java開發的團隊閱讀以及比較引入成本。

原帖較長,所以爲了節省閱讀時間,先把原文中比較核心的內容摘出來。

四、封裝可複用易維護的流程面臨哪些挑戰:

  1. 流程的體驗應當流暢
  2. 流程需要支持嵌套
  3. 流程步驟間數據傳遞應當簡單
  4. 流程需要適應 Android 組件生命週期
  5. 流程需要可以簡單複用
  6. 流程頁面在完成後需要比較容易銷燬
  7. 流程進行中回退行爲的處理

五、解決方案以及缺陷:

解決方案1: 基於原生startActivityForResult:

  1. 寫法過於 Dirty,發起請求和處理結果的邏輯被分散在兩處,不易維護。
  2. 頁面中如果存在的多個請求,不同流程回調都被雜糅在一個onActivityResult 裏,不易維護。
  3. 如果一個流程包含多個頁面,代碼編寫會非常繁瑣,顯得力不從心。
  4. 流程步驟間數據共享基於Intent,沒有解決 問題(三)。
  5. 流程頁面的自動銷燬和流程進行中回退行爲存在矛盾,問題(六) 和 問題(七) 沒有很好地解決。

解決方案2:EventBus 或者其他基於事件總線的解決方案

總結來說,就是EventBus容易在時間管理上出現混亂,易導致錯誤。

住:這裏我感覺爲了讓回調給到需要的觀察者而添加字段的做法並不是較大的開銷,這中開銷其他的解決方案是也是存在的!所以對於沒有太多多複雜業務,開發人員較少的情況下我依然認爲EventBus是一個可採用的方案。

解決方案3:FLAG_ACTIVITY_CLEAR_TOP 或許是一種方案

  1. 和 startActivityForResult 一樣,寫法 Dirty,如果流程很多,維護較爲不易;
  2. 即使是同一個流程,在同一個頁面中也存在複用的情況,不增加新字段無法在 onNewIntent 裏面區分;
  3. 問題(三)沒有得到解決,onNewIntent 數據傳遞也是基於 Intent 的,
  4. 也沒有用於步驟間共享數據的措施,共享的數據可能需要從頭傳到尾; 步驟之間有輕微的耦合:每個步驟需要負責啓動它的下一個步驟;

解決方案4:利用新開闢的 Activity 棧來完成業務流程

開闢一個新的任務棧來處理,手機上最近應用列表上,就會多一個App的欄位(多出來的那個代表流程任務棧),也就是說用戶在做流程的時候如果按 Home 鍵切換出去,那他想回來的時候,按 最近應用列表,他會看到兩個任務,他不知道回哪個。

解決方案5:使用 Fragment 框架封裝流程(推薦的解決方案,框架也建立在這個方案之上)

優點

  1. 流程步驟間是解耦的,每個步驟職責清晰,只需要完成自己的事並且通知給宿主;
  2. 回退支持良好,用戶體驗流暢;
  3. 銷燬流程只需要調用Activity 的 finish 方法,非常輕量級;
  4. 只有一個 Activity 代表這個流程暴露給外部,封裝良好而且易於複用;
  5. 流程步驟間數據的共享變得更簡單
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章