在我上一篇文章中介绍了一下Android M版本与非M版本的动态权限适配的方案,只介绍了一个大体思路。
详细的可以看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中都不要去声明),对于非必要权限,可以允许用户继续操作,必要权限被拒绝,可以直接退出,避免处理太多的异常场景。