目錄
- 模塊間交互
- APT介紹與使用
- APT高階用法JavaPoet
模塊間交互
常見的交互方式
1)EventBus一對一通訊,會造成Bean對象泛
2)反射技術 維護成本較高,高版本容易出現@hide限
3)隱式意圖 維護成本較高,action比較難以維護
4) 廣播 7.0後需要動態註冊
5)類加載器 需要全類名路徑
解決方案
類加載模式
public void jumpApp(View v){
try {
//通過全類名的方式進行跳轉
Class<?> clzz = Class.forName("com.canjun.myapplication.MainActivity");
Intent intent = new Intent(this,clzz);
startActivity(intent);
}catch (Exception e){
}
}
全局map記錄的方式
-
在公共模塊(common模塊)添加全局記錄類
/** * RecorderPathManager * 記錄全局的path信息 * * @author zfc * @date 2020-01-09 */ public class RecorderPathManager { private static Map<String, List<PathBean>> paths = new HashMap<>(); /** * 根據組名和路徑名記錄字節碼信息 * @param groupName * @param pathName * @param clazz */ public static void joinGroup(String groupName,String pathName,Class clazz){ List<PathBean> path = paths.get(groupName); if(path==null){ //添加 path = new ArrayList<>(); paths.put(groupName,path); }else { for (PathBean p: path){ if(p.getPath().equals(pathName)){ return; } } } path.add(new PathBean(pathName,clazz)); } /** * 獲取目標字節碼對象 * @param groupName 組名 * @param pathName 路徑名 * @return */ public static Class getTargetClass(String groupName,String pathName){ List<PathBean> path = paths.get(groupName); if(path==null){ return null; } for (PathBean p: path){ if(p.getPath().equals(pathName)){ return p.getClzz(); } } return null; } } /** * PathBean * * 記錄Activity字節碼及其路徑 * 例如 * path:'order/OrderMainActivity' * clzz: OrderMainActivity.class * * @author zfc * @date 2020-01-09 */ public class PathBean { private String path; private Class clzz; public PathBean(String path, Class clzz) { this.path = path; this.clzz = clzz; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public Class getClzz() { return clzz; } public void setClzz(Class clzz) { this.clzz = clzz; } }
-
在應用啓動時,添加需要記錄的字節碼對象
/** * MyApp * * @author zfc * @date 2020-01-09 */ public class MyApp extends BaseApplication { @Override public void onCreate() { super.onCreate(); //註冊activity RecorderPathManager.joinGroup("app","MainActivity",MainActivity.class); RecorderPathManager.joinGroup("order","OrderMainActivity", OrderMainActivity.class); RecorderPathManager.joinGroup("personal","PersonalMainActivity", PersonalMainActivity.class); } }
-
在頁面跳轉時,使用字節碼對象
try { Class<?> clzz = RecorderPathManager.getTargetClass("app","MainActivity"); Intent intent = new Intent(this, clzz); startActivity(intent); }catch (Exception e){ }
APT介紹與使用
APT是什麼
結構體語言
語言元素分類
常用的API
APT基本使用
-
環境描述
a.創建java library 名爲compiler
b.爲compiler添加依賴
// 註冊註解,並對其生成META-INF的配置信息,rc2在gradle5.0後有坑 // As-3.2.1 + gradle4.10.1-all + auto-service:1.0-rc2 // implementation 'com.google.auto.service:auto-service:1.0-rc2' // As-3.4.1 + gradle5.1.1-all + auto-service:1.0-rc4 compileOnly'com.google.auto.service:auto-service:1.0-rc4' annotationProcessor'com.google.auto.service:auto-service:1.0-rc4' implementation project(':annotation')
-
創建註解處理器
/* * 通過autoService通過生成文件 */ @AutoService(Processor.class) @SupportedAnnotationTypes({"com.canjun.annotation.ARouter"}) @SupportedSourceVersion(SourceVersion.RELEASE_7) @SupportedOptions({"content"}) //外部傳入的參數 public class ARouterProcessor extends AbstractProcessor { /** * 操作Element的工具類 */ private Elements elementUtils; /** * 類信息工具類 */ private Types typesUtils; /** * 日誌信息輸出工具類 */ private Messager messager; /** * 文件生成器 */ private Filer filer; //初始化工作 @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); elementUtils = processingEnv.getElementUtils(); typesUtils = processingEnv.getTypeUtils(); messager = processingEnv.getMessager(); filer = processingEnv.getFiler(); //可以獲取外部模塊傳入的參數 //傳參方式見下小結 String content = processingEnv.getOptions().get("content"); messager.printMessage(Diagnostic.Kind.NOTE,content); } // // //需要處理的註解類型 // @Override // public Set<String> getSupportedAnnotationTypes() { // return super.getSupportedAnnotationTypes(); // } // // //jdk版本去編輯 // @Override // public SourceVersion getSupportedSourceVersion() { // return super.getSupportedSourceVersion(); // } // // //接收外部參數 // @Override // public Set<String> getSupportedOptions() { // return super.getSupportedOptions(); // } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { }
-
通過AndroidModule向java library compiler傳入參數
// 在gradle文件中配置選項參數值(用於APT傳參接收) // 切記:必須寫在defaultConfig節點下 javaCompileOptions { annotationProcessorOptions { arguments = [content : 'hello apt'] } }
-
AndroidModule使用註解處理器
a.gradle中依賴的聲明
implementation project(':annotation') //使用註解處理器 annotationProcessor project(':compiler')
b.Activity中使用註解
@ARouter(path="/app/MainActivity") public class MainActivity extends AppCompatActivity { ... ... }
-
註解處理中核心方法的實現
@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { if(annotations == null){ return false; } messager.printMessage(Diagnostic.Kind.NOTE,annotations.toString()); //獲取被註解的類對象 Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(ARouter.class); for (Element e:elements){ //獲取包名 String pkgName = elementUtils.getPackageOf(e).getQualifiedName().toString(); //獲取類名 String className = e.getSimpleName().toString(); messager.printMessage(Diagnostic.Kind.NOTE,pkgName + ">>" + className); //生成ARouter文件 String finalClassName = className+"$$ARouter"; try { JavaFileObject sourceFile = filer.createSourceFile(pkgName+"."+finalClassName); Writer writer = sourceFile.openWriter(); //設置包名 writer.write("package "+pkgName + ";\n"); writer.write("public class "+finalClassName+" {\n"); writer.write("public static Class<?> findTargetClass(String pathName){\n"); //獲取註解的path的value(注意) String path = e.getAnnotation(ARouter.class).path(); writer.write("if(pathName.equalsIgnoreCase(\""+path+"\")){\n"); writer.write("return "+className+".class;\n"); writer.write("}\n"); writer.write("return null;\n"); writer.write("}\n"); writer.write("}\n"); writer.close(); } catch (Exception ex) { ex.printStackTrace(); } } return true; }
-
驗證註解處理器生成的類
通過build->make project
使用autoService註解 註解處理器,會在如下圖中的位置生成註冊信息。
APT高階用法和JavaPoet
JavaPoet概述
JavaPoet是什麼
JavaPoet運行環境
implementation 'com.squareup:javapoet:1.9.0'
// 註冊註解,並對其生成META-INF的配置信息,rc2在gradle5.0後有坑
// As-3.2.1 + gradle4.10.1-all + auto-service:1.0-rc2
// implementation 'com.google.auto.service:auto-service:1.0-rc2'
// As-3.4.1 + gradle5.1.1-all + auto-service:1.0-rc4
compileOnly'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'
JavaPoet常用類
javaPoet格式化字符串
使用javaPoet
要求 生成如下格式的代碼
package com.canjun.myapplication;
public class MainActivity$$ARouter {
public static Class findTargetClass(String name){
if(name.equalsIgnoreCase("/app/MainActivity")){
return MainActivity.class;
}
return null;
}
}
javaPoet編寫內容如下:
@AutoService(Processor.class)
@SupportedAnnotationTypes({"com.canjun.annotation.ARouter"})
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedOptions({"content"})
public class ARouterProcessor extends AbstractProcessor {
private Elements elementUtils;
private Types typeUtils;
private Messager messager;
private Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementUtils = processingEnv.getElementUtils();
typeUtils = processingEnv.getTypeUtils();
messager = processingEnv.getMessager();
filer = processingEnv.getFiler();
Map<String, String> options = processingEnv.getOptions();
String content = options.get("content");
messager.printMessage(Diagnostic.Kind.NOTE, content);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if(annotations==null||annotations.isEmpty()){
return false;
}
//獲取被ARouter註解的類
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(ARouter.class);
for (Element e : elements){
//獲取e的包名
String pkgName = elementUtils.getPackageOf(e).getQualifiedName().toString();
//獲取類名
String className = e.getSimpleName().toString();
String newFileName = className+"$$ARouter";
//通過javaPoet寫新生成的文件
//javaPoet項目地址https://github.com/square/javapoet
try {
String pathName = e.getAnnotation(ARouter.class).path();
MethodSpec methodSpec = MethodSpec.methodBuilder("findTargetClass")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(Class.class)
.addParameter(String.class, "name")
//需要注意的是 語句中的參數 不需要顯式添加“;”
.addStatement(" if(name.equalsIgnoreCase($S)){\n" +
" return $T.class;\n" +
" }\n" +
" return null", pathName, ClassName.get((TypeElement)e))
.build();
TypeSpec typeSpec = TypeSpec.classBuilder(newFileName)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(methodSpec)
.build();
JavaFile javaFile = JavaFile.builder(pkgName,typeSpec)
.build();
javaFile.writeTo(filer);
} catch (IOException ex) {
ex.printStackTrace();
}
}
return true;
}
}
生成結果: