前言
android6.0开始,权限的申请发生了改变,申请变的动态化,也就是运行时权限,和iOS相仿,动态化的意思是指,在每次使用需要危险权限的方法的时候,需要检查程序是否获得了该权限的许可。动态化的权限申请能够让用户更加清晰的知道程序需要什么权限,以及程序中哪些地方的操作需要涉及用户安全。不再是仅仅在程序安装的时候,一次性把所需要的普通的、危险级别的权限一次性列出来,然后展示给用户。
当工程项目的target Sdk为23时,由于考虑到用户如果没有进行权限的动态检查,那么在运行到需要权限的代码时,会发生crash,而当你的target Sdk为23以下时,则不强制要求权限的动态监测,此时app不会crash,还是可以正常的使用功能。
Google官网上动态申请权限的方法分了在Activity和Fragment这两种,通过ContextCompat以及子类,ActivityCompat和FragmentCompat去进行权限的申请和权限的检查,而申请的方式是弹出一个系统的不可以改写的对话框,结果是通过Activity和Fragment的onRequestPermissionResult()方法进行返回。
具体可以参考官网
但是这种代码会促使以前的工程进行大改造或者说代码的逻辑会耦合的写在同一个方法里,显的不方便和臃肿。所以以下的EasyPermissionUtil就是简化权限请求的方式,同时可以使代码的逻辑更加清晰。
EasyPermissionUtil:简化权限请求
由于权限的请求和结果的返回需要分开Activity和Fragment两种去进行操作,这样会比较麻烦,所以EasyPermissionUtil中投机取巧,通过开启一个新的activity进行权限申请和检查的操作,这样不用去区分多种情况,同时也能够把所有的申请过程和结果统一由EasyPermissionUtil进行处理。
接下来看一下整体的思想:
使用的方法
PermissionUtil.getInstance().request(MainActivity.this, new String[]{Manifest.permission.READ_CALENDAR}, mRequestCode,
new PermissionResultCallBack() {
@Override
public void onPermissionGranted() {
// 当所有权限的申请被用户同意之后,该方法会被调用
}
@Override
public void onPermissionDenied(String... permissions) {
// 当权限申请中的某一个或多个权限,被用户曾经否定了,并确认了不再提醒时,也就是权限的申请窗口不能再弹出时,该方法将会被调用
}
@Override
public void onRationalShow(String... permissions) {
// 当权限申请中的某一个或多个权限,被用户否定了,但没有确认不再提醒时,也就是权限窗口申请时,但被否定了之后,该方法将会被调用.
}
});
项目源码
在PermissionUtil中,要做的是:
1.进行权限检查
2.没有得到权限许可的进行权限申请
3.返回权限申请的结果
public class PermissionUtil {
private PermissionResultCallBack mPermissionResultCallBack;
private volatile static PermissionUtil instance;
private int mRequestCode;
private Context mContext;
private Fragment mFragment;
private List<PermissionInfo> mPermissionListNeedReq;
private String[] mPermissions;
public static PermissionUtil getInstance() {
if (instance == null) {
synchronized (PermissionUtil.class) {
if (instance == null) {
instance = new PermissionUtil();
}
}
}
return instance;
}
/**
* 用于fragment中请求权限
* @param fragment
* @param permissions
* @param requestCode
* @param callBack
*/
public void request(@NonNull Fragment fragment,@NonNull String[] permissions,@NonNull int requestCode, PermissionResultCallBack callBack) {
this.mFragment = fragment;
this.request(fragment.getActivity(), permissions, requestCode, callBack);
}
/**
* 用于activity中请求权限
* @param context
* @param permissions
* @param requestCode
* @param callBack
*/
public void request(@NonNull Context context,@NonNull String[] permissions,@NonNull int requestCode, PermissionResultCallBack callBack) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new RuntimeException("request permission only can run in MainThread!");
}
if (permissions.length == 0) {
return;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
onGranted();
return;
}
this.mContext = context;
this.mPermissions = permissions;
this.mRequestCode = requestCode;
this.mPermissionResultCallBack = callBack;
this.mPermissionListNeedReq = new ArrayList<PermissionInfo>();
if (needToRequest()) {
requestPermissions();
} else {
onGranted();
}
}
/**
* 通过开启一个新的activity作为申请权限的媒介
*/
private void requestPermissions() {
Intent intent = new Intent(mContext, HelpActivity.class);
intent.putExtra("permissions", mPermissions);
intent.putExtra("requestCode", mRequestCode);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
/**
* 检查是否需要申请权限
* @return
*/
private boolean needToRequest() {
for (String permission : mPermissions) {
int checkRes = ContextCompat.checkSelfPermission(mContext, permission);
if (checkRes != PackageManager.PERMISSION_GRANTED) {
PermissionInfo info = new PermissionInfo(permission);
if (mContext instanceof Activity &&
ActivityCompat.shouldShowRequestPermissionRationale((Activity) mContext, permission)) {
info.setRationalNeed(true);
}
mPermissionListNeedReq.add(info);
}
}
if (mPermissionListNeedReq.size() > 0) {
mPermissions = new String[mPermissionListNeedReq.size()];
for (int i = 0; i < mPermissionListNeedReq.size(); i++) {
mPermissions[i] = mPermissionListNeedReq.get(i).getName();
}
return true;
}
return false;
}
/**
* 申请权限结果返回
* @param requestCode
* @param permissions
* @param grantResults
*/
@TargetApi(Build.VERSION_CODES.M)
protected void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == mRequestCode) {
if (mContext != null && mContext instanceof Activity) {
((Activity) mContext).onRequestPermissionsResult(requestCode, permissions, grantResults);
}
if (mFragment != null) {
mFragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
boolean isAllGranted = true;
List<PermissionInfo> needRationalPermissionList = new ArrayList<PermissionInfo>();
List<PermissionInfo> deniedPermissionList = new ArrayList<PermissionInfo>();
for (int i = 0; i < permissions.length; i++) {
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
if (mPermissionListNeedReq.get(i).isRationalNeed()) {
needRationalPermissionList.add(mPermissionListNeedReq.get(i));
} else {
deniedPermissionList.add(mPermissionListNeedReq.get(i));
}
isAllGranted = false;
}
}
if (needRationalPermissionList.size() != 0) {
showRational(needRationalPermissionList);
}
if (deniedPermissionList.size() != 0) {
onDenied(deniedPermissionList);
}
if (isAllGranted) {
onGranted();
}
}
}
/**
* 权限被用户许可之后回调的方法
*/
private void onGranted() {
if (mPermissionResultCallBack != null) {
mPermissionResultCallBack.onPermissionGranted();
}
}
/**
* 权限申请被用户否定之后的回调方法,这个主要是当用户点击否定的同时点击了不在弹出,
* 那么当再次申请权限,此方法会被调用
* @param list
*/
private void onDenied(List<PermissionInfo> list) {
if(list == null || list.size() == 0) return;
String[] permissions = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
permissions[i] = list.get(i).getName();
}
if (mPermissionResultCallBack != null) {
mPermissionResultCallBack.onPermissionDenied(permissions);
}
}
/**
* 权限申请被用户否定后的回调方法,这个主要场景是当用户点击了否定,但未点击不在弹出,
* 那么当再次申请权限的时候,此方法会被调用
* @param list
*/
private void showRational(List<PermissionInfo> list) {
if(list == null || list.size() == 0) return;
String[] permissions = new String[list.size()];
for (int i = 0; i < list.size(); i++) {
permissions[i] = list.get(i).getName();
}
if (mPermissionResultCallBack != null) {
mPermissionResultCallBack.onRationalShow(permissions);
}
}
}
在PermissionResutCallBack中,要做的是:
1.返回对应的结果
public interface PermissionResultCallBack {
/**
* 当所有权限的申请被用户同意之后,该方法会被调用
*/
void onPermissionGranted();
/**
* 当权限申请中的某一个或多个权限,被用户曾经否定了,并确认了不再提醒时,也就是权限的申请窗口不能再弹出时,
* 该方法将会被调用
* @param permissions
*/
void onPermissionDenied(String... permissions);
/**
* 当权限申请中的某一个或多个权限,被用户否定了,但没有确认不再提醒时,也就是权限窗口申请时,但被否定了之后,
* 该方法将会被调用.
* @param permissions
*/
void onRationalShow(String... permissions);
}
在HelpActivity中,要做的就是:
1.申请权限
2.通过onRequestPermissionUtil返回结果给PermissionUtil
当然这个activity必须是透明的,而且是没有任何的view的,这样看起来才不像是开了一个新的activity。
public class HelpActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
handleIntent(getIntent());
}
}
@Override
protected void onNewIntent(Intent intent) {
handleIntent(intent);
}
// 权限申请
@TargetApi(Build.VERSION_CODES.M)
private void handleIntent(Intent intent) {
String[] permissions = intent.getStringArrayExtra("permissions");
int requestCode = intent.getIntExtra("requestCode", 42);
ActivityCompat.requestPermissions(this, permissions, requestCode);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
// 返回结果
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
PermissionUtil.getInstance().onRequestPermissionResult(requestCode, permissions, grantResults);
finish();
}
}