彙總篇章:
前言
前面對ARouter的源碼進行了分析,爲了分析的完整性(潔癖),接下來就對ARouter的插件進行分析,這一篇是基於ARouter的gradle插件及編譯的註解生成部分進行源碼分析,當然也會通過一些小例子來輔助我們理解這塊,順便提高我們擼代碼的逼格
插件啓動
在ARouter源碼的gradle項目中,arouter-gradle-plugin
這個module中用來做gradle插件,我們進入gradle插件的啓動類
public class PluginLaunch implements Plugin<Project> {
@Override
public void apply(Project project) {
def isApp = project.plugins.hasPlugin(AppPlugin)
首先判斷在application模塊下在進行插件生成註冊代碼
if (isApp) {
Logger.make(project)
Logger.i('Project enable arouter-register plugin')
def android = project.extensions.getByType(AppExtension)
def transformImpl = new RegisterTransform(project)
初始化arouter的配置信息
ArrayList<ScanSetting> list = new ArrayList<>(3)
list.add(new ScanSetting('IRouteRoot'))
list.add(new ScanSetting('IInterceptorGroup'))
list.add(new ScanSetting('IProviderGroup'))
填充目標配置信息集合
RegisterTransform.registerList = list
向application中進行插件註冊
android.registerTransform(transformImpl)
}
}
}
插件Transform轉換
[getName]:指明transform轉換的名稱,在ARouter中聲明爲"com.alibaba.arouter"
[isIncremental]:是否開啓增量編譯,在ARouter中關閉了增量編譯
[getInputTypes]:
【ContentType的數據類型】: CLASSES和RESOURCES兩種
CLASSES:包含了項目中.class文件和三方庫中的.class文件
RESOURCES:僅包含項目中的.class文件
在ARouter中inputType爲CLASSES
[getScopes]:要處理的.class文件的範圍
【Scope的數據類型】: PROJECT、SUB_PROJECTS、EXTERNAL_LIBRARIES、PROJECT_LOCAL_DEPS、SUB_PROJECTS_LOCAL_DEPS等
在ARouter中getScope爲Scope.PROJECT,Scope.PROJECT_LOCAL_DEPS,Scope.SUB_PROJECTS,Scope.SUB_PROJECTS_LOCAL_DEPS,Scope.EXTERNAL_LIBRARIES
[transform]: 即轉換的主要邏輯實現
接下來就着重分析transform,
-
第一部分掃描jar文件是:
-
獲取jar的絕對路徑的md5值
-
使用OutputProvider輸出名爲 jar名稱_md5 值的文件
-
對非如下條件的:
進行jar文件掃描
-
ScanUtil的scanJar方法
將編譯的jar文件內
com/alibaba/android/arouter/routes/
所有的內容進行scanClass處理(scanClass放在後面統一分析);對於
com/alibaba/android/arouter/core/LogisticsCenter.class
在掃描完成後,生成註冊代碼進目標文件,同步RegisterTransform的fileContainsInitClass狀態可能有人對這塊有些疑問,其實這裏就牽扯到前面文章分析的LogisticsCenter中的loadRouterMap方法
這裏就動態的添加registerRouteRoot(目標類)
-
-
第二部分掃描class文件是:
這裏就是掃描所有的class內容,對符合條件的class進行scanClass操作
在scanClass中藉助ASM的classVisitor將符合條件的class存放在ScanSetting的list中
-
最後一部分
若scanJar方法掃描到LogisticsCenter,則執行更新代碼操作
遍歷ScanSetting的list去更新loadRouterMap的方法,實現方法在RegisterCodeGenerator中,關鍵代碼如下:
藉助ASM的ClassVisitor的visitMethod方法,在RouteMethodVisitor中進行插入代碼:
至此插件部分相關的就結束了
Compiler相關
前面贅述了在gradle插件中的工作,接下來就分析ARouter中註解處理部分,在這部分中主要處理三個註解處理類
AutowiredProcessor
這個類中,處理的就是我們在使用過程中@Autowired
的註解,幫我們對我們@Autowired註解修飾的變量進行賦值
在該註解處理器中總共可分爲註解分類識別和代碼生成兩個功能
-
註解分類識別
在該部分中,將roundEnvironment中獲取的所有的Autowired進行分類
將需要注入的成員變量的相關信息存放在名爲parentAndChild的容器內
-
代碼生成
generateHelper
首先獲取如下類信息: com.alibaba.android.arouter.facade.template.ISyringe com.alibaba.android.arouter.facade.service.SerializationService com.alibaba.android.arouter.facade.template.IProvider android.app.Activity android.app.Fragment android.support.v4.app.Fragment
創建一個方法帶Override註解名爲inject且帶有一個Object類型參數的Public方法 創建一個實現了ISyringe接口名爲[註解所在類名稱$$ARouter$$Autowired]的Public類
添加一個名爲SerializationService的private成員變量 在剛纔創建的inject方法中添加如下兩行代碼: serializationService = ARouter.getInstance().navigation(SerializationService.class); 註解所在類 substitute = (註解所在類)target;
-
情況一:
遍歷當前類內所有帶Autowired註解的成員變量 當成員變量的類型是IProvider時 如果Autowired註解的name沒有賦值則用類型跳轉,語句爲: substitute.成員變量名稱 = ARouter.getInstance().navigation(當前類.class); 否則添加如下語句: substitute.成員變量名稱 = ARouter.getInstance().build(Autowired註解的name).navigation(當前類.class); 該註解參數是否必傳,如果是必傳,則在inject添加如下語句: if (substitute.成員變量名稱 == null) { throw new RuntimeException("The field 成員變量名稱 is null, in class 當前類名稱 !"); }
-
情況二
buildStatement方法體過長,這裏只羅列一部分:
正常進入時 如果是Activity的,添加: substitute.成員變量名稱 = [如果是Serializable類型的則加上類型轉換]substitute.getIntent().getBooleanExtra(如果註解的name存在則用name沒有則用substitute.成員變量名稱) 如果是fragment的,則添加: substitute.成員變量名稱 = [如果是Serializable類型的則加上類型轉換]substitute.getArguments().getBoolean(如果註解的name存在則用name沒有則用substitute.成員變量名稱) 如果是OBJECT,則statement爲serializationService.parseObject 此時會添加如下代碼: if (null != serializationService) { substitute.成員變量名稱 = serializationService.parseObject(substitute.getIntent().getStringExtra(如果註解的name存在則用name沒有則用substitute.成員變量名稱), new com.alibaba.android.arouter.facade.model.TypeWrapper<當前歸屬類>(){}.getType()); } else { Log.e("ARouter::", "You want automatic inject the field 'substitute.成員變量名稱' in class 當前歸屬類 , then you should implement 'SerializationService' to support object auto inject!"); }
與之前的必傳項相同,以上就是對AutoProcessor的註解處理
-
RouteProcessor
核心註解處理類,之所以成爲核心註解處理類,ARouter的跳轉的關鍵實現就是這裏,廢話不多說直接開大
直接對Route方法進行parse處理
-
獲取Activity/Service/fragment/IRouteGroup/IRouteRoot類型
創建一個Map<String, Class<? extends IRouteGroup>>和一個Map<String, RouteMeta>
構建參數名爲: Map<String, Class<? extends IRouteGroup>> routes Map<String, RouteMeta> atlas Map<String, RouteMeta> providers 創建一個帶Override名爲loadInfo且帶有"Map<String, Class<? extends IRouteGroup>> routes"參數的public方法
根據element的類型,進行填充routeMeta
將RouteMeta放到對應的分組中
創建一個帶Override名爲loadInfo且帶有"Map<String, RouteMeta> providers"參數的public方法 創建一個存放route文檔的容器
對分組數據的處理
//遍歷分組數據 for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) { String groupName = entry.getKey(); //創建一個帶Override名爲loadInfo且帶有"Map<String, RouteMeta> atlas"參數的public方法 MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO) .addAnnotation(Override.class) .addModifiers(PUBLIC) .addParameter(groupParamSpec); //創建route信息的容器 List<RouteDoc> routeDocList = new ArrayList<>(); // 按組內的信息存放對應的組內 Set<RouteMeta> groupData = entry.getValue(); for (RouteMeta routeMeta : groupData) { //生成對應的routeMeta詳細內容 RouteDoc routeDoc = extractDocInfo(routeMeta); ClassName className = ClassName.get((TypeElement) routeMeta.getRawType()); switch (routeMeta.getType()) { case PROVIDER: //獲取provider類型的父類信息 List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces(); for (TypeMirror tm : interfaces) { routeDoc.addPrototype(tm.toString()); //找到iProvider類型 //在provider中添加如下語句: //" providers.put(掃描當前類信息, RouteMeta.build(RouteType.PROVIDER, 當前類.class, route路徑, 分組名稱, 等));" if (types.isSameType(tm, 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)) { 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; } // Make map body for paramsType StringBuilder mapBodyBuilder = new StringBuilder(); Map<String, Integer> paramsType = routeMeta.getParamsType(); Map<String, Autowired> injectConfigs = routeMeta.getInjectConfig(); if (MapUtils.isNotEmpty(paramsType)) { List<RouteDoc.Param> paramList = new ArrayList<>(); for (Map.Entry<String, Integer> types : paramsType.entrySet()) { mapBodyBuilder.append("put(\"").append(types.getKey()).append("\", ").append(types.getValue()).append("); "); RouteDoc.Param param = new RouteDoc.Param(); Autowired injectConfig = injectConfigs.get(types.getKey()); param.setKey(types.getKey()); param.setType(TypeKind.values()[types.getValue()].name().toLowerCase()); param.setDescription(injectConfig.desc()); param.setRequired(injectConfig.required()); paramList.add(param); } routeDoc.setParams(paramList); } String mapBody = mapBodyBuilder.toString(); //在參數名爲atlas的loadinfo方法中添加如下語句: //atlas.put(route路徑, RouteMeta.build(routeMeta類型, Test2Activity.class, route路徑", "test", new java.util.HashMap<String, Integer>(){{如果有參數}}等)); 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()); routeDoc.setClassName(className.toString()); routeDocList.add(routeDoc); }
向參數名爲routes的loadInto方法中添加 routes.put(分組名稱, 分組生成類.class);) 如果生成routeDoc,則輸出routeDoc
生成文件
將provider/root寫進本地文件 名稱爲:[ARouter$$Providers$$分組名稱] 名稱爲:[ARouter$$Root$$分組名稱]
到這裏就結束了
InterceptorProcessor
查找所有的Interceptor註解方法,按照其優先級存放
創建一個名爲[ARouter$$Interceptors$$分組名稱]
創建一個帶Override名爲loadInfo且帶有"Map<Integer, Class<? extends IInterceptor>> interceptors"參數的public方法
將掃描到所有interceptor在loadInfo方法中添加:
interceptors.put(interceptor優先級, intercepter所在類.class);
小結
以上就是所有註解部分和插件部分的內容了,請按照大綱中的流程來瀏覽不迷路