赞
踩
API 23之前的版本都是自动获取权限,而从 Android 6.0 开始添加了权限申请的需求,更加安全。并且目前android系统已经发展到10.0,加上国家对用户隐私的保护,了解这一技术不可避免。google也为我们提供了RxPermission这一框架RxPermission,有兴趣的可以自行了解。本文将采用AspectJ的方式实现。
dependencies {
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'org.aspectj:aspectjtools:1.8.6'
}
(1)添加依赖
implementation 'org.aspectj:aspectjrt:1.8.13'
(2)添加aspect编译相关
下面这一段代码,是在主module下的配置,如果想要在library中使用,可以自行查看如何配置。
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; } } } }
/** * <pre> * author : QB * time : 2019/10/21 * version : v1.0.0 * desc : 权限和请求码 * </pre> */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Permission { /** * 请求权限 */ String[] value(); /** * 请求码 */ int requestCode(); }
/** * <pre> * author : QB * time : 2019/10/21 * version : v1.0.0 * desc : 权限申请结果回调 * </pre> */ public interface PermissionRequestCallback { /** * 申请权限成功 */ void permissionSuccess(); /** * 申请权限失败,用户点击拒绝了 */ void permissionCanceled(); /** * 申请权限失败,用户点击了不再询问 */ void permissionDenied(); }
(1)判断权限是否已经申请
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = getIntent(); if (intent != null) { String[] permissions = intent.getStringArrayExtra(REQUEST_PERMISSIONS); int requestCode = intent.getIntExtra(REQUEST_CODE, REQUEST_CODE_DEFAULT); //三者一项不符合,结束当前页面 if (permissions == null || requestCode == -1 || requestCallback == null) { this.finish(); return; } //判断是否已经授权了 if(PermissionUtils.hasPermissionRequest(this,permissions)){ requestCallback.permissionSuccess(); this.finish(); return; } //开始申请权限 ActivityCompat.requestPermissions(this,permissions,requestCode); } }
判断权限是否已经申请,并将结果进行回调。如果没有申请,开始申请权限。
(2)权限申请系统回调
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); //再次判断是否权限真正的申请成功 if(PermissionUtils.requestPermissionSuccess(grantResults)){ requestCallback.permissionSuccess(); this.finish(); return; } //权限拒绝,并且用户勾选了不在提示 if(PermissionUtils.shouldShowRequestPermissionRationale(this,permissions)){ requestCallback.permissionCanceled(); this.finish(); return; } //权限拒绝 requestCallback.permissionDenied(); this.finish(); }
回调权限申请结果。
核心代码:
//启动透明的activity DialogActivity.launchDialog(context, permission.value(), permission.requestCode(), new PermissionRequestCallback() { @Override public void permissionSuccess() { //代码继续执行下去 try { joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } } @Override public void permissionCanceled() { //反射执行permissionCanceled()方法 PermissionUtils.invokeAnnotation(object,PermissionFailed.class); } @Override public void permissionDenied() { //反射执行permissionDenied()方法 PermissionUtils.invokeAnnotation(object,PermissionDenied.class); //跳转到系统设置界面 PermissionUtils.startAndroidSettings(finalContext); } });
首先拿到当前activity的上下文,然后用它来启动这个权限申请的透明的activity。得到申请的回调结果。三种情况:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionFailed {
/**
* 请求码
*/
int requestCode();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionDenied {
/**
* 请求码
*/
int requestCode();
}
public static void invokeAnnotation(Object object, Class annotationClass) { // 获取 object 的 Class对象 Class<?> objectClass = object.getClass(); // 遍历 所有的方法 Method[] methods = objectClass.getDeclaredMethods(); for (Method method : methods) { method.setAccessible(true); // 让虚拟机,不要去检测 private // 判断方法 是否有被 annotationClass注解的方法 boolean annotationPresent = method.isAnnotationPresent(annotationClass); if (annotationPresent) { // 当前方法 代表包含了 annotationClass注解的 try { method.invoke(object); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } }
由于不同的手机厂商,跳转到设置界面的代码是不一样的,所以,这里要做适配。适配采用依赖倒置原则。所谓的依赖倒置是面向过程,不面向细节的。
(1)创建依赖接口
public interface ISetting {
Intent getStartSettingsIntent(Context context);
}
(2)针对不同手机进行实现
(3)跳转实现
public static void startAndroidSettings(Context context) { // 拿到当前手机品牌制造商,来获取 具体细节 Class aClass = permissionMenu.get(Build.MANUFACTURER.toLowerCase()); if (aClass == null) { aClass = permissionMenu.get(MANUFACTURER_DEFAULT); } try { Object newInstance = aClass.newInstance(); // new OPPOStartSettings() ISetting iMenu = (ISetting) newInstance; // ISetting iMenu = (ISetting) oPPOStartSettings; // 高层 面向抽象,而不是具体细节 Intent startActivityIntent = iMenu.getStartSettingsIntent(context); if (startActivityIntent != null) { context.startActivity(startActivityIntent); } } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } }
在目标activity中添加如下代码。
@Permission(value = "android.permission.READ_EXTERNAL_STORAGE", requestCode = 1)
private void requestPermission() {
Toast.makeText(this, "权限申请成功", Toast.LENGTH_SHORT).show();
}
@PermissionFailed(requestCode = 1)
private void requestPermissionFailed() {
Toast.makeText(this, "权限申请失败", Toast.LENGTH_SHORT).show();
}
@PermissionDenied(requestCode = 1)
private void requestPermissionDenied() {
Toast.makeText(this, "权限申请失败,不再询问", Toast.LENGTH_SHORT).show();
}
代码传送门源码下载。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。