1. 复现场景
我自定义 了一个继而自AppCompatActivity的Activity类,在该类中加载了一个自定义的AppTakephotoFragment, 关键代码如下:
public class FullScreenActivity extends AppCompatActivity {
......
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults){
if(mTakephotoFragment != null){
mTakephotoFragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
AppTakephotoFragment中使用takephoto库拍照,继承自takephoto的TakePhotoFragment,主要代码如下:
public class AppTakephotoFragment extends TakePhotoFragment {
......
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
LinearLayout view = new LinearLayout(getActivity());
takePhoto = getTakePhoto();
takePhoto.onPickFromGallery();
return view;
}
@Override
public void takeCancel() {
super.takeCancel();
seReturnResult(null);
close();
}
@Override
public void takeFail(TResult result, String msg) {
super.takeFail(result, msg);
seReturnResult(null);
close();
}
@Override
public void takeSuccess(TResult result) {
super.takeSuccess(result);
if(result != null && result.getImage()!= null) {
seReturnResult(result.getImage().getOriginalPath());
}
close();
}
private void close() {
getActivity().finish();
}
private void seReturnResult(String dataString) {
if(dataString != null) {
Intent data = new Intent();
data.setData(Uri.parse(dataString));
getActivity().setResult(Activity.RESULT_OK, data);
} else {
getActivity().setResult(Activity.RESULT_CANCELED);
}
}
}
在onCreateView中调用onPickFromGallery时需要申请权限,当我选择拒绝授权时,发现并没有调用takeFail。
2. 问题原因
通过单步调试onPickFromGallery调用流程,发现在调用requestPermissions时传递的requestCode为2000,而在回调到TakePhotoFragment的onRequestPermissionsResult时传回来的requestCode为67536,由于非之前传递的2000,因此导致无法调用到takeFail。
3. 修改方法
按如下方法修改,代码如下:
public class FullScreenActivity extends AppCompatActivity {
......
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults){
super.onRequestPermissionsResult(requestCode,permissions,grantResults); //添加该行代码即可修改
if(mTakephotoFragment != null){
mTakephotoFragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
4. 根因分析
Fragment中requestPermisssions的调用流程如下:
具体的调用流程请读者自行分析,这里只介绍上图中标的两个关键函数requestPermissionsFromFragment和onRequestPermissionsResult。
4.1 requestPermissionsFromFragment
代码如下:
void requestPermissionsFromFragment(Fragment fragment, String[] permissions, int requestCode) {
if (requestCode == -1) {
ActivityCompat.requestPermissions(this, permissions, requestCode);
} else {
checkForValidRequestCode(requestCode); //验证requestCode,超过65535(0xFFFF)会报异常
try {
this.mRequestedPermissionsFromFragment = true;
int requestIndex = this.allocateRequestIndex(fragment);//根据fragment请求权限的顺序分配索引
ActivityCompat.requestPermissions(this, permissions, (requestIndex + 1 << 16) + (requestCode & '\uffff')); //requestCode改变了
} finally {
this.mRequestedPermissionsFromFragment = false;
}
}
}
从代码中可以看到在调用ActivityCompat.requestPermissions时将requestCode修改成了(requestIndex + 1 << 16) + (requestCode & ‘\uffff’),这也是在调试过程中requestCode变为67536的原因。
4.2 onRequestPermissionsResult
先看下FragmentActivity中的onRequestPermissionsResult源代码,如下:
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
mFragments.noteStateNotSaved();
//去掉高于16位的(index)
int index = (requestCode >> 16) & 0xffff;
if (index != 0) {
index--; //index=0
String who = mPendingFragmentActivityResults.get(index);
mPendingFragmentActivityResults.remove(index);
if (who == null) {
Log.w(TAG, "Activity result delivered for unknown Fragment.");
return;
}
//通过who拿到相应的Fragment
Fragment frag = mFragments.findFragmentByWho(who);
if (frag == null) {
Log.w(TAG, "Activity result no fragment exists for who: " + who);
} else {
//通过requestCode & 0xffff去掉高于16位的(index),恢复原来的requestCode
frag.onRequestPermissionsResult(requestCode & 0xffff, permissions, grantResults);
}
}
}
由于FullScreenActivity 继承自AppCompatActivity,而AppCompatActivity又继承自FragmentActivity,因此在FullScreenActivity的onRequestPermissionsResult加入super.onRequestPermissionsResult,显式调用父类的super.onRequestPermissionsResult可以解决该问题。