Android筆記之Fragment的startActivityForResult(與requestPermissions)

druidZ發表於2018-12-25

Android筆記之Fragment的startActivityForResult(與requestPermissions)

一、fragment使用...的注意事項

首先是大家熟悉的,在fragment中使用startActivityForResult和requestPermissions的時候,要注意:

  • 使用fragment自己的方法,即Fragment.startActivityForResult和...,而不是fragment.getActivity(=FragmentActivity)的方法
  • 在Activity中的對應方法(onActivityResult和onRequestPermissionsResult)中不要去掉super.

本來我只是知其然但不知其所以然,但是一次debug讓我瞭解了其中原因。

二、 debug什麼

這樣的,我在fragment中發出startActivityForResult請求,在fragment所在的Activity中的onActivityResult打了個斷點看一下,這一看就發現了有個問題:fragment中啟動的requestCode是0,然而到了activity中的onResult的requestCode變成了65536。

如果是別的數字的話可能會忽略掉,反正最後是能正常呼叫fragment的回撥的,但是65536?2^16??0xffff+1?這個數字可是很特殊。在看了原始碼之後終於有了具體的解釋,所以來看看發出請求到onResult的過程吧。

宣告: 以下原始碼為API 27.1.1

三、(support)Fragment的請求

Android筆記之Fragment的startActivityForResult(與requestPermissions)

可以看到Fragment的startActivityForResult呼叫一個mHost的方法,這個mHost最後就是呼叫了FragmentActivity的startActivityFromFragment,那FragmentActivity做了什麼呢?

Android筆記之Fragment的startActivityForResult(與requestPermissions)
requestCode = -1 這是沒有result的startActivity用的,暫且不表,下邊是重要的部分了,check的函式我也放到一起了:

static void checkForValidRequestCode(int requestCode) {
    if ((requestCode & 0xffff0000) != 0) {
        throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
    }
}
...
    checkForValidRequestCode(requestCode);
    int requestIndex = allocateRequestIndex(fragment);
    ActivityCompat.startActivityForResult(
        this, intent, 
        ((requestIndex + 1) << 16) + (requestCode & 0xffff), options);
複製程式碼

首先是檢查requestCode,高16位不能有1,即requestcode不能大於0xffff,requestIndex可以理解成fragment的一個標識編號,fragmentActivity來管理的。
重點在這裡: 在實際用ActivityCompact啟動請求的時候,傳入的requestCode並不是我們設定的requestCode了,做了一個轉換,((requestIndex + 1) << 16) + (requestCode & 0xffff)
這個轉換的意思就是requestIndex+1放在高16位,requestCode的低16位放在新的低16位,這樣組成了新的requestCode,寫成數學表示式就是:
requestCode = (requestIndex+1)*0xffff+requeseCode 這樣新的requestCode就是大於0xffff的了。

Fragment的startActivityForResult總結

  • 傳入的requestCode要不大於0xffff
  • 實際請求的requestCode做了轉換,變成大於0xffff的數了,但是低8位仍然是原來的requestCode,高8位是requestIndex+1

四、 FragmentActivity的請求

首先還是看原始碼:

Android筆記之Fragment的startActivityForResult(與requestPermissions)
嗯..那個check的函式就是上邊那個,所以結果很清楚了。

FragmentActivity的startActivityForResult總結

  • 沒幹啥,就要求requestCode不大於0xffff,實際請求的requestCode沒變

FragmentActivity的onActivityResult

首先我們要明白,Fragment的請求也是先有FragmentActivity來處理的。然後經過上邊的分析,我們大概可以猜測,通過轉換requestCode,Fragment和FragmentActivity的請求就被分開了,那onResult的時候也是分開處理的。來看code:

Android筆記之Fragment的startActivityForResult(與requestPermissions)

分開來看:

int requestIndex = requestCode>>16;
if (requestIndex != 0) {
    requestIndex--;
    ...
}
複製程式碼

首先是除以(右移)16,這樣剩下的就是高16位,如果不為0的話,就說明requestCode是大於0xffff的,就是我們Fragment發出的請求,不為0的高16位就是requestIndex。下邊就是根據requestIndex找到對應的Fragment了

targetFragment.onActivityResult(
requestCode & 0xffff, resultCode, data);
複製程式碼

這是真正呼叫找到的Fragment的onResult的地方,由於請求的時候requestCode經過了轉換,所以這裡要轉換回來,取出低16位就是原來的requestCode。

五、one more thing

以上是分析了startActivityForResult的流程,requestPermissions是類似的可以自己去看一遍。原始碼真的是好東西,Google的工程師也真的厲害,這個邏輯不難但是讓我這種新手是不好想出來的。

//作為Android開發的初學者,如果我有錯誤的地方或者不足的話歡迎大家指正。希望與大家一同進步。

Android筆記之Fragment的startActivityForResult(與requestPermissions)

相關文章