当前位置: 首页 > news >正文

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.myapplicationprivate 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;@Overrideprotected 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);}@Overridepublic 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 29buildToolsVersion "29.0.2"defaultConfig {applicationId "com.netease.premissionstudy"minSdkVersion 19targetSdkVersion 29versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles 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.Mainfinal def log = project.logger
final def variants = project.android.applicationVariantsvariants.all { variant ->if (!variant.buildType.isDebuggable()) {log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")return;}JavaCompile javaCompile = variant.javaCompilejavaCompile.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.thrownbreak;case IMessage.WARNING:log.warn message.message, message.thrownbreak;case IMessage.INFO:log.info message.message, message.thrownbreak;case IMessage.DEBUG:log.debug message.message, message.thrownbreak;}}}
}
// 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
}

相关文章:

  • webpack基础使用了解(入口、出口、插件、加载器、优化、别名、打包模式、环境变量、代码分割等)
  • IDEA创建Gradle项目然后删除报错解决方法
  • 调整IntelliJ IDEA中当前文件所在目录的显示位置
  • [原创](现代Delphi 12指南):[macOS 64bit App开发]:在Mac App Store外创建、部署与公证
  • CoinNexus Chain 推出泰利风暴,开启 Web3.0 智能金融元宇宙科技新时代
  • Lua 第8部分 补充知识
  • webrtc使用
  • 自然语言处理 | 语言模型(LM) 浅析
  • 【MCP Node.js SDK 全栈进阶指南】中级篇(2):MCP身份验证与授权实践
  • AI数字人:品牌营销的新宠与增长密码(6/10)
  • 【Linux】计算机基本知识补充
  • 使用PyTorch构建神经网络笔记
  • 【音视频】FFmpeg内存模型
  • 准确--Tomcat更换证书
  • 短视频+直播商城系统源码全解析:音视频流、商品组件逻辑剖析
  • 【延迟双删】简单解析
  • Java 安全:如何防止 SQL 注入与 XSS 攻击?
  • 【Harmony】常用工具类封装
  • Kafka 面试,java实战贴
  • ARM Cortex-M (STM32)如何调试HardFault
  • 王励勤当选中国乒乓球协会新一任主席
  • 医学泰斗客死他乡?AI小作文批量如何炮制?对话已被抓获的网络水军成员
  • 全国人大常委会启动工会法执法检查
  • 吉祥航空去年净利增超17%,海航实控人方威退出前十大股东
  • 中保协:当前普通型人身保险产品预定利率研究值为2.13%
  • 深一度|中国花样滑冰因何大滑坡