Android M版本和非M版本动态权限适配方案---续篇
2016-05-11 21:16
447 查看
在我上一篇文章中介绍了一下Android M版本与非M版本的动态权限适配的方案,只介绍了一个大体思路。
详细的可以看Android M版本和非M版本动态权限适配方案。
这篇文章主要介绍一下该方案的实现。
1. 控制权限申请的入口和出口,将动态权限的出口和入口唯一化,降低动态权限对原来老版本代码的冲击,避免大面积重构代码。
整个方案职能图如下:
2、PermissionsManager核心代码:
3、PermissionsActivity核心代码
注:对于动态权限,建议应用可以区分一下哪些是必要权限和哪些是非必要权限,比如读写sd卡这个权限完全是非必要的,在Android4.4之后,访问自身应用在sd卡的私有路径不需要任何权限(manifest中都不要去声明),对于非必要权限,可以允许用户继续操作,必要权限被拒绝,可以直接退出,避免处理太多的异常场景。
详细的可以看Android M版本和非M版本动态权限适配方案。
这篇文章主要介绍一下该方案的实现。
1. 控制权限申请的入口和出口,将动态权限的出口和入口唯一化,降低动态权限对原来老版本代码的冲击,避免大面积重构代码。
整个方案职能图如下:
2、PermissionsManager核心代码:
/** * 权限申请回调函数 * */ public interface PermissionsResultsCallback { void onRequestPermissionsResult(int[] grantResults); } /** 请求权限CodeId对应到具体的每一次申请*/ private int mRequestCodeId; /** 每一次申请对应回调函数*/ private final Map<Integer, PermissionsResultsCallback> mRequestIdToCallback = new HashMap<Integer, PermissionsResultsCallback>(); /** 每一次申请对应的权限表*/ private final Map<Integer, Map<String, Integer>> mRequestIdToPermissions = new HashMap<Integer, Map<String, Integer>>(); /** 当前请求权限*/ private final Map<String, Integer> mWaitingRequestPermissions = new HashMap<String, Integer>(); /** * 新的请求权限方法,在内部处理M版本和非版本的适配, 同时也是PermissionsManager动态权限申请的入口 * * @param callback 回调函数,回调申请权限的string数组对应的状态(有权限或没有权限) * @param activity * @param permissionsToRequest */ public synchronized void newRequestPermissions(PermissionsResultsCallback callback, Activity activity, String... permissionsToRequest) { // 初始化申请权限状态表 Map<String, Integer> map = PermissionsUtil.getStatusPermissions(mApplicationContext, permissionsToRequest); // 筛选出未获取用户授权的申请权限数组 String[] permissionsArray = PermissionsUtil.arrayDeniedPermissions(map); // 分配申请Id给本次权限申请 int requestId = getNextRequestId(); LogC.i("requestPermissions, requestId:" + requestId, false); // 存储本次申请授权的回调函数和权限状态表 mRequestIdToCallback.put(requestId, callback); mRequestIdToPermissions.put(requestId, map); permissionsArray = getDeniedOrRequestingPermissions(permissionsArray); // 判断当申请权限全部为已授权时,直接回调 if (null == permissionsArray || permissionsArray.length == 0) { // 当前权限申请正在进行 LogC.i("PermissionsManager permissionsArray has a permission requesting or null", false); onRequestPermissionsResult(requestId, permissionsArray, new int[0]); return; } else { for (String permission : permissionsArray) { // 当前权限申请未正在进行 LogC.i("PermissionsManager permission === "+permission+" is requesting", false); mWaitingRequestPermissions.put(permission,requestId); } } // 判断当有申请权限为未授权时,执行权限申请操作 if (null != activity) { LogC.i("PermissionsManager requestPermissions startActivityForResult", false); Intent intent = new Intent(activity, PermissionsActivity.class); intent.putExtra(PermissionsActivity.EXTRA_PERMISSION_REQUESTED_PERMISSIONS, permissionsArray); intent.putExtra(PermissionsActivity.EXTRA_PERMISSION_REQUEST_CODE, requestId); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); activity.startActivityForResult(intent, REQUEST_CODE); } else { // 默认情况下申请权限方式 LogC.i("PermissionsActivity.run(mApplicationContext, requestId, permissionsArray)", false); PermissionsActivity.run(mApplicationContext, requestId, permissionsArray); } } /** * 回调函数,回调每次申请权限的处理接口 同时PermissionsManager动态权限申请的出口 * * @param requestCode * @param permissions * @param grantResults */ public synchronized void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { LogC.i("PermissionsManager onRequestPermissionsResult, requestCode:" + requestCode, false); // 解析权限申请结果,将权限结果状态更新到权限状态表 Map<String, Integer> map = mRequestIdToPermissions.get(requestCode); if (null != map) { // 再次检测刷新申请权限的状态 map = PermissionsUtil.getStatusPermissions(mApplicationContext, map); for (int i = 0; i < grantResults.length; i++) { map.put(permissions[i], grantResults[i]); } grantResults = PermissionsUtil.arrayStatusPermissions(map); for (String permission : map.keySet()) { if(mWaitingRequestPermissions.containsKey(permission) && requestCode == mWaitingRequestPermissions.get(permission)) { LogC.i("PermissionsManager key === "+ permission +" remove !", false); mWaitingRequestPermissions.remove(permission); } } } // 将权限状态回调给本次申请的回调函数 PermissionsResultsCallback permissionsResultsCallback = mRequestIdToCallback.get(requestCode); if (null == permissionsResultsCallback) { LogC.i("onRequestPermissionsResult permissionsResultsCallback null", false); } else { // 删除本次权限申请的回调和状态表 LogC.i("callback remove:" + mRequestIdToCallback.remove(requestCode), false); LogC.i("requestMap remove:" + mRequestIdToPermissions.remove(requestCode), false); permissionsResultsCallback.onRequestPermissionsResult(grantResults); } }
3、PermissionsActivity核心代码
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); sp = getApplicationContext().getSharedPreferences(PERMISSION_CACHE_NAME, Context.MODE_PRIVATE); LogC.i("permissions check onCreate", false); mPendingRequestCode = (savedInstanceState != null) ? savedInstanceState.getInt(EXTRA_PERMISSION_REQUEST_CODE, INVALID_REQUEST_CODE) : INVALID_REQUEST_CODE; } @Override protected void onResume() { super.onResume(); // 当请求码为默认值时执行 if (mPendingRequestCode == INVALID_REQUEST_CODE) { Intent intent = getIntent(); Bundle extras = null; if (null != intent) { extras = getIntent().getExtras(); } if (null != extras) { final String[] permissionsToRequest = extras.getStringArray(EXTRA_PERMISSION_REQUESTED_PERMISSIONS); mPendingRequestCode = extras.getInt(EXTRA_PERMISSION_REQUEST_CODE); // 判断用户是否之前有拒绝权限申请且勾选了不再提醒选项 if (!PermissionsUtil.isShouldShowRequestPermissionRationale(this, permissionsToRequest) && !isHaveFirstRequestPermission(permissionsToRequest)) { showPermissionDialog(PermissionsActivity.this, mPendingRequestCode, permissionsToRequest); } else { requestPermissions(permissionsToRequest,mPendingRequestCode); } } } } /** * * 引导进入应用权限管理页面的dialog * * @param activity * @param permissionsToRequest */ private void showPermissionDialog(final Activity activity, final int mPendingRequestCode, String[] permissionsToRequest) { AlertDialog.Builder builder = new Builder(this); AlertDialog dialog = null; StringBuilder sb = new StringBuilder(getString(R.string.permissions_tips)); for (int i = 0; i < permissionsToRequest.length; i++) { sb.append("\n"); Integer key = PermissionsUtil.PERMISSION_TIPS.get(permissionsToRequest[i]); if (null != key) { String tip = getString(key); sb.append(tip); } } builder.setMessage(sb.toString()); builder.setTitle(getString(R.string.huaweipay_note)); builder.setPositiveButton(getString(R.string.go_settings), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 打开权限管理器的应用授权activity openManangePermissionUI(activity, mPendingRequestCode); dialog.dismiss(); } }); builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); // 直接回调结果给Permissionsmanager,String[0],int[0]表示本次对于权限无任务修改 onRequestPermissionsResult(mPendingRequestCode, new String[0], new int[0]); } }); dialog = builder.create(); dialog.setOnKeyListener(new DialogInterface.OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { dialog.dismiss(); // 直接回调结果给Permissionsmanager,String[0],int[0]表示本次对于权限无任务修改 onRequestPermissionsResult(mPendingRequestCode, new String[0], new int[0]); } return false; } }); dialog.setCanceledOnTouchOutside(false); dialog.show(); } /** * * 调起系统应用的权限管理页面,依赖与android.permission.GRANT_RUNTIME_PERMISSIONS权限( * 正式平台签名apk才会授予)若自身应用没有平台权限,建议引导用户跳转到setting页面即可 * */ public void openManangePermissionUI(Activity activity, int mPendingRequestCode) { if (null == activity) { LogC.w("openManangePermissionUI activity is null ", false); return; } try { Intent intent = new Intent("android.intent.action.MANAGE_APP_PERMISSIONS"); intent.putExtra("android.intent.extra.PACKAGE_NAME", activity.getPackageName()); // 屏蔽掉权限管理页面右上角的应用详情按钮 intent.putExtra("hideInfoButton", true); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 使用startActivityForResult,避免权限管理器操作未完成,而权限申请结果已回调 activity.startActivityForResult(intent, mPendingRequestCode); } catch (ActivityNotFoundException e) { LogC.w("start action android.intent.action.MANAGE_APP_PERMISSIONS ActivityNotFoundException error", false); onRequestPermissionsResult(mPendingRequestCode, new String[0], new int[0]); } catch (SecurityException ee) { LogC.w("start action android.intent.action.MANAGE_APP_PERMISSIONS java.lang.SecurityException==Permission Denial error", false); onRequestPermissionsResult(mPendingRequestCode, new String[0], new int[0]); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 回调权限管理器的处理结果 onRequestPermissionsResult(requestCode, new String[0], new int[0]); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { LogC.i(" permission activity onRequestPermissionsResult ", false); mPendingRequestCode = INVALID_REQUEST_CODE; setPermissionCache(permissions, true); finish(); PermissionsManager.getInstance().onRequestPermissionsResult(requestCode, permissions, grantResults); }
注:对于动态权限,建议应用可以区分一下哪些是必要权限和哪些是非必要权限,比如读写sd卡这个权限完全是非必要的,在Android4.4之后,访问自身应用在sd卡的私有路径不需要任何权限(manifest中都不要去声明),对于非必要权限,可以允许用户继续操作,必要权限被拒绝,可以直接退出,避免处理太多的异常场景。
相关文章推荐
- Android AutoCompleteTextView
- Android Studio中文件颜色的意义
- 【Android】注解机制详解
- Android广播机制
- Android 开源项目浅读-------SwipeMenuListView-重写-第一章-手指移动事件捕获
- Android之自定义标题--使用自定义Layout
- Android Studio报错,Error:SSL peer shut down incorrectly
- Android 开源项目浅读-------SwipeMenuListView
- android pd px sp 转换
- android一句话搞定图片加载
- android中xml tools:属性详解
- android中播放本地raw下的文件
- android基础学习之fragment+viewpager+fragment事件听
- Android程序报错,不能再console捕捉并定位到错误行,怎么办?
- 在源码环境中编译Android studio项目时的mk脚本编写
- Android——内存优化相关
- Android中跨进程通信方式之使用AIDL一些小细节
- Android Design Support Library--FloatingActionButton及其Behavior的使用
- Android studio安装配置常见问题及其解决方案
- 通过电脑键盘按键操控android设备