从 Fragment 请求权限时,在 Activity 的 onRequestPermissionsResult 中收到不正确的 resultCode
Receive incorrect resultCode in Activity's onRequestPermissionsResult when request permission from Fragment
我有一个 targetSdkVersion = 23、compileSdkVersion = 23 的应用,其主要 activity 设置如下
- HomeActivity (AppCompatActivity)
- FragmentA (V4 Fragment)
- ViewPager
- NestedFragmentA (V4 Fragment)
- NestedFragmentB (v4 Fragment)
- NestedFragmentC (v4 Fragment)
- NestedFragmentD (v4 Fragment)
- Fragment B (V4 Fragment)
- Fragment C (V4 Fragment)
并且在 HomeActivity
public static final String PERMISSION = Manifest.permission.WRITE_EXTERNAL_STORAGE
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
Log.i("Logger", "Request Code: " + String.valueOf(requestCode));
// Handle permission request result
}
我从@CommonsWare 读到了这个答案,
嵌套片段中的 onRequestPermissionsResult 永远不会被调用(在我的例子中,它的结果将 return 到 HomeActivity)但我对此很好,我会手动通知结果的片段。
但问题是,当我在 HomeActivity 中请求权限时,一切正常。
HomeActivity
// Request permission from HomeActivity
// Supply 101 as request code, get 101 back
@Override
public void clickSomething(View v) {
requestPermissions(new String[]{PERMISSION}, 101);
}
// Logcat
Logger: Request Code: 101 <<< CORRECT
但是在FragmetnA或NestedFragmentA中,当我从嵌套片段请求时,return到HomeActivity的requestCode已经改变
片段A
// Request permission from FragmentA
// Supply 102 as request code, get 358 back
@Override
public void clickAnotherThing(View v) {
requestPermissions(new String[]{HomeActivity.PERMISSION}, 102);
}
// Logcat
Logger: Request Code: 358 <<< INCORRECT
NestedFragmentA
// Request permission from NestedFragmentA
// Supply 103 as request code, get 615 back
@Override
public void clickDifferentThing(View v) {
requestPermissions(new String[]{HomeActivity.PERMISSION}, 103);
}
// Logcat
Logger: Request Code: 615 <<< INCORRECT
您知道导致此问题的原因吗?
我不会否定"Nested Fragments do not receive request permissions (onRequestPermissionsResult()) callback"
.
这个事实
但是我在这里要做的是解释您观察到的关于容器 activity 中接收到的不同 "weird" 请求代码的行为 requestPermissions()
由片段和嵌套片段。
为了解释这一点,让我们考虑您的示例 -
- HomeActivity (AppCompatActivity)
- FragmentA (V4 Fragment)
- ViewPager
- NestedFragmentA (V4 Fragment)
- NestedFragmentB (v4 Fragment)
- NestedFragmentC (v4 Fragment)
- NestedFragmentD (v4 Fragment)
- Fragment B (V4 Fragment)
- Fragment C (V4 Fragment)
仅在 HomeActivity, FragmentA and NestedFragmentA
中实施 onRequestPermissionsResult()
以便更好地理解打印收到的请求代码的日志
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Log.d("debug", "req code :: " + requestCode);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
还请求 FragmentA
和 NestedFragmentA
的某些许可。让我们以位置权限为例
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 102);
现在,每当我们从片段或嵌套片段中 requestPermissions()
时,它都会调用 Fragment class's requestPermissions()
,后者又会调用 FragmentHostCallback's onRequestPermissionsFromFragment()
,而 FragmentHostCallback's onRequestPermissionsFromFragment()
又会调用 FragmentActivity's requestPermissionsFromFragment()
。现在这里是您的请求代码的转换。
验证您的请求代码后,它会调用
ActivityCompat's requestPermissions()
但是
使用转换后的请求代码 -
ActivityCompat.requestPermissions(this, permissions,((fragment.mIndex + 1) << 8) + (requestCode & 0xff));
所以更改后的请求代码是 -
((fragment.mIndex + 1) << 8) + (requestCode & 0xff)
其中 fragment.mIndex
是片段级别。所以对于直接片段(直接表示容器 activity 的 child),它将是“0”
对于直接嵌套的片段(意味着片段紧邻片段),它将是“1”,并且会根据片段的嵌套深度递增。
在我们的例子中,对于 FragmentA
,请求代码更改为
(((0 + 1) << 8) + (102 & 0xff)) which computes to 358
而对于 NestedFragmentA
,请求代码更改为
(((1 + 1) << 8) + (102 & 0xff)) which computes to 614
现在我们知道请求代码在哪里更改了。让我们从 ActivityCompat.requestPermissions()
继续。所以我们知道 ActivityCompat.requestPermissions()
因为我们使用此方法从活动请求权限。
此外,我们知道这将执行一些操作,并且将向用户显示 accept/deny 所请求权限的权限弹出窗口。
现在我们将进入 onRequestPermissionsResult()
。当用户 accept/deny 时,容器 activity 的 onRequestPermissionsResult()
将被调用,因为最终 ActivityCompat.requestPermissions()
被调用。
假设您 accept/deny 来自 FragmentA
的许可,那么您将获得日志-
req code ::358
之后
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
将调用 FragmentActivity's onRequestPermissionsResult()
,后者会执行一些验证并调用
frag.onRequestPermissionsResult(requestCode&0xff, permissions, grantResults);
弓你可以看到frag.onRequestPermissionsResult()
传入的请求码不一样。 requestCode
是 358
,在 &0xff
之后又变成了 102
。
瞧!
这意味着虽然我们在 HomeActivity's onRequestPermissionsResult()
中得到了不同的请求代码 (358
),但我们正在使用原始请求代码 (102
) 调用 FragmentA's onRequestPermissionsResult()
所以我们将从 FragmentA
-
中获取这些日志
req code ::358
现在 NestedFragmentA
。假设您 accept/deny 获得了 NestedFragmentA
的许可,那么您将获得登录 HomeActivity
-
req code ::614
但我们知道 onRequestPermissionsResult()
不会被嵌套片段调用,所以我们不会在 NestedFragmentA's onRequestPermissionsResult()
中获得任何日志
我想我已经解释了为什么我们在容器activity中为片段和嵌套片段制作的requestPermissions()
获得不同请求代码的原因。
所以我会说对于没有嵌套的片段 requestPermissions()
只从片段中实现 onRequestPermissionsResult()
并且只在容器 activity 中实现 onRequestPermissionsResult()
。
对于嵌套片段,应该 requestPermissions()
仅 parent 片段中的嵌套片段所需的权限。看来这是唯一的解决方法。
我有一个 targetSdkVersion = 23、compileSdkVersion = 23 的应用,其主要 activity 设置如下
- HomeActivity (AppCompatActivity)
- FragmentA (V4 Fragment)
- ViewPager
- NestedFragmentA (V4 Fragment)
- NestedFragmentB (v4 Fragment)
- NestedFragmentC (v4 Fragment)
- NestedFragmentD (v4 Fragment)
- Fragment B (V4 Fragment)
- Fragment C (V4 Fragment)
并且在 HomeActivity
public static final String PERMISSION = Manifest.permission.WRITE_EXTERNAL_STORAGE
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
Log.i("Logger", "Request Code: " + String.valueOf(requestCode));
// Handle permission request result
}
我从@CommonsWare 读到了这个答案,
嵌套片段中的 onRequestPermissionsResult 永远不会被调用(在我的例子中,它的结果将 return 到 HomeActivity)但我对此很好,我会手动通知结果的片段。
但问题是,当我在 HomeActivity 中请求权限时,一切正常。
HomeActivity
// Request permission from HomeActivity
// Supply 101 as request code, get 101 back
@Override
public void clickSomething(View v) {
requestPermissions(new String[]{PERMISSION}, 101);
}
// Logcat
Logger: Request Code: 101 <<< CORRECT
但是在FragmetnA或NestedFragmentA中,当我从嵌套片段请求时,return到HomeActivity的requestCode已经改变
片段A
// Request permission from FragmentA
// Supply 102 as request code, get 358 back
@Override
public void clickAnotherThing(View v) {
requestPermissions(new String[]{HomeActivity.PERMISSION}, 102);
}
// Logcat
Logger: Request Code: 358 <<< INCORRECT
NestedFragmentA
// Request permission from NestedFragmentA
// Supply 103 as request code, get 615 back
@Override
public void clickDifferentThing(View v) {
requestPermissions(new String[]{HomeActivity.PERMISSION}, 103);
}
// Logcat
Logger: Request Code: 615 <<< INCORRECT
您知道导致此问题的原因吗?
我不会否定"Nested Fragments do not receive request permissions (onRequestPermissionsResult()) callback"
.
但是我在这里要做的是解释您观察到的关于容器 activity 中接收到的不同 "weird" 请求代码的行为 requestPermissions()
由片段和嵌套片段。
为了解释这一点,让我们考虑您的示例 -
- HomeActivity (AppCompatActivity)
- FragmentA (V4 Fragment)
- ViewPager
- NestedFragmentA (V4 Fragment)
- NestedFragmentB (v4 Fragment)
- NestedFragmentC (v4 Fragment)
- NestedFragmentD (v4 Fragment)
- Fragment B (V4 Fragment)
- Fragment C (V4 Fragment)
仅在 HomeActivity, FragmentA and NestedFragmentA
中实施 onRequestPermissionsResult()
以便更好地理解打印收到的请求代码的日志
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Log.d("debug", "req code :: " + requestCode);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
还请求 FragmentA
和 NestedFragmentA
的某些许可。让我们以位置权限为例
requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 102);
现在,每当我们从片段或嵌套片段中 requestPermissions()
时,它都会调用 Fragment class's requestPermissions()
,后者又会调用 FragmentHostCallback's onRequestPermissionsFromFragment()
,而 FragmentHostCallback's onRequestPermissionsFromFragment()
又会调用 FragmentActivity's requestPermissionsFromFragment()
。现在这里是您的请求代码的转换。
验证您的请求代码后,它会调用
ActivityCompat's requestPermissions()
但是
使用转换后的请求代码 -
ActivityCompat.requestPermissions(this, permissions,((fragment.mIndex + 1) << 8) + (requestCode & 0xff));
所以更改后的请求代码是 -
((fragment.mIndex + 1) << 8) + (requestCode & 0xff)
其中 fragment.mIndex
是片段级别。所以对于直接片段(直接表示容器 activity 的 child),它将是“0”
对于直接嵌套的片段(意味着片段紧邻片段),它将是“1”,并且会根据片段的嵌套深度递增。
在我们的例子中,对于 FragmentA
,请求代码更改为
(((0 + 1) << 8) + (102 & 0xff)) which computes to 358
而对于 NestedFragmentA
,请求代码更改为
(((1 + 1) << 8) + (102 & 0xff)) which computes to 614
现在我们知道请求代码在哪里更改了。让我们从 ActivityCompat.requestPermissions()
继续。所以我们知道 ActivityCompat.requestPermissions()
因为我们使用此方法从活动请求权限。
此外,我们知道这将执行一些操作,并且将向用户显示 accept/deny 所请求权限的权限弹出窗口。
现在我们将进入 onRequestPermissionsResult()
。当用户 accept/deny 时,容器 activity 的 onRequestPermissionsResult()
将被调用,因为最终 ActivityCompat.requestPermissions()
被调用。
假设您 accept/deny 来自 FragmentA
的许可,那么您将获得日志-
req code ::358
之后
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
将调用 FragmentActivity's onRequestPermissionsResult()
,后者会执行一些验证并调用
frag.onRequestPermissionsResult(requestCode&0xff, permissions, grantResults);
弓你可以看到frag.onRequestPermissionsResult()
传入的请求码不一样。 requestCode
是 358
,在 &0xff
之后又变成了 102
。
瞧!
这意味着虽然我们在 HomeActivity's onRequestPermissionsResult()
中得到了不同的请求代码 (358
),但我们正在使用原始请求代码 (102
) 调用 FragmentA's onRequestPermissionsResult()
所以我们将从 FragmentA
-
req code ::358
现在 NestedFragmentA
。假设您 accept/deny 获得了 NestedFragmentA
的许可,那么您将获得登录 HomeActivity
-
req code ::614
但我们知道 onRequestPermissionsResult()
不会被嵌套片段调用,所以我们不会在 NestedFragmentA's onRequestPermissionsResult()
我想我已经解释了为什么我们在容器activity中为片段和嵌套片段制作的requestPermissions()
获得不同请求代码的原因。
所以我会说对于没有嵌套的片段 requestPermissions()
只从片段中实现 onRequestPermissionsResult()
并且只在容器 activity 中实现 onRequestPermissionsResult()
。
对于嵌套片段,应该 requestPermissions()
仅 parent 片段中的嵌套片段所需的权限。看来这是唯一的解决方法。