Android如何通过aspectj打造一个无侵入式动态权限申请框架

目录

一,背景

二,通过Aspectj管理所有的注解

三,配置注解

四,通过空白Activity完成真正的权限申请

五,引入依赖配置


一,背景

在Activity或者fragment中,写在几个方法写一些注释,用来表示权限申请成功申请失败多次拒绝。同时需要无侵入式,让业务开发者尽可能的少些代码,把核心的业务逻辑下沉到框架层

二,通过Aspectj管理所有的注解

它的作用就是劫持被注释的方法的执行。我在ASPECT中配置拦截@permission注释的方法。先做判断。如果没有了解过Aspect的话,AOP面向切面编程,大家应该听说过,它可以用来配置事务、做日志、权限验证、在用户请求时做一些处理等等。而用@Aspect做一个切面,就可以直接实现。

复制代码
package com.example.myapplication;

import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;

import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;

@Aspect
public class PermissionAspect {
    //com.example.myapplication

    private static final String POINTCUT_METHOD = "execution(@com.example.myapplication.Permission * *(..))";

    @Pointcut(POINTCUT_METHOD)
    public void methodAnnotatedWithPermission() {
    }

    @Around("methodAnnotatedWithPermission()")
    public Object permissionMethod(final ProceedingJoinPoint joinPoint) throws Throwable {

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        Permission permission = method.getAnnotation(Permission.class);

        String[] permissions = permission.value();
        int requestCode = permission.requestCode();

        Object object = joinPoint.getThis();

        Context context = null;

        if (object instanceof Activity) {
            context = (Activity) object;
        } else if (object instanceof FragmentActivity) {
            context = (FragmentActivity) object;
        } else if (object instanceof Fragment) {
            context = ((Fragment) object).getContext();
        } else if (object instanceof Service) {
            context = (Service) object;
        }

        Object o = null;

        if (checkPermissions(context, permissions)) {
            o = joinPoint.proceed();
        } else {

            Intent intent = new Intent();
            intent.setClass(context, PermissionActivity.class);
            intent.putExtra("permissions", permissions);
            intent.putExtra("requestcode", requestCode);
            context.startActivity(intent);
        }

        return o;
    }

    private boolean checkPermission(Context context, String permission) {

        if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) {
            return true;
        }
        return false;
    }

    private boolean checkPermissions(Context context, String[] permissions) {

        for(String permission : permissions) {
            if (!checkPermission(context, permission)) {
                return false;
            }
        }
        return true;
    }

}

这样@Permission就被切点劫持了,然后方法就会跑到切面aProceedingJoinPoint。然后获取上下文Context,把权限请求交给一个透明的Activity来做。做完之后判断结果,用户是同意了还是拒绝了还是曲线了。同意了直接执行point.proceed(),其他方式则通过Activity或者fragment获取带注解的方法,反射执行即可。

三,配置注解

复制代码
package com.example.myapplication;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {

    String[] value();

    int requestCode() default 1;
}

四,通过空白Activity完成真正的权限申请

复制代码
package com.example.myapplication;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class PermissionActivity extends Activity {

    private String[] permissions;
    private int requestCode;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        permissions = intent.getStringArrayExtra("permissions");
        requestCode = intent.getIntExtra("requestcode", 0);
        setContentView(R.layout.activity_permission);
        if (permissions != null && permissions.length > 0) {
            requestPermission(permissions);
        }
    }

    private void requestPermission(String[] permissions) {

        List<String> failure = new ArrayList<>();
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                failure.add(permission);
            }
        }
        if (failure.size() == 0) {
            requestPermissionSuccess();
            return;
        }

        ActivityCompat.requestPermissions(this, failure.toArray(new String[]{}), requestCode);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        
        if (requestCode == this.requestCode) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                requestPermissionSuccess();
            } else {

                boolean alwaysHidePermission = false;
                for (int i = 0; i < grantResults.length; i++) {
                    if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                        //判断是否勾选禁止后不再询问
                        boolean showRequestPermission = ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i]);
                        if (!showRequestPermission) {
                            alwaysHidePermission = true;
                        }
                    }
                }

                requestPermissionFailed();
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    private void requestPermissionSuccess() {
        setResult(RESULT_OK);
        finish();
    }

    private void requestPermissionFailed() {
        setResult(RESULT_CANCELED);
        finish();
    }
}

五,引入依赖配置

复制代码
apply plugin: 'com.android.application'


buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
}

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.netease.premissionstudy"
        minSdkVersion 19
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {

    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

    implementation 'org.aspectj:aspectjrt:1.8.13'
}


import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main

final def log = project.logger
final def variants = project.android.applicationVariants

variants.all { variant ->

    if (!variant.buildType.isDebuggable()) {
        log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
        return;
    }

    JavaCompile javaCompile = variant.javaCompile

    javaCompile.doLast {

        String[] args = ["-showWeaveInfo",

                         "-1.8",

                         "-inpath", javaCompile.destinationDir.toString(),

                         "-aspectpath", javaCompile.classpath.asPath,

                         "-d", javaCompile.destinationDir.toString(),

                         "-classpath", javaCompile.classpath.asPath,

                         "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]

        log.debug "ajc args: " + Arrays.toString(args)

        MessageHandler handler = new MessageHandler(true);

        new Main().run(args, handler);

        for (IMessage message : handler.getMessages(null, true)) {

            switch (message.getKind()) {

                case IMessage.ABORT:

                case IMessage.ERROR:

                case IMessage.FAIL:

                    log.error message.message, message.thrown

                    break;

                case IMessage.WARNING:

                    log.warn message.message, message.thrown

                    break;

                case IMessage.INFO:

                    log.info message.message, message.thrown

                    break;

                case IMessage.DEBUG:

                    log.debug message.message, message.thrown

                    break;
            }
        }
    }
}

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        google()
        jcenter()
        
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.2'
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'


        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
相关推荐
二J2 小时前
管理100个小程序-很难吗
android·小程序
s11show_1632 小时前
hz修改后台新增keyword功能
android·java·前端
IT技术图谱3 小时前
【绝非标题党】网络监听新姿势:APT编译时注解实现高扩展框架
android·面试·架构
RichardLai884 小时前
[Flutter 基础] - Flutter基础组件 - Text
android·flutter
姜行运4 小时前
数据结构【树和二叉树】
android·数据结构·算法·c#
东风西巷4 小时前
Control Center安卓版:自定义控制中心,提升手机操作体验
android·智能手机·性能优化·软件需求
CYRUS_STUDIO5 小时前
Android 加壳应用运行流程 与 生命周期类处理方案
android·安全·逆向
隐-梵5 小时前
Android studio进阶开发(四)--okhttp的网络通信的使用
android·ide·okhttp·android studio
_一条咸鱼_5 小时前
Android大厂面试通关秘籍
android·面试·android jetpack