当Fragment的请求权限时,在Activity的onRequestPermissionsResult中接收到错误的resultCode

Tar*_*w45 6 android android-fragments android-permissions android-6.0-marshmallow

我有一个带有targetSdkVersion = 23的应用程序,compileSdkVersion = 23,主要活动设置如下

- 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)
Run Code Online (Sandbox Code Playgroud)

在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
}
Run Code Online (Sandbox Code Playgroud)

我从@CommonsWare读到这个答案,

/sf/answers/2321937201/

嵌套片段中的onRequestPermissionsResult永远不会被调用(在我的情况下,它会返回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
Run Code Online (Sandbox Code Playgroud)

但是在FragmetnANestedFragmentA中,当我从嵌套片段请求时,返回HomeActivity的requestCode已更改

FragmentA

// 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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

你知道什么会导致这个问题吗?

Sha*_*ari 15

我不会否定这个事实"Nested Fragments do not receive request permissions (onRequestPermissionsResult()) callback".

但我在这里要做的是解释你观察到的关于容器活动中收到的不同"怪异"请求代码的行为,这些代码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)
Run Code Online (Sandbox Code Playgroud)

onRequestPermissionsResult()仅为HomeActivity, FragmentA and NestedFragmentA了更好地理解打印收到的请求代码的日志而实施

    @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            Log.d("debug", "req code :: " + requestCode);     
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
Run Code Online (Sandbox Code Playgroud)

还要求某些权限FragmentANestedFragmentA.我们来看一下位置权限

 requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 102); 
Run Code Online (Sandbox Code Playgroud)

现在,无论何时我们requestPermissions()来自片段或嵌套片段,它都会调用Fragment class's requestPermissions()哪些调用依次调用FragmentHostCallback's onRequestPermissionsFromFragment()哪些片段FragmentActivity's requestPermissionsFromFragment().现在这里是您的请求代码的转换.验证您的请求代码后,它会调用

ActivityCompat's requestPermissions()
Run Code Online (Sandbox Code Playgroud)

使用转换后的请求代码 -

ActivityCompat.requestPermissions(this, permissions,((fragment.mIndex + 1) << 8) + (requestCode & 0xff));
Run Code Online (Sandbox Code Playgroud)

所以更改的请求代码是 -

((fragment.mIndex + 1) << 8) + (requestCode & 0xff)
Run Code Online (Sandbox Code Playgroud)

fragment.mIndex片段级别在哪里.因此对于立即片段(直接意味着容器活动的子代),它将是"0"并且对于直接嵌套片段(意味着片段内的片段)它将是"1"并且它将根据深度增加你的片段是嵌套的.

在我们的例子中,for FragmentA,请求代码更改为

 (((0 + 1) << 8) + (102 & 0xff)) which computes to 358
Run Code Online (Sandbox Code Playgroud)

并且NestedFragmentA,请求代码更改为

(((1 + 1) << 8) + (102 & 0xff)) which computes to 614
Run Code Online (Sandbox Code Playgroud)

现在我们知道请求代码的变化.让我们继续吧 ActivityCompat.requestPermissions().因此我们知道,ActivityCompat.requestPermissions()因为我们使用此方法从活动请求权限.此外,我们知道这将执行一些操作,并且将向用户显示权限弹出窗口以接受/拒绝所请求的权限.

现在我们来了onRequestPermissionsResult().当用户接受/拒绝onRequestPermissionsResult()时,容器活动将被调用,因为最终ActivityCompat.requestPermissions()被调用.假设您接受/拒绝了许可,FragmentA那么您将获得日志 -

 req code ::358
Run Code Online (Sandbox Code Playgroud)

之后

super.onRequestPermissionsResult(requestCode, permissions, grantResults);
Run Code Online (Sandbox Code Playgroud)

将调用FragmentActivity's onRequestPermissionsResult()哪个进而执行一些验证和调用

frag.onRequestPermissionsResult(requestCode&0xff, permissions, grantResults);
Run Code Online (Sandbox Code Playgroud)

Bow你可以看到传入的请求代码frag.onRequestPermissionsResult()是不同的.requestCode358以后&0xff就变成102 一次.瞧!! 这意味着虽然我们有不同的请求代码(358)HomeActivity's onRequestPermissionsResult(),但我们FragmentA's onRequestPermissionsResult()使用原始请求代码调用(102)所以我们将从 - 获取这些日志FragmentA-

 req code ::358
Run Code Online (Sandbox Code Playgroud)

现在来了NestedFragmentA.假设您接受/拒绝来自NestedFragmentA您的许可,您将获得登录HomeActivity-

 req code ::614
Run Code Online (Sandbox Code Playgroud)

但我们知道onRequestPermissionsResult()不会为嵌套片段调用,所以我们不会得到任何登录NestedFragmentA's onRequestPermissionsResult()

我想我已经解释了为什么我们在容器活动中requestPermissions()获得由片段和嵌套片段产生的不同请求代码的原因.

所以我要说的是,对于那些不是requestPermissions()仅从片段嵌套 的片段,只onRequestPermissionsResult()在那里实现,而不是在容器活动中实现.对于嵌套片段,应该requestPermissions() 只为父片段中的嵌套片段所需的权限.这似乎是唯一的解决方法.