从 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

但是在FragmetnANestedFragmentA中,当我从嵌套片段请求时,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);
    }

还请求 FragmentANestedFragmentA 的某些许可。让我们以位置权限为例

 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()传入的请求码不一样。 requestCode358,在 &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 片段中的嵌套片段所需的权限。看来这是唯一的解决方法。