Android 6.0运行时权限动态申请处理
最近需要在项目中对android 6.0以上的手机做权限处理,所以抽了个时间,将大于或等于6.0以上的权限处理做一个总结!
android 在6.0以后,将权限分为两类,分别为Normal Pemission和Dangerous Permission,Normal Permission不需要用户在操作的时候进行授权,例如最常用的访问网络的权限;Dangerous Permission 则不同,其涉及到了用户的隐私,必须在用户操作的过程中,进行授权通过了,才可以进行某些预定的操作,例如获取手机联系人,若用户没有对此进行授权,则会导致程序无法获取到手机上的联系人。而当用户拒绝该权限之后,没有在设置中将其打开,则会一直处于权限拒绝状态,所以,我们需要对此进行处理。
下面,将通过一个打电话的例子来演示操作。
在6.0以下的手机中,我们可以直接通过Intent的方式来拨打电话,代码如下:
private void call(){
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + mPhone);
intent.setData(data);
startActivity(intent);
}
而在6.0以上的手机中,我们则需要通过一些步骤,确定当前程序是否拨打电话的权限,如果有权限,则可以和如上方式一样直接调用call()方法来打电话,但是若检测到当前程序没有拨打电话的权限。那么,我们需要通过如下操作来进行处理:
1:申请用户授权拨打电话的权限
2:若授予了该权限,则调用call()方法进行拨打电话
3:若用户拒绝了,则我们需要进行一些其他的操作,来告诉用户,该功能不可用。
下面,我将具体的完整流程整理了下(画图技术太烂,不要在意,哈哈,希望大家都能看明白我画的是啥玩意儿 ^_^ ):
————————————-一条有逼格的分界线,下面开始撸码—————————————–
首先,先完成功能上的操作,具体代码如下,代码中,打了很多的Log,也写了很多注释,这样能让大家看的更清楚,明白!
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView(){
call = (TextView) findViewById(R.id.call);
call.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//检查权限
checkPermission();
}
});
}
private void checkPermission(){
//1:判断当前手机系统是否大于或等于6.0
//2:若大于6.0,则检查是否由打电话的权限
//3:若没有打电话的权限 则申请打电话的权限
//4:若有打电话的权限 则直接打电话
//5:若当前小于6.0,则直接打电话
if(Build.VERSION.SDK_INT >= M ){
Log.e(TAG,"版本大于等于6.0");
//checkSelfPermission方法用于检测程序是否含有某项权限
//参数一:当前的上下文对象
//参数二:需要检测的权限
/**
* checkSelfPermission方法用于检测程序是否含有某项权限
* 参数一:当前的上下文对象
* 参数二:需要检测的权限
*
* 该方法得返回值为int类型,有两种值,分别为:
* 1:PackageManager.PERMISSION_DENIED 该权限被拒绝
* 2:PackageManager.PERMISSION_GRANTED 该权限被授予
*/
int isPermission = ContextCompat.checkSelfPermission(this, android.Manifest.permission.CALL_PHONE);
if(isPermission == PackageManager.PERMISSION_GRANTED){
//该权限已被授予
Log.e(TAG,"该权限已被授予");
call();
}else{
Log.e(TAG,"该权限已被拒绝");
//申请权限
/**
* ActivityCompat.requestPermissions()方法用于申请权限
* 参数一:Activity
* 参数二:需要申请的权限,其为一个数组,可同时传入多个将要申请的权限
* 参数三:请求码,用于在请求完成之后的回调中使用
*/
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CALL_PHONE},REQUEST_CODE);
}
}else {
call();
}
}
private void call(){
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + mPhone);
intent.setData(data);
startActivity(intent);
}
/**
* 该方法即为申请权限的回调方法 无论申请成功或者失败 都会回调这个函数
* @param requestCode 上文中提到的请求码
* @param permissions 申请的权限
* @param grantResults 申请的结果
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == REQUEST_CODE){
if(grantResults != null && grantResults.length>0){
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
Log.e(TAG,"权限已被授予,此时可以拨打电话了");
call();
}else{
Toast.makeText(this,"您拒绝了拨打电话的权限!",Toast.LENGTH_SHORT).show();
}
}
}
}
到此 动态权限的功能也就处理完毕了!
你以为我会说结束了么,哈哈哈,想多了吧,老铁!^_^
不知道读到这里,大家有没有一个问题:难道我一涉及到危险权限的操作,就要写这么多代码来处理吗?这样会不会很丧心病狂啊!
下面,高大上的东西来了:封装一个高大上的权限申请框架!
写博客之前,也看了好些技术求人的方案,最终选择的是一个注解 + 反射的方案来处理
下面直接贴代码,注释已经很详细了,相信大家能看明白!
PermissionHelper:权限问题集中在此处理
package com.justh.dell.util; import android.app.Activity; import android.app.Fragment; import android.support.v4.app.ActivityCompat; import java.util.ArrayList; import static com.justh.dell.util.PermissionUtils.getDeniedPermissions; /** * Created by DELL on 2017/7/29. * 权限处理类 */ public class PermissionHelper { /** * 需要的参数: * 1:申请全的的Activity 或 fragment * 2:需要申请的权限 * 3:申请权限的请求码 */ private Object mObject; private String[] permissions; private int requestCode; private PermissionHelper(Object mObject){ this.mObject = mObject; } //第一种使用方式,直接传入参数 public static PermissionHelper requestPermission(Activity activity,String[] permissions,int requestCode){ return PermissionHelper.with(activity).requestCode(requestCode).requestPermission(permissions); } public static PermissionHelper requestPermission(Fragment fragment,String[] permissions,int requestCode){ return PermissionHelper.with(fragment).requestCode(requestCode).requestPermission(permissions); } //第二种方式:链式调用 public static PermissionHelper with(Activity activity){ return new PermissionHelper(activity); } public static PermissionHelper with(Fragment fragment){ return new PermissionHelper(fragment); } public PermissionHelper requestCode(int requestCode){ this.requestCode = requestCode; return this; } public PermissionHelper requestPermission(String... permissions){ this.permissions = permissions; return this; } //发起操作 public void request(){ /** * 1:判断当前手机系统是否大于或等于6.0 */ if(!PermissionUtils.isMoreThanM()){ /** * 当前系统小于6.0,则直接执行需要执行的方法,如拨号 * 由于需要执行的方法是不确定的 但Activity或fragment是已知的, * 所以这里使用注解 + 反射 的方式来获取到需要执行的方法并执行操作 */ PermissionUtils.executeSuccesseMethod(mObject,requestCode); }else{ /** * 当前系统大于 或等于6.0 * 1:判断这些权限中,是否有被被拒绝的权限 * 2:若有,则向用户申请权限 * 2.1:若申请成功 则执行成功的方法 * 2.2:若申请失败 则执行一些提示操作 * 3:若无,则直接执行成功的方法 */ ArrayList<String> deniedPermissions = getDeniedPermissions(mObject,permissions); if (deniedPermissions.size() > 0){ //有被用户拒绝的权限 需要向用户提交申请 ActivityCompat.requestPermissions(PermissionUtils.getActivity(mObject), deniedPermissions.toArray(new String[deniedPermissions.size()]),requestCode); }else{ //无被拒绝的权限 PermissionUtils.executeSuccesseMethod(mObject,requestCode); } } } public static void requestPermissionsResult(int requestCode, String[] permissions,Object object) { //再去获取以下 看看是否还有被用户拒绝的权限 ArrayList<String> deniedPermission = PermissionUtils.getDeniedPermissions(object,permissions); if(deniedPermission.size() == 0){ //用户已授权所有权限 PermissionUtils.executeSuccesseMethod(object,requestCode); }else{ /** * 还是有部分权限被用户拒绝了 * 执行一个类似提示或捕获异常等的方法 * 视情况而定 * * 这里弹出一个Toast * 套路一样 还是使用 注解 + 反射的方法 */ PermissionUtils.executeFailedMethod(object,requestCode); } } }
2.PermissionUtils: PermissionUtils的辅助类
package com.justh.dell.util;
import android.app.Activity;
import android.app.Fragment;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* Created by DELL on 2017/7/29.
*/
public class PermissionUtils {
private static final String TAG = PermissionUtils.class.getSimpleName();
/**
*
* @return true or false
* 如果大于或等于 返回true
* 否则 返回 false
*/
public static boolean isMoreThanM(){
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
/**
* 执行方法
* @param object
* @param requestCode
*/
public static void executeSuccesseMethod(Object object, int requestCode) {
Class clz = object.getClass();
//通过反射获取到这个类的全部方法
Method[] methods = clz.getDeclaredMethods();
//遍历所有方法,寻找被我们加了注解的方法
for (Method method : methods){
Log.i(TAG,method+"");
PermissionSucceed correctMethod = method.getAnnotation(PermissionSucceed.class);
//从注解中获取到requestCode 比较是否与参数中的request一致,若一致,则可确定就是该方法
if(correctMethod != null){
if(correctMethod.requestCode() == requestCode){
//执行该方法
executeMethod(object,method);
}
}
}
}
//执行方法
public static void executeMethod(Object object,Method method){
try {
/**
* 代表允许执行私有方法
* 若不加这句代码
* 在执行私有方法时,是会出问题滴
*/
method.setAccessible(true);
method.invoke(object,new Object[]{});
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static ArrayList<String> getDeniedPermissions(Object object, String[] permissions) {
//遍历检测所有权限 将未授权的权限添加到集合中去 并返回
ArrayList<String> deniedPermissions = new ArrayList<>();
for (String permission : permissions){
if(ContextCompat.checkSelfPermission(getActivity(object),permission) != PackageManager.PERMISSION_GRANTED){
//即为被拒绝的权限 将其添加到集合中 后面统一处理
deniedPermissions.add(permission);
}
}
return deniedPermissions;
}
/**
*
* @param object activity or fragment
* @return Activity
*/
public static Activity getActivity(Object object){
if(object instanceof Activity){
return (Activity) object;
}else if(object instanceof Fragment){
return ((Fragment) object).getActivity();
}
return null;
}
public static void executeFailedMethod(Object object, int requestCode) {
Class clz = object.getClass();
Method[] methods = clz.getDeclaredMethods();
for (Method method : methods){
PermissionFailed permissionFailed = method.getAnnotation(PermissionFailed.class);
if(permissionFailed != null){
if(permissionFailed.requestCode() == requestCode){
executeMethod(object,method);
}
}
}
}
}
3.PermissionSucceed :成功时的注解(相当于TAG的作用)在含有全部权限授权的情况下使用,作用在执行成功的方法的头部
package com.justh.dell.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by DELL on 2017/7/29.
* 执行正确的操作
*/
@Target(ElementType.METHOD)//代表其作用于方法中
@Retention(RetentionPolicy.RUNTIME) //表示在运行时起作用
public @interface PermissionSucceed {
public int requestCode();
}
4.PermissionFailed:失败时的注解 在程序含有未被用户授权的危险权限时使用,作用在执行失败的方法的头部
package com.justh.dell.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by DELL on 2017/7/29.
* 执行某些提示
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionFailed {
public int requestCode();
}
5:在Activity中调用:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView(){
call = (TextView) findViewById(R.id.call);
call.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//检查权限
checkPermission();
}
});
}
private void checkPermission(){
//发起请求
PermissionHelper.with(this).requestCode(REQUEST_CODE).requestPermission(Manifest.permission.CALL_PHONE).request();
}
@PermissionSucceed(requestCode = REQUEST_CODE)
private void call(){
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + mPhone);
intent.setData(data);
startActivity(intent);
}
@PermissionFailed(requestCode = REQUEST_CODE)
private void showToast(){
Toast.makeText(this,"您拒绝了拨打电话的权限!",Toast.LENGTH_SHORT).show();
}
/**
* 该方法即为申请权限的回调方法 无论申请成功或者失败 都会回调这个函数
* @param requestCode 上文中提到的请求码
* @param permissions 申请的权限
* @param grantResults 申请的结果
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionHelper.requestPermissionsResult(requestCode, permissions, this);
}
到此 就真的结束了 !咵咵咵写了这么多,感觉都不知道到写了啥,希望大家能看懂吧!
如若碰到什么问题或疑问的,可以通过评论进行交流,谢谢!