ARouter系列文章:
“終於懂了” 系列:組件化框架 ARouter 完全解析(一)原理全解
“終於懂了” 系列:組件化框架 ARouter 完全解析(二)APT—幫助類生成
“終於懂了” 系列:組件化框架 ARouter 完全解析(二)AGP/Transform—動態代碼注入
在上一篇《“終於懂了” 系列:組件化框架 ARouter 完全解析(一) 原理詳解》中,詳細介紹了ARouter的核心原理。其中提到了“幫助類”的概念,也就是在運行時生成 用於幫助填充WareHouse路由元信息的類,這裏就涉及到了APT技術。那麼本篇就對這一技術點進行介紹,並詳細分析ARouter中是如何使用APT來生成幫助類的。
一、APT介紹
1.1 APT的理解
APT(Annotation Processing Tool),即 註解處理器,是javac中提供的編譯時掃描和處理註解的工具,它對源代碼文件進行檢測找出其中的註解,然後使用註解進行額外的處理。
註解就像是一個標籤,有很多類型,可以貼在某些元素上面進行標記,並且標籤上可以寫一些信息。APT就是用來處理標籤的工具,在編譯開始後,可以拿到自己所關心的類型的所有標籤,然後根據標籤信息和被標記的元素信息,做一些事情。做那些事呢,這就看你如何寫APT了,你讓他幹啥他就幹啥,通常都是會生成一些幫助類——幫助完成你的目的的類。 後面無論對這種標籤的使用是增加、減少了,每次編譯都會重新走這一過程,而上一次的處理結果會被清空。
宏觀上理解,APT就是javac提供給開發者在編譯時處理註解的一種技術;微觀上,具體到實例中就是指 繼承自javax.annotation.processing.AbstractProcessor 的實現類,即一個處理特定註解的處理器。(下文提到的APT都是宏觀上理解,具體的處理器簡稱爲Processor)
那不使用APT能否完成目的呢? 也是可以的,畢竟APT就是爲了幫助我完成目的,那我自己肯定也是可以完成目的的,只不過有了APT會很省事。例如,上篇提到的幫助類,目的就是爲了收集路由元信息(路由目標類class),我們如果不使用ARouter,那麼就需要自己定義一個moduleA、moduelB共同依賴的muduleX,把需要進行跳轉的XXXActivity這些類的class手工寫代碼保存到muduleX的Map中,key可以用XXXActivity的類名,value就是XXXActivity.class,這樣moduleA、moduelB之間發起跳轉時 就通過想要跳轉的Activity的類名 從muduleX的Map中獲取目標Activity的class,這樣也是能完成 無相互依賴的moduleA、moduelB之前進行頁面跳轉的。
而使用了APT,只要使用註解進行標記即可,無論使用者怎麼標記,每次編譯時都由APT統一處理,不會出錯、也不擔心有遺漏。
通常用到APT技術的都是像ARouter這樣的通用能力框架:
- 提供定義好的註解,例如@Route
- 提供簡潔的API接口,例如ARouter.getInstance().build("/module/1").navigation()
這樣上層業務使用極爲方便,只需要使用註解進行標記,然後調用API即可。信息如何收集和存儲、如何尋找和使用,框架使用者是不用關心的。
APT還有兩個特點:
- 獲取註解及生成代碼都是在代碼編譯時候完成的,相比反射在運行時處理註解大大提高了程序性能。
- 注意APT並不能對源文件進行修改,只能獲取註解信息和被註解對象的信息,然後做一些自定義的處理,例如生成java類。
1.2 APT的原理
我們先來看下Java的編譯過程:
可以看到在Java源碼到class文件之間,需要經過註解處理器的處理,註解處理器生成的代碼也同樣會經過這一過程,最終一起生成class文件。在Android中,class文件還會被打進Dex文件中,最後生成APK文件。
註解處理器的執行是在編譯的初始階段,並且會有多個processor(查看所有註冊的processor:intermediates/annotation_processor_list/debug/annotationProcessors.json)。
那麼我們自定義的註解處理器,如何註冊進去呢?又如何實現一個註解處理器呢?
來看一個簡單的實例:
@AutoService(Processor.class) //把 TestProcessor註冊到編譯器中
@SupportedAnnotationTypes({"com.hfy.test_annotations.TestAnnotation"})//TestProcessor 要處理的註解
@SupportedSourceVersion(SourceVersion.RELEASE_8)//設置jdk環境爲java8
//@SupportedOptions() //一些支持的可選配置項
public class TestProcessor extends AbstractProcessor {
public static final String ACTIVITY = "android.app.Activity";
//Filer 就是文件流輸出路徑,當我們用AbstractProcess生成一個java類的時候,我們需要保存在Filer指定的目錄下
Filer mFiler;
//類型 相關的工具類。當process執行的時候,由於並沒有加載類信息,所以java文件中的類信息都是用element來代替了。
//類型相關的都被轉化成了一個叫TypeMirror,其getKind方法返回類型信息,其中包含了基礎類型以及引用類型。
Types types;
//Elements 獲取元素信息的工具,比如說一些類信息繼承關係等。
Elements elementUtils;
//來報告錯誤、警告以及提示信息
//用來寫一些信息給使用此註解庫的第三方開發者的
Messager messager;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnv.getFiler();
types = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
messager = processingEnv.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
if (annotations == null || annotations.size() == 0){
return false;
}
//獲取所有包含 @TestAnnotation 註解的元素
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(TestAnnotation.class);
//每個Processor的獨自的邏輯,其他的寫法一般都是固定的
parseAnnotation(elements)
return true;
}
//解析註解並生成java文件
private boolean parseAnnotation(Set<? extends Element> elements) {
...
}
}
上面的TestProcessor就是一個典型的註解處理器,繼承自javax.annotation.processing.AbstractProcessor。注意到TestProcessor重寫了init()和process()兩個方法,並且添加了幾個註解。
幾個註解是處理器的註冊和配置 :
- 使用註解
@AutoService
進行註冊,這樣在編譯這段就會執行這個處理器了。需要依賴com.google.auto.service:auto-service:1.0-rc4' 才能使用@AutoService - 使用註解
@SupportedAnnotationTypes
設置 TestProcessor 要處理的註解,要使用目標註解全類名 - 使用註解
@SupportedSourceVersion
設置支持的Java版本、使用註解@SupportedOptions()配置一些支持的可選配置項
重寫的兩個方法:init()、process()是Processor的初始化和處理過程:
-
init() 方法是初始化處理器,每個註解處理器被初始化的時候都會被調用,通常是這裏使用 處理環境-ProcessingEnvironment 獲取一些工具實例,如上所示是一般通用的寫法:
- mFiler,就是文件流輸出路徑,當我們用AbstractProcess生成一個java類的時候,我們需要保存在Filer指定的目錄下
- types,類型 相關的工具類。當process執行的時候,由於並沒有加載類信息,所以java文件中的類信息都是用element來代替了。類型相關的都被轉化成了一個叫TypeMirror,其getKind方法返回類型信息,其中包含了基礎類型以及引用類型。
- elementUtils,獲取元素信息的工具,比如說一些類信息繼承關係等。
- messager,來報告錯誤、警告以及提示信息,用來寫一些信息給使用此註解庫的第三方開發者的
-
process() 方法,註解處理器實際處理方法,在這裏寫處理註解的代碼,以及生成Java文件。它有兩個參數:
- annotations,是@SupportedAnnotationTypes聲明的註解 和 未被其他Processor消費的註解的子集,也就是剩下的且是本Processor關注的註解,類型是TypeElement
- roundEnvironment,有關當前和之前處理器的環境信息,可以讓你查詢出包含特定註解的被註解元素
- 返回值,true表示@SupportedAnnotationTypes中聲明的註解 由此 Processor 消費掉,不會傳給下個Processor。註解不斷向下分發,每個processor都可以決定是否消費掉自己聲明的註解。
在編譯流程進入Processor前,APT會對整個Java源文件進行掃描,這樣就會獲取到 所有添加了的註解和對應被註解的類。 註解和被註解的類,一起被視爲一個元素,即TypeElement,就是process()方法參數annotations的數據類型。 通過TypeElement,我們可以獲取註解的所有信息、被註解類的所有信息,這樣就可以根據這些信息來生成 我們需要的幫助類了。
這裏重點是理解Processor的原理,關於註解和Element的知識這裏不過多介紹,可自行了解。
到這裏,Processor的工作流程我們瞭解了,並且其 定義、配置、註冊 這些都是固定的寫法,一般無需過多關注。最重要的就是 process()方法的實現:拿到所有關注的註解元素後,就是每個Processor的獨自的邏輯——解析註解並生成需要的java文件。
二、ARouter的APT
2.1 ARouter工程介紹
ARouter是一個典型的 APT+AGP 框架,有4個module:
- annotation,定義共業務側使用的註解,例如@Route
- api,框架的核心邏輯實現 並提供便於業務側使用的Api,即上一篇介紹的內容
- compiler,定義註解處理器,用於生Java類,就是對APT的使用,即本篇的內容
- gradle-plugin,定義Android Gradle 插件,用於編譯時在class轉爲dex文件前掃描目標幫助類 並進行代碼注入,將在第三篇中介紹
接下來就來詳細分析 compiler,看看ARouter是如何解析註解並生成幫助類的。
2.2 RouteProcessor
2.2.1 BaseProcessor
有4個Processor,其中 BaseProcessor 是直接繼承自 AbstractProcessor 的,其他都是繼承BaseProcessor:
- RouteProcessor,處理註解@Route、@Autowired註解,用於生成路由幫助類
- InterceptorProcessor,處理@Interceptor註解,用於生成攔截器幫助列
- AutowiredProcessor,處理@Autowired註解,用戶生成處理activity/fragment變量的幫助類
這裏我們重點關注RouteProcessor。先來看下 BaseProcessor:
public abstract class BaseProcessor extends AbstractProcessor {
Filer mFiler;
Logger logger;
Types types;
Elements elementUtils;
TypeUtils typeUtils;
//此Module的名字
String moduleName = null;
// 是否生成router doc
boolean generateDoc;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler();
types = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
typeUtils = new TypeUtils(types, elementUtils);
logger = new Logger(processingEnv.getMessager());
// 獲取配置: moduleName、generateDoc
Map<String, String> options = processingEnv.getOptions();
if (MapUtils.isNotEmpty(options)) {
moduleName = options.get(KEY_MODULE_NAME);
generateDoc = VALUE_ENABLE.equals(options.get(KEY_GENERATE_DOC_NAME));
}
if (StringUtils.isNotEmpty(moduleName)) {
moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", "");
...
} else {
//沒有配置 moduleName,就報錯!
logger.error(NO_MODULE_NAME_TIPS);
throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log.");
}
}
...
//設置關注的配置項,其中KEY_MODULE_NAME就是在.gradle中配置的
@Override
public Set<String> getSupportedOptions() {
return new HashSet<String>() {{
this.add(KEY_MODULE_NAME);
this.add(KEY_GENERATE_DOC_NAME);
}};
}
}
BaseProcessor主要是在init()中做了各種工具的初始化,同時獲取了key爲 AROUTER_MODULE_NAME 的配置項 moduleName——module名字。我們注意重寫的getSupportedOptions()方法,它和前面的介紹的@SupportedOptions作用是一致的。 AROUTER_MODULE_NAME 就是路由所在module的gradle文件中配置的:
拿到moduleName有什麼作用呢?上一篇中提到的 根幫助類、Provider幫助類、攔截器幫助類 的類名就需要moduleName構成:
2.2.2 整體流程
@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor {
private Map<String, Set<RouteMeta>> groupMap = new HashMap<>(); // ModuleName and routeMeta.
private Map<String, String> rootMap = new TreeMap<>(); // Map of root metas, used for generate class file in order.
private TypeMirror iProvider = null;
private Writer docWriter; // Writer used for write doc
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
...
iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
...
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
try {
this.parseRoutes(routeElements);
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
定義了兩個Map:
- groupMap,此module所有路由組的信息。key爲路由的group,value是此group下所有路由元信息。用於生成 組幫助類
- rootMap,key是group,value是組幫助類的類名,用於生成 根幫助類
在init()中使用elementUtils獲取了 IProvider接口的類型,用於後面判斷一個類元素是否是IProvider的實現類。
在process()中獲取了添加了@Route的所有Element,然後調用parseRoutes()開始解析。
2.2.3 核心邏輯
在上篇中介紹了 根幫助類的loadInfo方法體內,是用接收的map來put當前module所有路由組幫助類的class。所以我們可以猜想應該是先創建了所有的組幫助類之後,才創建的根幫助類,並且一個module只有一個根幫助類。而組幫助類,則應該是先遍歷此module所有 @Route註解類,找到相同group的路由創建組幫助類。
另外這裏有個問題是,Java文件要如何生成呢?這裏就要介紹 javepoet 這個庫了:
- JavaPoet是一款可以自動生成Java文件的第三方依賴
- 簡潔易懂的API,上手快
- 讓繁雜、重複的Java文件,自動化生成,提高工作效率,簡化流程
JavaPoet的常用類:
- TypeSpec,用於生成類、接口、枚舉對象的類
- MethodSpec,用於生成方法對象的類
- ParameterSpec,用於生成參數對象的類
- FieldSpec,用於配置生成成員變量的類
- ClassName,通過包名和類名生成的對象,在JavaPoet中相當於爲其指定Class
- ParameterizedTypeName,通過MainClass和IncludeClass生成包含泛型的Class
- JavaFile,控制生成的Java文件的輸出的類
通常APT框架中生成Java類都是使用javepoet。這裏也不展開介紹,下面結合我添加的註釋即可理解。
下面就來看parseRoutes()方法:
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
------------ 一、準備工作:遍歷routeElements並創建對應RouteMeta,接着按group分組後 存入groupMap ---------------
if (CollectionUtils.isNotEmpty(routeElements)) {
// 清空rootMap
rootMap.clear();
//準備好會用到的元素類型,用於後面的判斷
TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
TypeMirror type_Service = elementUtils.getTypeElement(SERVICE).asType();
TypeMirror fragmentTm = elementUtils.getTypeElement(FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// ARouter的相關接口
TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
ClassName routeTypeCn = ClassName.get(RouteType.class);
//幫助類:就是使用javaPoet生成的所有用於 收集所有路由等信息的類。
//創建一個參數類型:用於存所有 組幫助類class 的Map,Map<String, Class<? extends IRouteGroup>>
ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
)
);
//創建一個參數類型:用於存某個分組內的所有路由,Map<String, RouteMeta>
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
//創建輸入參數的名字,也就是幾種幫助類loadInto方法的參數名
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build();
//創建方法builder:根幫助類loadInto方法,loadInto(Map<String, Class<? extends IRouteGroup>> routes)
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(rootParamSpec);
//遍歷routeElements,創建對應類型的RouteMeta,並分組後存入 groupMap 中,統計後最後生成根幫助類
for (Element element : routeElements) {
TypeMirror tm = element.asType();
Route route = element.getAnnotation(Route.class);
RouteMeta routeMeta;
//是 Activity 或者 Fragment
if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
// 獲取被@Autowired註解的變量
Map<String, Integer> paramsType = new HashMap<>();
Map<String, Autowired> injectConfig = new HashMap<>();
injectParamCollector(element, paramsType, injectConfig);
//對應類型的RouteMeta
if (types.isSubtype(tm, type_Activity)) {
routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
} else {
routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);
}
routeMeta.setInjectConfig(injectConfig);
} else if (types.isSubtype(tm, iProvider)) {
//是IProvider實現類
routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
} else if (types.isSubtype(tm, type_Service)) {
//是Service
routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
} else {
throw new RuntimeException("The @Route is marked on unsupported class, look at [" + tm.toString() + "].");
}
//按group分類 然後存入groupMap,一個value就是同group的所有路由
categories(routeMeta);
}
//Provider幫助類的 的 loadInto 方法:loadInto(Map<String,RouteMeta> providers)
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
Map<String, List<RouteDoc>> docSource = new HashMap<>();
------------ 二、遍歷groupMap:創建 組幫助類文件 並把類名存入rootMap、構建Provider幫助類方法體語句 ---------------
//遍歷groupMap:創建 組幫助類文件 並把類名存入rootMap、構建Provider幫助類方法體語句
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
String groupName = entry.getKey();
//組幫助類的 的 loadInto 方法
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(groupParamSpec);
...
Set<RouteMeta> groupData = entry.getValue(); //groupData,一個組 的所有路由
//遍歷同組路由:構建Provider幫助類loadInto方法體語句、組幫助類 的方法體語句
for (RouteMeta routeMeta : groupData) {
ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());
switch (routeMeta.getType()) {
case PROVIDER:
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
for (TypeMirror tm : interfaces) {
if (types.isSameType(tm, iProvider)) {//直接實現自IProvider接口
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
(routeMeta.getRawType()).toString(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
// //直接實現自IProvider的子接口
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
tm.toString(), // So stupid, will duplicate only save class name.
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
}
}
break;
default:
break;
}
//參數
...
//組幫助類 的方法體內的語句
loadIntoMethodOfGroupBuilder.addStatement(
"atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap<String, Integer>(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
routeMeta.getPath(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath().toLowerCase(),
routeMeta.getGroup().toLowerCase());
...
}
//創建 實現自IRouteGroup的 組幫助類文件(有多個,每個分組一個幫助類,每個幫助內 含有同組的activity、fragment、IProvider)
String groupFileName = NAME_OF_GROUP + groupName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(groupFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IRouteGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfGroupBuilder.build())
.build()
).build().writeTo(mFiler);
//rootMap 存入了所有的組幫助類
rootMap.put(groupName, groupFileName);
}
--------------------- 三、創建根幫助類、創建Provider幫助類 ------------------------
//遍歷rootMap,創建 根幫助類 方法體語句
if (MapUtils.isNotEmpty(rootMap)) {
for (Map.Entry<String, String> entry : rootMap.entrySet()) {
loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
}
}
...
//創建 Provider幫助類 文件(只有一個,這個是包含所有服務的幫助類)
String providerMapFileName = NAME_OF_PROVIDER + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(providerMapFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IProviderGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfProviderBuilder.build())
.build()
).build().writeTo(mFiler);
//創建 根幫助類文件(只有一個)
String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(rootFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT)))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfRootBuilder.build())
.build()
).build().writeTo(mFiler);
}
}
(以上定義參數、創建方法體語句、生成Java類 就是使用的javapoet相關API)
如上所示,(忽略了參數處理以及路由Doc邏輯)總共有三個步驟:
- 準備工作,遍歷routeElements並創建對應RouteMeta,接着按group分組後 存入 groupMap
- 遍歷groupMap,創建 組幫助類文件 並把類名存入rootMap、構建Provider幫助類方法體語句
- 遍歷rootMap生成方法體語句後創建根幫助類,創建Provider幫助類
整體邏輯和我們的猜想是一致的,邏輯也比較清晰。(對照實際生成的幫助類會更好理解)
到這裏,RouteProcessor就分析完了。 InterceptorProcessor、AutowiredProcessor,這裏不在分析,大家可以自行查看。
四、總結
本文首先介紹了對ARouter中使用的APT技術的理解——編譯時解析註解並生成Java文件,以及ARouter中的RouteProcessor是如何處理@Route註解並生成各種幫助類。
重點掌握對APT技術的理解,以及學習ARouter中幫助類的生成邏輯。這樣以後再遇到其他使用到APT技術的框架時就更容易掌握,更深入地,能夠在業務中嘗試使用APT技術解決問題。
另外,文中有三個未展開介紹的內容:註解的用法、Element相關知識、javapoet用法,它們是掌握和使用APT技術的基礎,但由於知識點比較固定,且不是本章重點,就未能詳細介紹。大家可在理解本篇內容的基礎上再去自行學習。
使用APT生成了幫助類,那麼要如何使用幫助類呢?這將在下一篇中介紹幫助類的使用以及涉及的AGP相關知識。
好了本篇就到這裏,歡迎繼續關注~
你的 點贊、評論,是對我的巨大鼓勵!
歡迎關注我的 公衆號 胡飛洋 ,文章更新可第一時間收到;
如果有問題或者想進羣,號內有加我微信的入口,我拉你進技術討論羣。在技術學習、個人成長的道路上,我們一起前進!