最近给老项目的sdk从25升级到34,在安卓14设备上测试时会出现如下错误:
One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered exclusively for system broadcasts at android.os.Parcel.createExceptionOrNull(Parcel.java:3057) at android.os.Parcel.createException(Parcel.java:3041) at android.os.Parcel.readException(Parcel.java:3024) at android.os.Parcel.readException(Parcel.java:2966) at android.app.IActivityManager$Stub$Proxy.registerReceiverWithFeature(IActivityManager.java:5684) at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1852) at android.app.ContextImpl.registerReceiver(ContextImpl.java:1792) at android.app.ContextImpl.registerReceiver(ContextImpl.java:1780) at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:755) at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:755) at com.yanzhenjie.permission.bridge.Messenger.register(Messenger.java:45) at com.yanzhenjie.permission.bridge.RequestExecutor.run(RequestExecutor.java:45) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644) at java.lang.Thread.run(Thread.java:1012) Caused by: android.os.RemoteException: Remote stack trace: at com.android.server.am.ActivityManagerService.registerReceiverWithFeature(ActivityManagerService.java:13927) at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2570) at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2720) at android.os.Binder.execTransactInternal(Binder.java:1339) at android.os.Binder.execTransact(Binder.java:1275)
原因是老项目使用了大佬的权限管理工具,而大佬已经不在进行维护导致的新sdk不可用了。
就此决定移除第三方框架根据google的文档自己写权限申请。
Activity
在Activity下很顺利
在工具类中使用此方法判断是否已经拥有了对应的权限,如果有,进行回调执行逻辑。
如果没有则进行申请。
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
) {
iPermission.grantPermissions();
} else {
// Request the camera permission
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_WR_EX_STORAGE_PERMISSION);
}
在Activity下,使用onRequestPermissionsResult 接收结果,根据结果分别执行业务逻辑或者跳转到权限管理手动更改权限配置。
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions,
int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PermissionUtil.REQUEST_WR_EX_STORAGE_PERMISSION){
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (updateDialog != null) {
updateDialog.dismiss();
}
startDownLoadApk(downUrl, newVersionName);
} else {
// Permission was denied
new AlertDialog.Builder(this)
.setTitle(getString(R.string.permissions_failed))
.setMessage(getString(R.string.no_file_write_and_photo_permissions))
.setPositiveButton(getString(R.string.ok_go_set), (dialogInterface, i) -> PermissionUtil.toSetting(getApplicationContext())).show();
}
}
}
Fragment
在对Fragment进行配置时,出现了问题。
androidx.fragment.app.Fragment.java 的源码中onRequestPermissionsResult方法已经彻彻底底的废弃了,而官方推荐使用Result API
Result API? 常年在老环境下开发,根本没用过这东西!所以简单尝试了下。
单申请
首先生命一个ActivityResultLauncher,并实现registerForActivityResult中的毁掉方法。
private ActivityResultLauncher<String> mPermissionResult = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
new ActivityResultCallback<Boolean>() {
@Override
public void onActivityResult(Boolean result) {
if(result) {
//业务逻辑
} else {
new AlertDialog.Builder(getContext())
.setTitle(getString(R.string.permissions_failed))
.setMessage(getString(R.string.no_file_write_and_photo_permissions))
.setPositiveButton(getString(R.string.ok_go_set), (dialogInterface, i) -> PermissionUtil.toSetting(getContext())).show();
}
}
});
然后发射! 这样就可以申请了。
mPermissionResult.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE);
但这只是申请一个权限的方法,多数情况下,需要同时申请好几个权限。这时候发现有个泛类。于是乎尝试进行改造。
批量申请
将泛类改成String[] ,callback会以map的形式返回。
private ActivityResultLauncher<String []> mPermissionResult = registerForActivityResult(
new ActivityResultContracts.RequestMultiplePermissions(),
new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> o) {
//业务逻辑
} else {
new AlertDialog.Builder(getContext())
.setTitle(getString(R.string.permissions_failed))
.setMessage(getString(R.string.no_file_write_and_photo_permissions))
.setPositiveButton(getString(R.string.ok_go_set), (dialogInterface, i) -> PermissionUtil.toSetting(getContext())).show();
}
}
});
然后发射! 这样就可以一次申请多个权限了。
mPermissionResult.launch(new String [] {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE});
对callback的map进行结果校验使用如下代码。
Boolean.TRUE.equals(o.get(Manifest.permission.READ_EXTERNAL_STORAGE)) &&
Boolean.TRUE.equals(o.get(Manifest.permission.WRITE_EXTERNAL_STORAGE)) &&
Boolean.TRUE.equals(o.get(Manifest.permission.CAMERA));
至此,实现了最简单的权限申请功能,接下来就是发挥主观能动性对这些方法进行工具类封装了。