AOP方案实现android动态获取权限

Tags: java

Android 6.0版本(Api 23)推出了很多新的特性, 大幅提升了用户体验, 同时也为程序员带来新的负担. 动态权限管理就是这样, 如果你不申请就使用,那么有一个崩溃在后面等待着你。

这个权限的申请确实比较DT,今天恰巧看了下关于AOP方面的书,(如果你还不明白AOP是什么,而且如果你是android程序员的话,我推荐你看这篇文章 http://www.devtf.cn/?p=981

如果,我在执行某个方法的时候,需要某个危险权限,比如

<!--危险权限--> <uses-permission android:name="android.permission.RECORD_AUDIO"/>

虽然你在AndroidManifest中已经申明,但是如果是6.0系统,你同样是需要申请的,比如ActivityCompat.requestPermissions(.......),具体的代码我就不帖了。。。

如果我在这个方法执行之前@before,能够判断下这个权限有没有,没有自动申请一下,那该多好啊,诶,真不巧,AOP,就特别擅长干这种事情。

如是,说干就干咯,定义PermissionCheck注解:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PermissionCheck {
    public String[] permession();
}

紧接着,在定义 Aspect

/**
 * Created by brzhang on 16/7/20.
 * Description :
 */

@Aspect
public class PermissionAspect {

    private static final int MY_PERMISSIONS_REQUEST_PERMISSION = 101;

    //private static final String POINTCUT_METHOD_PERMISS_CHECK = "execution(@test.tencent.com.aop.anonation.PermissionCheck * *TakePhoto(..))";//所有方法已TakePhoto结尾的方法  ,不带参数的写发
    private static final String POINTCUT_METHOD_PERMISS_CHECK = "execution(@test.tencent.com.aop.anonation.PermissionCheck * *TakePhoto(..)) && @annotation(permissionCheck)";//所有方法已TakePhoto结尾的方法,这里的@annotation(permissionCheck) ,里面一定是首字母小写,否则失效


    @Pointcut(POINTCUT_METHOD_PERMISS_CHECK)
    public void methodAnnotatedWithtakePhoto(PermissionCheck permissionCheck) {
        //Log.e("PermissionCheck", "methodAnnotatedWithtakePhoto");
    }

    //PermissionCheck 参数一个切面函数有的话,所有的Before,after都要有,

    @Around("methodAnnotatedWithtakePhoto(permissionCheck)")
    public Object around(ProceedingJoinPoint joinPoint, PermissionCheck permissionCheck) throws Throwable {
        Object result = null;
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String className = methodSignature.getDeclaringType().getSimpleName();
        String methodName = methodSignature.getName();
        if (permissionCheck.permession() != null && permissionCheck.permession().length != 0) {
            for (int i = 0; i < permissionCheck.permession().length; i++) {
                if (ContextCompat.checkSelfPermission((Context) joinPoint.getTarget(),
                        permissionCheck.permession()[i])
                        != PackageManager.PERMISSION_GRANTED) {
                    DebugLog.log(className, className + ":" + methodName + "没有" + permissionCheck.permession()[i] + "的权限执行");
                }
            }
        }
        result = joinPoint.proceed();
        return result;
    }

    @Before("methodAnnotatedWithtakePhoto(permissionCheck)")
    public void before(JoinPoint joinPoint, PermissionCheck permissionCheck) throws Throwable {

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String className = methodSignature.getDeclaringType().getSimpleName();
        String methodName = methodSignature.getName();

        DebugLog.log(className, className + ":" + methodName + "on before");

        if (permissionCheck.permession() == null || permissionCheck.permession().length == 0) {
            return;
        } else {
            boolean needRequestPermissions = false;
            List<String> stringList = new ArrayList<>(Arrays.asList(permissionCheck.permession()));
            for (int i = 0; i < permissionCheck.permession().length; i++) {
                DebugLog.log(className, "检查" + permissionCheck.permession()[i] + "权限,in before ");
                if (joinPoint.getTarget() instanceof FragmentActivity) {
                    if (ContextCompat.checkSelfPermission((Context) joinPoint.getTarget(),
                            permissionCheck.permession()[i])
                            != PackageManager.PERMISSION_GRANTED) {
                        DebugLog.log(className, "没有" + permissionCheck.permession()[i] + "权限,正在申请权限 in before");
                        needRequestPermissions = true;
                    } else {
                        DebugLog.log(className, "已经有" + permissionCheck.permession()[i] + "权限, in before");
                        stringList.remove(permissionCheck.permession()[i]);//不用申请这个权限,移除掉
                    }
                }
            }
            if (needRequestPermissions) {
                String[] needToRequestPermission = new String[stringList.size()];
                ActivityCompat.requestPermissions((Activity) joinPoint.getTarget(),
                        stringList.toArray(needToRequestPermission),
                        MY_PERMISSIONS_REQUEST_PERMISSION);
            }
        }
    }

    @After("methodAnnotatedWithtakePhoto(permissionCheck)")
    public void after(JoinPoint joinPoint, PermissionCheck permissionCheck) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String className = methodSignature.getDeclaringType().getSimpleName();
        String methodName = methodSignature.getName();
        DebugLog.log(className, className + ":" + methodName + "on after");
    }

}

