android从MM开始,添加了动态权限机制。
针对一些特别的敏感权限,要求程序主动向用户申请,用户同意之后才可以使用相关权限。
个人觉得处理添加动态权限最方便的是写一个基类(类似于PermissionBaseActivity,PermissionBaseFragment),然后让需要申请权限的界面(某个Activity或者Fragment)继承于对应的基类。
因为在基类中已经写好了申请权限的基本方法,所以继承自基类的子类,可以方便的处理权限相关的逻辑。
以Activity为例,新建基类PermissonBaseActivity继承于AppCompatActivity
public abstract class PermissionBaseActivity extends AppCompatActivity {
private Set mPermsAll = new HashSet();
private Set mPermsNeedful = new HashSet();
protected String TAG;
protected Context mContext;
protected static final int PERMISSION_REQUEST_CODE = 110;
protected FragmentManager mFragmentManager;
private int mTransactionIndex = 1;
private int getTransactionId() {
return mTransactionIndex++;
}
private void resetTransactionId(@IntRange(from = 0) int v) {
mTransactionIndex = v;
}
public interface IPermissionCallback {
void onPermissionResult(@NonNull String perm, int result);
}
private static final String KEY_PERMISSION = "permission";
private final SparseArray> mPermissionMapHandler = new SparseArray<>();
private IPermissionCallback pushPermissionsHolderData(int key, String[] perms, @NonNull IPermissionCallback permissionCallback) {
HashMap permKey = mPermissionMapHandler.get(key);
if (permKey == null) {
permKey = new HashMap<>();
}
permKey.clear();
for (String perm: perms) {
permKey.put(perm, permissionCallback);
}
mPermissionMapHandler.put(key, permKey);
return permissionCallback;
}
private void addPermissionsHolderGuardKey(int key, @NonNull IPermissionCallback permissionCallback) {
HashMap permKey = mPermissionMapHandler.get(key);
if (permKey == null) {
permKey = new HashMap<>();
}
permKey.put(KEY_PERMISSION, permissionCallback);
mPermissionMapHandler.put(key, permKey);
}
private IPermissionCallback removePermissionHolder(int key, @NonNull String perm) {
HashMap permCallback = mPermissionMapHandler.get(key);
if (permCallback != null) {
return permCallback.get(perm);
}
return null;
}
private IPermissionCallback getPermissionCallback(int key) {
HashMap permissionMap = mPermissionMapHandler.get(key);
for (IPermissionCallback callback: permissionMap.values()) {
if (callback != null) {
return callback;
}
}
return null;
}
private void iteratePermissionHolder(int key) {
HashMap permsMap = mPermissionMapHandler.get(key);
if (permsMap == null) {
return;
}
Iterator> it = permsMap.entrySet().iterator();
Map.Entry entry;
while (it.hasNext()) {
entry = it.next();
if (!entry.getKey().equals(KEY_PERMISSION)) {
entry.getValue().onPermissionResult(entry.getKey(), PackageManager.PERMISSION_GRANTED);
}
}
mPermissionMapHandler.remove(key);
}
@CallSuper
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getApplicationContext();
TAG = this.getClass().getSimpleName();
mFragmentManager = getFragmentManager();
}
protected void pushPermsData(String... perms) {
//略
}
protected void updatePermInfo() {
//略
}
protected boolean checkPermission(String perm) {
if (Build.VERSION.SDK_INT >= M) {
return ActivityCompat.checkSelfPermission(mContext, perm) == PackageManager.PERMISSION_GRANTED;
}
return true;
}
protected boolean checkPermState() {
updatePermInfo();
//略
}
protected ArrayList updatePermissions(String[] perms, ArrayList granted) {
if (perms == null) {
return null;
}
ArrayList resultPerms = new ArrayList<>();
for (String perm : perms) {
if (ActivityCompat.checkSelfPermission(mContext, perm) == PackageManager.PERMISSION_GRANTED) {
granted.add(perm);
} else {
resultPerms.add(perm);
}
}
return resultPerms;
}
private void requestPermissionManage(@NonNull String[] perms, @NonNull IPermissionCallback permissionCallback) {
int key = getTransactionId();
if (key != -1 && (key & 0xffff0000) != 0) {
key = 1;
resetTransactionId(1);
}
if (perms.length == 1) {
//如果只申请一个权限,调用这里
requestSingleCompatPermission(key, perms[0], permissionCallback);
} else {
//申请多个权限,调用这里
requestMultiCompatPermissions(key, perms, permissionCallback);
}
}
private void requestMultiCompatPermissions(@IntRange(from = 0) int key, @NonNull String[] perms, @NonNull IPermissionCallback permissionCallback) {
ArrayList granted = new ArrayList<>();
//申请多个权限的时候,需要过滤权限,筛选出已经获得过的权限
ArrayList needRequest = updatePermissions(perms, granted);
if (granted.size() > 0) {
if (needRequest.size() > 0) {
String[] grantedArray = new String[granted.size()];
granted.toArray(grantedArray);
//这个稍后补充说明
pushPermissionsHolderData(key, grantedArray, permissionCallback);
} else {
//如果想申请的所有权限都已经被授予过了,那么直接回调返回
for (String perm: granted) {
permissionCallback.onPermissionResult(perm, PackageManager.PERMISSION_GRANTED);
}
return;
}
}
//TODO check ActivityCompat.shouldShowRequestPermissionRationale for every permission
if (needRequest.size() > 0) {
String[] permsArray = new String[needRequest.size()];
needRequest.toArray(permsArray);
addPermissionsHolderGuardKey(key, permissionCallback);
//开始申请那些还未获得的权限
ActivityCompat.requestPermissions(this, permsArray, key);
}
}
private void requestSingleCompatPermission(@IntRange(from = 0) int key, @NonNull String perm, @NonNull IPermissionCallback permissionCallback) {
if (ActivityCompat.checkSelfPermission(mContext, perm) == PackageManager.PERMISSION_GRANTED) {
//如果要申请的权限已经被授予,直接回调返回
permissionCallback.onPermissionResult(perm, PackageManager.PERMISSION_GRANTED);
} else {
//以前申请权限时,用户是否勾选过不再提示的复选框
boolean should = ActivityCompat.shouldShowRequestPermissionRationale(this, perm);
if (!should) {//如果勾选过,那么直接跳到APP详细信息页面
jumpToAppDetailsDialog();
} else {//开始申请权限
addPermissionsHolderGuardKey(key, permissionCallback);
ActivityCompat.requestPermissions(this, new String[]{perm}, key);
}
}
}
//申请权限从这里开始,String... perms就是需要申请的权限,permissionCallback是申请成功或者失败后的回调
protected void requestPermissions(IPermissionCallback permissionCallback, String... perms) {
if (perms == null) {
return;
}
if (permissionCallback == null) {
return;
}
if (Build.VERSION.SDK_INT >= M) {
//如果是MM平台以上,跳转去处理申请权限
requestPermissionManage(perms, permissionCallback);
} else {
//如果MM平台之前的,直接处理回调
for (String perm: perms) {
permissionCallback.onPermissionResult(perm, PackageManager.PERMISSION_GRANTED);
}
}
}
protected void requestPermissions(IPermissionCallback permissionCallback) {
//略
}
protected void requestPermissions() {
//略
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
updatePermInfo();
permissionResultHandler(requestCode, permissions, grantResults);
}
private void permissionResultHandler(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (mPermissionMapHandler.size() > 0) {
//这里主要是回调而已
IPermissionCallback callback = getPermissionCallback(requestCode);
iteratePermissionHolder(requestCode);
for (int i = 0; i < permissions.length; i++) {
if (callback != null) {
callback.onPermissionResult(permissions[i], grantResults[i]);
}
}
}
}
protected void jumpToAppSettings() {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
private void jumpToAppDetailsDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle("Title")
.setMessage("Message")
.setPositiveButton("Okay", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
jumpToAppSettings();
}
})
.setNegativeButton("Exit", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create();
builder.show();
}
}
解释下pushPermissionsHolderData(key, grantedArray, permissionCallback)
在开头的时候,定义了一个SparseArray:
private final SparseArray
IPermissionCallback pushPermissionsHolderData(int key, String[] perms,IPermissionCallback permissionCallback) { HashMap
permKey = mPermissionMapHandler.get(key); if (permKey == null) { permKey = new HashMap<>(); } permKey.clear(); for (String perm: perms) {//以权限和回调接口建立键值对 permKey.put(perm, permissionCallback); } mPermissionMapHandler.put(key, permKey); return permissionCallback; }
每次申请权限,都为这一次权限申请事件赋予一个独立的事务id : mTransactionIndex ,.
private int mTransactionIndex = 1; private int getTransactionId() { return mTransactionIndex++; }
当申请权限时,以这个自定义的mTransactionIndex 做为申请权限传入的requestCode:
int key = getTransactionId(); ActivityCompat.requestPermissions(this, permsArray, key);
这样在onRequestPermissionsResult方法中,就可以通过
HashMap
permissionMap = mPermissionMapHandler.get(key);
获取到正确的权限事件映射。
在方法onRequestPermissionsResult中,获取到requestCode对应的HashMap键值对之后,就可以遍历想申请的权限,然后回调申请的结果:
private void permissionResultHandler(int requestCode, String[] permissions, @NonNull int[] grantResults) { if (mPermissionMapHandler.size() > 0) { IPermissionCallback callback = getPermissionCallback(requestCode); iteratePermissionHolder(requestCode); for (int i = 0; i < permissions.length; i++) { if (callback != null) { callback.onPermissionResult(permissions[i], grantResults[i]); } } } }
大体流程就是这样,具体的使用方法如下:
requestPermissions(new IPermissionCallback() { @Override public void onPermissionResult(@NonNull String perm, int result) { Log.e(TAG, "onPermissionResult2 " + perm + " " + result); } }, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_CONTACTS, Manifest.permission.CAMERA );
不管用户最后是同意还是拒绝权限,最后都回调到 onPermissionResult(@NonNull String perm, int result) 方法。perm表示权限的名字,result是这个perm权限申请的结果,是同意(0)还是拒绝(-1)。
我使用动态权限的想法是,比如在需要录音权限的地方,这样使用
requestPermissions(new IPermissionCallback() { @Override public void onPermissionResult(@NonNull String perm, int result) { if (result == PackageManager.PERMISSION_GRANTED) { //开始录音 } else { //无法录音,添加用户提示 } } }, Manifest.permission.RECORD_AUDIO);
这是个人偏好的用法,分享出来。