美食家大橙子 发表于 3 天前

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 == PackageManager.PERMISSION_GRANTED) {

                requestPermissionSuccess();
            } else {

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


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Android怎样通过aspectj打造一个无侵入式动态权限申请框架