从Android Apt(Annotation-Processing-Tool)到手写一个Android6.0以上的运行时权限申请框架

目录

  • 一.什么是APT
  • 二.用APT写一个自己的权限框架

一.什么是APT

apt是一种注解处理工具,对源码文件进行检测找出其中的注解,根据注解会自动生成代码,如果想要自定义的注解处理器能够正常运行,必须要通过apt工具来进行处理。相信用过EventBus、ButterKnife、Dagger2的同学都会有所了解,因为它们都用的是APT的技术。如果对于注解都不了解的,可以去看下我之前写过的注解反射一些总结

二.用APT写一个自己的权限框架

我们写个简单的权限请求的Demo来熟悉下APT的玩法。
先看看效果

在看看代码,首先我们新建两个JavaLib,Annotations和Compiler,一个是存放自定义注解,一个是用来对注解进行处理,
然后我们在新建一个Android的Library,这个库的作用是提供一些权限相关的处理类,还有用反射的方式,调用APT生成的代码。
我们先看下效果

Annotation Module的配置:

apply plugin: 'java-library'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}

//处理乱码问题
tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}
sourceCompatibility = "1.8"
targetCompatibility = "1.8"

Compiler Module的配置:

apply plugin: 'java-library'

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')

    implementation 'com.google.auto.service:auto-service:1.0-rc4'
    //    implementation 'com.google.auto.service:auto-service:1.0-rc6'
    //        annotationProcessor "com.google.auto.service:auto-service:1.0-rc6" //3.3.2以上需要这个

    implementation project(':Annotations')
}

tasks.withType(JavaCompile) {
    options.encoding = "UTF-8"
}
sourceCompatibility = "1.8"
targetCompatibility = "1.8"

Library Module的配置:

apply plugin: 'com.android.library'