这里有几点需要注意的地方

1、这里的RetentionPolicy要填RUNTIME,不然,你和面会遇到一个这样的错误:

 Annotation type xxx.xxx.com.aop.anonation.PermissionCheck does not have runtime retention

2、@annotation(permissionCheck),里面一定是PermissionCheck首字母小写,否则失效。

3、@before ,@after的参数需要一致

4、最后一点比较重要的是你一定要对比下我的工程中app的build.gradle  和 aop工程的build.gradle

如果,你读到这里,恭喜你,已经完成了,下面就是看看如何使用了。


takePhoto

没错,就是这么简单了,这个方法一旦调用,就会自动检测是否需要你上面的权限,并且弹框出来让你确认。


弹一个.png


接着弹第二个.png

控制台也是可以看到我们权限检测已经自动申请的过程,这些都在真正执行你要执行的方法之前发生。


Paste_Image.png

遗留问题,如果权限那里,你点了禁止,实际上你要执行的方法会继续执行下去,如果你没有进行错误捕捉,很不幸,你的app依然会崩溃,那么怎么办?

可以在before中抛出错误,终止joinPoint.proceed();从而终止方法执行?但是你什么时机抛出错误呢?

public interface OnRequestPermissionsResultCallback {        /**
         * Callback for the result from requesting permissions. This method
         * is invoked for every call on {@link #requestPermissions(android.app.Activity,
         * String[], int)}.
         * <p>
         * <strong>Note:</strong> It is possible that the permissions request interaction
         * with the user is interrupted. In this case you will receive empty permissions
         * and results arrays which should be treated as a cancellation.
         * </p>
         *
         * @param requestCode The request code passed in {@link #requestPermissions(
         * android.app.Activity, String[], int)}
         * @param permissions The requested permissions. Never null.
         * @param grantResults The grant results for the corresponding permissions
         *     which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
         *     or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
         *
         * @see #requestPermissions(android.app.Activity, String[], int)
         */
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                @NonNull int[] grantResults);
    }

aspect中去实现这么一个接口吗?然后判断用户点了禁止之后,在@before就抛出异常,终止方法继续执行吗?中这个有意思的事情当然就交给读者你了。

写在最后

本文源码放在:https://github.com/bravekingzhang/Analyser



文/brzhang(简书作者)
原文链接:http://www.jianshu.com/p/ea4cc77bf984
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

本文链接:http://www.4byte.cn/learning/120088/aop-fang-an-shi-xian-android-dong-tai-huo-qu-quan-xian.html