在組件化開發(fā)過程中呆盖,我們經(jīng)常會遇到一個問題,一些全局變量如何共享贷笛,如Application的全局功效应又,還有在應(yīng)用初始化過程中能否同時對組件進行初始化操作。誠然乏苦,通過每次使用手動編寫株扛,或者維護一個組件初始化表通過反射方式進行動態(tài)初始化操作都是可以的。那有沒有一個更效率更方便的方式實現(xiàn)呢汇荐,答案是有多洞就,接下來我們就通過Demo了解APT的使用及組件初始化的實現(xiàn)邏輯。
一 創(chuàng)建項目
首先我們先創(chuàng)建一個MyAnnotation的項目掀淘。同時添加兩個Java-lib的Module旬蟋。
其中AnnotationLib作為注解類。ProcessLib作為解析類革娄。
二 創(chuàng)建注解
然后我們在AnnotationLib下添加注解類InitAppliation 代碼如下
package kt.dongdong.annotationlib;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface InitAppliation {
}
其中元注解@Target和@Retention分別表示作用對象為class倾贰,留存范圍為源碼階段。其他相關(guān)參數(shù)意義可以自行查詢稠腊。
這樣我們注解就完成了躁染,接下來我們處理對于多解析類。
解析配置
在創(chuàng)建解析類前架忌,我們先在ProcessLib中做一些相關(guān)引用。
implementation project(":AnnotationLib")
implementation 'com.google.auto.service:auto-service:1.0-rc4'
然后我們需要創(chuàng)建一個解析類InitProcess我衬。并在類上添加注解與父類叹放。
@AutoService(Processor.class)
public class InitProcess extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
}
其中@AutoService表示注冊此解析類,只有注冊了才能用挠羔。
然后繼承AbstractProcessor并實現(xiàn)相關(guān)方法井仰。
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
這個方法返回執(zhí)行的java版本,一般直接返回SourceVersion.latestSupported()即可破加,當(dāng)然也可以指定版本如:SourceVersion.RELEASE_7則定義為java7.
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> setTypes = new HashSet<>();
setTypes.add(InitAppliation.class.getCanonicalName());
return setTypes;
}
getSupportedAnnotationTypes返回需要處理的注解的集合俱恶。其中InitAppliation.class.getCanonicalName()表示拿到注解類的完整路徑。
ps:以上兩個方法配置都可以用注解的形式實現(xiàn)。
接下來我們就需要實現(xiàn)自動生成類的部分了
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 參數(shù)annotations表示需要處理的注解的集合
// 參數(shù)roundEnv通過getElementsAnnotatedWith方法可以獲取對應(yīng)使用注解的類
if (annotations.isEmpty()) {
return false;
}
for (TypeElement typeElement : annotations) {
//判斷是否是要解析的注解類
if (typeElement.toString().equals(InitApplication.class.getCanonicalName())) {
//獲取對應(yīng)列表
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(typeElement);
StringBuilder strImpory = new StringBuilder();
StringBuilder strNew = new StringBuilder();
for (Element element : elements) {
strImpory.append("import " + element.toString() + ";\n");
strNew.append(element.getSimpleName() + " "
+ element.getSimpleName().toString() + "C = new "
+ element.getSimpleName() + "();\n");
strNew.append(element.getSimpleName().toString() + "C.init(application);\n");
}
try {
//通過processingEnv.getFiler()創(chuàng)建類文件并寫入合是。
JavaFileObject fileObject = processingEnv.getFiler().createSourceFile("kt.dongdong.annotationlib.AllInit");
Writer writer = fileObject.openWriter();
writer.write("package kt.dongdong.annotationlib;\n\n");
writer.write("import android.app.Application;\n");
writer.write(strImpory.toString() + "\n\n\n");
writer.write("public class AllInit{\n\n");
writer.write("public AllInit(Application application){\n");
writer.write(strNew.toString());
writer.write("}\n}\n");
writer.close();//注意需要關(guān)閉流 不然文件沒有內(nèi)容
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
- 在上面代碼中我寫了些注釋了罪,主要流程是:
- 通過roundEnv.getElementsAnnotatedWith()獲取使用了對應(yīng)注解多所有類信息。
- 通過循環(huán)這些類編寫對應(yīng)import和初始化的代碼串聪全。
- 通過processingEnv.getFiler().createSourceFile()創(chuàng)建指定名稱的類文件泊藕。
- 然后開啟流編寫包名,引用难礼,類名娃圆,初始化并調(diào)用方法等信息
- 然后關(guān)閉流 writer.close()很重要 不然生成的類沒有內(nèi)容。
生成文件
先在App項目中添加引用
implementation project(":AnnotationLib")
annotationProcessor project(":ProcessLib")
這里要注意的是解析Lib引用使用annotationProcessor關(guān)鍵字
創(chuàng)建AppliationA蛾茉,AppliationB類并添加注解讼呢。然后執(zhí)行Build 》ReBuild Project。
便能在該目錄下生成文件了谦炬。
文件內(nèi)容為我們在解析類里面寫入的內(nèi)容
package kt.dongdong.annotationlib;
import android.app.Application;
import kt.dongdong.myannotation.AppliationA;
import kt.dongdong.myannotation.AppliationB;
public class AllInit{
public AllInit(Application application){
AppliationA AppliationAC = new AppliationA();
AppliationAC.init(application);
AppliationB AppliationBC = new AppliationB();
AppliationBC.init(application);
}
}
PS:這里有幾點需要注意的
如果你的Gradle版本高于5悦屏,也許會因為不兼容無法生成類文件。這時候需要在注解類的Lib里面添加下面的引用
implementation 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
或者你可以把gradle版本降低至4.6 吧寺,這時候生成類的地址變?yōu)閎uild>generated>source>apt下
使用方式
在上面我們知道可以生成對于的類文件窜管,但是在開發(fā)中我們未編譯時又如何去使用對應(yīng)的生成類呢。這時候我們就需要通過一個中間類以反射的形式進行初始化了稚机。
package kt.dongdong.myannotation;
import android.app.Application;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class TestAppInit {
public static void init(Application application) {
try {
Class<?> clazz = Class.forName("kt.dongdong.annotationlib.AllInit");
Class[] parameterTypes = {Application.class};
Constructor constructor = clazz.getConstructor(parameterTypes);
Object object = constructor.newInstance(application);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
其中Class.forName的類名就是我們在解析類里生成文件的名稱幕帆。
然后在Application中進行調(diào)研
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
TestAppInit.init(this);
}
}
運行起來后查看日志
OK 致此通過注解形式初始化組件與APT的使用方式大致了解了就。