android {
    compileSdkVersion 28
    compileOptions {
        sourceCompatibility 1.8
        targetCompatibility 1.8
    }


    defaultConfig {
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:28.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation project(':Annotations')
}

项目中的配置:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    compileOptions {
        sourceCompatibility 1.8
        targetCompatibility 1.8
    }
    defaultConfig {
        applicationId "com.seven.pemissions"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//        javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation project(':Annotations')
    implementation project(':Library')
    annotationProcessor project(':Compiler')

}

我们在来看看Compiler Module中的代码:

package com.seven.permissions.compiler;

import com.google.auto.service.AutoService;
import com.seven.annotations.NeedPermissions;
import com.seven.annotations.OnNeverAskPermissions;
import com.seven.annotations.OnPermissionsDenied;


import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

/**
 * Time:2019/12/5
 * 

* Author:wangzhou *

* Description: */ @AutoService(Processor.class) public class PermissionProcessor extends AbstractProcessor { private Messager messager; private Elements elementUtils; private Filer filer; private Types typeUtils; @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); typeUtils = processingEnvironment.getTypeUtils(); elementUtils = processingEnvironment.getElementUtils(); filer = processingEnvironment.getFiler(); messager = processingEnvironment.getMessager(); } @Override public Set<String> getSupportedAnnotationTypes() { Set<String> types = new LinkedHashSet<>(); types.add(NeedPermissions.class.getCanonicalName()); types.add(OnPermissionsDenied.class.getCanonicalName()); types.add(OnNeverAskPermissions.class.getCanonicalName()); return types; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latest(); } @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { messager.printMessage(Diagnostic.Kind.NOTE, "processing..."); System.out.println("start process"); Set<? extends Element> needPermissionsSet = roundEnvironment.getElementsAnnotatedWith(NeedPermissions.class); Map<String, List<ExecutableElement>> needPermissionMap = new HashMap<>(); for (Element element : needPermissionsSet) { ExecutableElement executableElement = (ExecutableElement) element; String activityName = getActivityName(executableElement); //获取类 List<ExecutableElement> list = needPermissionMap.get(activityName); if (list == null) { list = new ArrayList<>(); needPermissionMap.put(activityName, list); } list.add(executableElement); System.out.println("NeedPermission executableElement" + element.getSimpleName().toString()); } Set<? extends Element> onNeverAskAgainSet = roundEnvironment.getElementsAnnotatedWith(OnNeverAskPermissions.class); Map<String, List<ExecutableElement>> onNeverAskAgainMap = new HashMap<>(); for (Element element : onNeverAskAgainSet) { ExecutableElement executableElement = (ExecutableElement) element; String activityName = getActivityName(executableElement); List<ExecutableElement> list = onNeverAskAgainMap.get(activityName); if (list == null) { list = new ArrayList<>(); onNeverAskAgainMap.put(activityName, list); } list.add(executableElement); System.out.println("NeedPermission executableElement" + element.getSimpleName().toString()); } Set<? extends Element> onPermissionDeniedSet = roundEnvironment.getElementsAnnotatedWith(OnPermissionsDenied.class); Map<String, List<ExecutableElement>> onPermissionDeniedMap = new HashMap<>(); for (Element element : onPermissionDeniedSet) { ExecutableElement executableElement = (ExecutableElement) element; String activityName = getActivityName(executableElement); List<ExecutableElement> list = onPermissionDeniedMap.get(activityName); if (list == null) { list = new ArrayList<>(); onPermissionDeniedMap.put(activityName, list); } list.add(executableElement); System.out.println("NeedPermission executableElement" + element.getSimpleName().toString()); } for (String activityName : needPermissionMap.keySet()) { List<ExecutableElement> needPermissionElements = needPermissionMap.get(activityName); List<ExecutableElement> onNeverAskAgainElements = onNeverAskAgainMap.get(activityName); List<ExecutableElement> onPermissionDeniedElements = onPermissionDeniedMap.get(activityName); final String CLASS_SUFFIX = "$Permissions"; // Filer filer = processingEnv.getFiler(); try { JavaFileObject javaFileObject = filer.createSourceFile(activityName + CLASS_SUFFIX); String packageName = getPackageName(needPermissionElements.get(0)); //获取注解地方的包名 Writer writer = javaFileObject.openWriter(); String activitySimpleName = needPermissionElements.get(0).getEnclosingElement() .getSimpleName() .toString() + CLASS_SUFFIX; writer.write("package " + packageName + ";\n"); writer.write("import com.seven.permissions.library.listener.RequestPermission;\n"); writer.write("import com.seven.permissions.library.utils.PermissionUtils;\n"); writer.write("import android.support.v4.app.ActivityCompat;\n"); writer.write("import java.lang.ref.WeakReference;\n"); writer.write("import com.seven.permissions.library.listener.PermissionRequest;\n"); writer.write("public class " + activitySimpleName + " implements RequestPermission<" + activityName + ">{\n"); writer.write("private static final int REQUEST_CODE = 666;\n"); writer.write("private static String[] PERMISSION;\n"); writer.write("public void requestPermission(" + activityName + " target,String[] permissions) {\n"); writer.write(" PERMISSION = permissions ;\n"); writer.write(" if (PermissionUtils.hasSelfPermissions(target, permissions)) {\n"); if (needPermissionElements != null) { for (ExecutableElement executableElement : needPermissionElements) { String methodName = executableElement.getSimpleName().toString(); writer.write("target." + methodName + "();\n"); } } writer.write("}else{ ActivityCompat.requestPermissions(target, PERMISSION, REQUEST_CODE);}}\n"); writer.write("public void onRequestPermissionsResult(\n" + activityName + " target, int requestCode, int[] grantResults){"); writer.write(" switch (requestCode) {\n" + "case REQUEST_CODE:\n" + "if (PermissionUtils.verifyPermissions(grantResults)) {\n"); if (needPermissionElements != null) { for (ExecutableElement executableElement : needPermissionElements) { String methodName = executableElement.getSimpleName().toString(); writer.write("target." + methodName + "();\n"); } } writer.write("} else if (!PermissionUtils.shouldShowRequestPermissionRationale(target, PERMISSION)) {\n"); if (onNeverAskAgainElements != null) { for (ExecutableElement executableElement : onNeverAskAgainElements) { String methodName = executableElement.getSimpleName().toString(); writer.write("target." + methodName + "();\n"); } } writer.write("}else{\n"); if (onPermissionDeniedElements != null) { for (ExecutableElement executableElement : onPermissionDeniedElements) { String methodName = executableElement.getSimpleName().toString(); writer.write("target." + methodName + "();\n"); } } writer.write(" } break;\n default:\nbreak;\n}\n }\n}"); writer.flush(); writer.close(); } catch (Exception e) { e.printStackTrace(); } } return true; } private String getPackageName(ExecutableElement executableElement) { TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement(); String packageName = elementUtils.getPackageOf(typeElement).getQualifiedName().toString(); return packageName; } private String getActivityName(ExecutableElement executableElement) { String packageName = getPackageName(executableElement); TypeElement typeElement = (TypeElement) executableElement.getEnclosingElement(); return packageName + "." + typeElement.getSimpleName().toString(); } }

这个就是非常笨的方式,去扫描到注解,然后拼接代码。最终生成的代码,会在我们的主项目的这个目录下
从Android Apt(Annotation-Processing-Tool)到手写一个Android6.0以上的运行时权限申请框架_第1张图片

代码生成完了,我们在Library中提供两个方法将APT生成的代码和需要使用的地方做个绑定就可以了。代码我就不贴了,有兴趣可以去我的Git上看一看。地址:玩一玩APT

上面的纯手工写代码是不是显得有点不智能,不过作为入门级玩一玩APT还是可以的,我们在来玩玩JavaPoet,Git地址是:JakeWharton大神的JavaPoet,JavaPoet是提供一些API,帮我们去生成代码。有兴趣的同学可以去看看。

你可能感兴趣的:(#,常用框架)