1. 程式人生 > >android用代理實現Rx方式獲取startActivityForResult結果

android用代理實現Rx方式獲取startActivityForResult結果

昨天,在整理程式碼的時候發現onActivityResult方法真的好煩,太影響程式碼的閱讀了,而且編寫的時候也需要到處切code位置。於是就有了這篇部落格和RxActivityResult 這個庫。

大概的呼叫方式就是這樣的

  RxActivityResult.with(MainActivity.this).putString("key", "笑一個")
                        .startActivityWithResult(new Intent(MainActivity.this, SecondActivity.class))
                        .subscribe(new
Consumer<Intent>() { @Override public void accept(Intent intent) throws Exception { tv.setText(intent.getStringExtra("msg")); } });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

原始碼大家直接到連結看好了,這篇部落格主要是聊聊思路

要想完全告別onActivityResult(),有兩種方案 
1、使用程序注入的方式hook系統的activity資料傳送,這種方案比較複雜,用到ndk知識,需要在原始碼下編譯應用,不適合大部分人。 
2、利用代理進行跳轉事件轉發代理和 onActivityResult()事件代理接收後再通知委託者。 
這裡我們用第二種,代理物件有兩個供選擇,一個是activity,每次startActivityForResult的時候開啟的使我們的代理activity(ProxyActivity),然後由ProxyActivity進行轉發intent,並在ProxyActivity裡進行 onActivityResult()事件處理,然後通知委託者;所以我們需要在manifest進行註冊,並且需要合理的管理activity的堆疊。 
一個是利用Fragment,這就需要將Fragment載入到activity上,否則發起intent的時候會報 not attach activity 異常,再一個就是需要相容v4和app兩種情況,當然,如果大家的專案裡的基類是固定的一種就不需要了。

下面我們來看看具體實現方式 
首先構建一個請求容器物件,用來存放被轉發的intent和請求識別碼

public class Request  {
    Intent intent;
    int code;

    public Request(Intent intent, int code) {
        this.intent = intent;
        this.code = code;
    }


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

再構建一個結果容器物件,用來存放onActivityResult返回的結果和請求識別碼

public class Result {
    Intent intent;

    public Result(Intent intent, int code) {
        this.intent = intent;
        this.code = code;
    }

    int code;


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

然後我們的返回結果是要以RxJava的方式傳遞給委託者的。這裡我們可以參考rxbus的實現方式 
構建一個subject

 static PublishSubject<Result> subject = PublishSubject.create();
  • 1
  • 1

然後我們需要把Fragment載入到Activity上,這就需要有一個介面控制元件ID和一個FragmentManager,FragmentManager我們可以從委託者獲取,根據activity和fragmentActivity的不同決定獲取不同的FragmentManager,這個控制元件ID怎麼來呢?熟悉Android原始碼的同學可能就知道了,每個介面的根佈局其實都是同一個系統ID(android.R.id.content)這個ID其實可以用來做很多事,比如透明狀態列時軟鍵盤不頂起佈局的時候可以用來強制位移佈局,或者自己做偽分屏。

接下來就是建立一個代理fragment並且把請求委託給它

            final Request request = new Request(intent, intent.hashCode());
                final V4Fragment v4Fragment = new V4Fragment();
                v4Fragment.setRequest(request);
                v4Transaction.replace(android.R.id.content, v4Fragment)
                        .commitAllowingStateLoss();
                v4Transaction = null;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

這裡的請求識別碼我們直接通過求intent的hash值來得到。

在代理fragment裡進行轉發intent和轉發返回結果操作

public class V4Fragment extends Fragment {
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK) {
            if (request != null) {
                RxActivityResult.post(new Result(data, request.code));
            }
            request = null;
            this.getActivity().getSupportFragmentManager().beginTransaction().detach(this).commit();
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    Request request;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (request != null) {
            Intent intent = request.intent;
            startActivityForResult(intent, 0);
        }
    }

    public void setRequest(Request request) {
        this.request = request;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

通過subject進行傳送

  protected static void post(Result result) {

        if (result.intent != null) {
            subject.onNext(result);
        } else {
            subject.onError(new Exception("intent is null"));
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

根據請求識別碼進行篩選

return subject.filter(new Predicate<Result>() {
                @Override
                public boolean test(Result result) throws Exception {
                    return request.code == result.code;
                }
            }).map(new Function<Result, Intent>() {
                @Override
                public Intent apply(Result result) throws Exception {
                    return result.intent;
                }
            });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

這樣就得到了委託者所需要的返回結果了。