上一篇文章我们主要介绍了在Android 6.0版本以上运行时权限的相关知识,并将使用方式进行了封装。但目前使用了Java注解+反射的方式实现,考虑到反射会影响到性能,下面将使用Java Annotation Processor处理(在此假设已经对Annotation Processor有了一些了解并会使用)。
对外暴露的接口还是和原来一样, 先上一段源码吧。
@AutoService(Processor.class)
public class PermissionProcessor extends AbstractProcessor {
private Elements elementUtils;
private Filer mFiler;
private Messager mMessager;
Map<String, ProxyInfo> classMap = new HashMap<>();
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementUtils = processingEnv.getElementUtils();
mFiler = processingEnv.getFiler();
mMessager = processingEnv.getMessager();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
HashSet<String> supportTypes = new LinkedHashSet<>();
supportTypes.add(PermissionSuccess.class.getCanonicalName());
supportTypes.add(PermissionFail.class.getCanonicalName());
return supportTypes;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
classMap.clear();
if (!gatherInformation(roundEnv, PermissionSuccess.class)) return false;
if (!gatherInformation(roundEnv, PermissionFail.class)) return false;
generateClassFile();
return true;
}
private boolean gatherInformation(RoundEnvironment roundEnv, Class<? extends Annotation> clazz) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(clazz);
for (Element element : elements) {
// 检测合法性
if (!checkMethodValid(element, clazz)) return false;
// 因为是方法,将element 转为ExecutableElement对象
ExecutableElement annotatedMethod = (ExecutableElement) element;
// class type
TypeElement classElement = (TypeElement) annotatedMethod.getEnclosingElement();
// class full name
String classFullName = classElement.getQualifiedName().toString();
ProxyInfo proxyInfo = classMap.get(classFullName);
if (proxyInfo == null) {
proxyInfo = new ProxyInfo(elementUtils, classElement);
classMap.put(classFullName, proxyInfo);
proxyInfo.setTypeElement(classElement);
}
// 获取注解
Annotation annotation = annotatedMethod.getAnnotation(clazz);
// 判断注解类型
if (annotation instanceof PermissionSuccess) {
int requestCode = ((PermissionSuccess) annotation).requestCode();
// 如果是PermissionSuccess注解,将方法名存入grantedMethodMap中,requestCode作为键
proxyInfo.grantedMethodMap.put(requestCode, annotatedMethod.getSimpleName().toString());
} else if (annotation instanceof PermissionFail) {
int requestCode = ((PermissionFail) annotation).requestCode();
proxyInfo.deniedMethodMap.put(requestCode, annotatedMethod.getSimpleName().toString());
} else {
error(element, "%s not support.", clazz.getSimpleName());
return false;
}
}
return true;
}
private void generateClassFile() {
// 遍历classMap
for (String key : classMap.keySet()) {
// 取出收集到的信息
ProxyInfo proxyInfo = classMap.get(key);
try {
mMessager.printMessage(Diagnostic.Kind.NOTE, "ClassFullName: " + proxyInfo.getProxyFullClassName());
JavaFileObject object = mFiler.createSourceFile(proxyInfo.getProxyFullClassName(), proxyInfo.getTypeElement());
Writer writer = object.openWriter();
// 生成Java代码
writer.write(proxyInfo.generateJavaCode());
writer.flush();
writer.close();
} catch (IOException e) {
error(proxyInfo.getTypeElement(),
"Unable to write injector for type %s: %s",
proxyInfo.getTypeElement(), e.getMessage());
}
}
}
private void error(Element element, String message, Object... args) {
if (args.length > 0) {
message = String.format(message, args);
}
mMessager.printMessage(Diagnostic.Kind.NOTE, message, element);
}
private boolean checkMethodValid(Element annotatedElement, Class clazz) {
if (annotatedElement.getKind() != ElementKind.METHOD) {
error(annotatedElement, "%s must be declared on method.", clazz.getSimpleName());
return false;
}
if (ClassValidator.isPrivate(annotatedElement) || ClassValidator.isAbstract(annotatedElement)) {
error(annotatedElement, "%s() must can not be abstract or private.", annotatedElement.getSimpleName());
return false;
}
return true;
}
}
process方法主要有两个功能:
- 收集信息
- 生成代码
收集信息的步骤在注释做了解释。接下来看生成代码的部分;
public String generateJavaCode() {
StringBuilder builder = new StringBuilder();
builder.append("// Generated code. Do not modify!\n");
builder.append("package ").append(packageName).append(";\n\n");
builder.append("import com.muse.permission.*;\n");
builder.append('\n');
builder.append("public class ").append(proxyClassName).append(" implements " + ProxyInfo.PROXY + "<" + typeElement.getSimpleName() + ">");
builder.append(" {\n");
generateMethods(builder);
builder.append('\n');
builder.append("}\n");
return builder.toString();
}
private void generateMethods(StringBuilder builder) {
generateSuccessMethod(builder);
generateFailMethod(builder);
}
private void generateFailMethod(StringBuilder builder) {
builder.append("@Override\n ");
builder.append("public void denied(" + typeElement.getSimpleName() + " source , int requestCode) {\n");
builder.append("switch(requestCode) {\n");
for (int code : deniedMethodMap.keySet()) {
builder.append("case " + code + ":\n");
builder.append("source." + deniedMethodMap.get(code) + "();");
builder.append("break;");
}
builder.append("}\n");
builder.append(" }\n");
}
private void generateSuccessMethod(StringBuilder builder) {
builder.append("@Override\n ");
builder.append("public void grant(" + typeElement.getSimpleName() + " source , int requestCode) {\n");
builder.append("switch(requestCode) {\n");
for (int code : grantedMethodMap.keySet()) {
builder.append("case " + code + ":\n");
builder.append("source." + grantedMethodMap.get(code) + "();");
builder.append("break;");
}
builder.append("}\n");
builder.append(" }\n");
}
这里生成了一个辅助类,该类实现了PermissionProxy接口。接口定义如下:
public interface PermissionProxy<T> {
void grant(T source, int requestCode);
void denied(T source, int requestCode);
}
以上generateSuccessMethod 方法和 generateFailMethod 方法分别是针对grant 接口方法 和 denied 接口方法的实现。使用了拼接字符串的方式实现了,还可以使用javapoet这里库实现,这里就先不做解释了,对此感兴趣的可以参考这篇文章:
JavaPoet的基本使用
下面看看生成的辅助类是什么样子;
import com.muse.permission.PermissionProxy;
public class MainActivity$$PermissionProxy implements PermissionProxy<MainActivity> {
public MainActivity$$PermissionProxy() {
}
public void grant(MainActivity source, int requestCode) {
switch(requestCode) {
case 1:
source.openSucc();
break;
case 2:
source.test1();
}
}
public void denied(MainActivity source, int requestCode) {
switch(requestCode) {
case 1:
source.openFail();
break;
case 2:
source.test2();
}
}
}
好了,讲解就到此吧!
又到周末了... “小伙子,‘快应用’ 了解下?”