零
一、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的請求
可以看到Fragment的startActivityForResult呼叫一個mHost的方法,這個mHost最後就是呼叫了FragmentActivity的startActivityFromFragment,那FragmentActivity做了什麼呢?
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的請求
首先還是看原始碼:
嗯..那個check的函式就是上邊那個,所以結果很清楚了。FragmentActivity的startActivityForResult總結
- 沒幹啥,就要求requestCode不大於0xffff,實際請求的requestCode沒變
FragmentActivity的onActivityResult
首先我們要明白,Fragment的請求也是先有FragmentActivity來處理的。然後經過上邊的分析,我們大概可以猜測,通過轉換requestCode,Fragment和FragmentActivity的請求就被分開了,那onResult的時候也是分開處理的。來看code:
分開來看:
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開發的初學者,如果我有錯誤的地方或者不足的話歡迎大家指正。希望與大家一同進步。