日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ARouter 源码历险记 (一)

發布時間:2025/4/16 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ARouter 源码历险记 (一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2019獨角獸企業重金招聘Python工程師標準>>>

目錄

ARouter 源碼歷險記 (一)

ARouter 源碼歷險記 (二)

ARouter 源碼歷險記 (三)

ARouter 源碼歷險記 (四)

ARouter 源碼歷險記 (五)

總綱

????????這里是云溪社區關于ARouter發布時候的博文,算得上是ARouter的總綱和指導思想。但是說實話,如果不接觸源碼,真的很難看懂這篇博文的深刻內容,所以筆者會在研究完ARouter之后再轉載這篇博文研讀一番。

????? ? PS: 源碼在這里

? ? ? ??

????? ? 代碼結構如上,其中 app 是唯一的可運行的demo。

????????arouter-annotation存放了定義的annotation,并且存放了兩個類

????????????? ? RouteType ? 一個枚舉類型,用來表示route的類型。

????????????? ? RouteMeta ?其實就是一個Bean ,用來存放route的基本信息,下面會看到

? ? ? ? aroute-api 核心api和處理代碼

????? ? test-module-1 demo中需要用到的測試內容,沒啥大東西。

RouteProcessor

????? ? 從非常明了作用的類開始讀起,此類的作用當然是使用Processor自動生成代碼,如果對processor不熟,可以參看這里和這里。

? ? ? ? 首先是Init方法(log部分和一些不關鍵部分使用...代替)

public synchronized void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);mFiler = processingEnv.getFiler(); // Generate class.typeUtil = processingEnv.getTypeUtils(); // Get type utils.elementUtil = processingEnv.getElementUtils(); // Get class meta.logger = new Logger(processingEnv.getMessager()); // Package the log utils.// Attempt to get user configuration [moduleName]Map<String, String> options = processingEnv.getOptions();if (MapUtils.isNotEmpty(options)) {moduleName = options.get(KEY_MODULE_NAME);}if (StringUtils.isNotEmpty(moduleName)) {moduleName = moduleName.replaceAll("[^0-9a-zA-Z_]+", "");...} else {...throw new RuntimeException("ARouter::Compiler >>> No module name, for more information, look at gradle log.");}iProvider = elementUtil.getTypeElement(Consts.IPROVIDER).asType();...}

????? ? 總得來說代碼還是比較清晰的,大概做了三件事情

????? ? 1. 獲取工具類

????? ? 2.獲取傳入的 moduleName 參數,并且去掉不合法字符(因為需要拼接類名),如果沒有傳入直接報錯

????? ? 3.獲取 com.alibaba.android.arouter.facade.template.IProvider?類的 TypeMirror 。該類的作用會在后續研讀中一點點揭開面紗。

????? ? 然后是process方法:

public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {if (CollectionUtils.isNotEmpty(annotations)) {Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);try {logger.info(">>> Found routes, start... <<<");this.parseRoutes(routeElements);} catch (Exception e) {logger.error(e);}return true;}return false;}

????? ? 該方法實際上就是找出所有使用Route修飾的節點元素(Element),然后調用parseRoutes方法。

private void parseRoutes(Set<? extends Element> routeElements) throws IOException {if (CollectionUtils.isNotEmpty(routeElements)) {rootMap.clear();// Fantastic fourTypeElement type_Activity = elementUtil.getTypeElement(ACTIVITY);TypeElement type_Service = elementUtil.getTypeElement(SERVICE);// Interface of ARouter.TypeElement type_IRouteGroup = elementUtil.getTypeElement(IROUTE_GROUP);TypeElement type_IProviderGroup = elementUtil.getTypeElement(IPROVIDER_GROUP);ClassName routeMetaCn = ClassName.get(RouteMeta.class);ClassName routeTypeCn = ClassName.get(RouteType.class);/*Build input type, format as :```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));/*Build input param name.*/ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); // Ps. its param type same as groupParamSpec!/*Build method : 'loadInto'*/MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO).addAnnotation(Override.class).addModifiers(PUBLIC).addParameter(rootParamSpec);.......................}}

????? ? 一段一段來解讀,首先獲取android中原生 activity 和 service 的 TypeElement。

????? ? PS,如果你懵了,你可能需要跟著這里走一遍。

? ? ? ? 然后還要獲取 com.alibaba.android.arouter.facade.template.IRouteGroup 以及?com.alibaba.android.arouter.facade.template.IProviderGroup 的 TypeElement。

? ? ? ? 之后就是構建java文件的一些前置操作了(使用的是javapoet).

????? ? 創建了 參數? Map<String, Class<? extends IRouteGroup>> routes

? ? ? ? 創建了 參數 ?Map<String, RouteMeta> atlas

????? ? 創建了參數 ?Map<String, RouteMeta> providers

????? 然后創建了一個方法 @Override public loadInto(Map<String, Class<? extends IRouteGroup>> routes){} (暫稱為方法 A )方法中內容尚未填充。

private void parseRoutes(Set<? extends Element> routeElements) throws IOException {............// Follow a sequence, find out metas of group first, generate java file, then statistics them as root.for (Element element : routeElements) {TypeMirror tm = element.asType();Route route = element.getAnnotation(Route.class);RouteMeta routeMete = null;if (typeUtil.isSubtype(tm, type_Activity.asType())) { // Activitylogger.info(">>> Found activity route: " + tm.toString() + " <<<");// Get all fields annotation by @AutowiredMap<String, Integer> paramsType = new HashMap<>();for (Element field : element.getEnclosedElements()) {if (field.getKind().isField() && field.getAnnotation(Autowired.class) != null && !typeUtil.isSubtype(field.asType(), iProvider)) {// It must be field, then it has annotation, but it not be provider.Autowired paramConfig = field.getAnnotation(Autowired.class);paramsType.put(StringUtils.isEmpty(paramConfig.name()) ? field.getSimpleName().toString() : paramConfig.name(), TypeUtils.typeExchange(field.asType()));}}routeMete = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);} else if (typeUtil.isSubtype(tm, iProvider)) { // IProviderlogger.info(">>> Found provider route: " + tm.toString() + " <<<");routeMete = new RouteMeta(route, element, RouteType.PROVIDER, null);} else if (typeUtil.isSubtype(tm, type_Service.asType())) { // Servicelogger.info(">>> Found service route: " + tm.toString() + " <<<");routeMete = new RouteMeta(route, element, RouteType.parse(SERVICE), null);}categories(routeMete);// if (StringUtils.isEmpty(moduleName)) { // Hasn't generate the module name.// moduleName = ModuleUtils.generateModuleName(element, logger);// }}........}

? ? ? ? ?循環遍歷分析route修飾的節點,如果是IProvider的子類或者service的子類,那么直接將注解類型對象Route,被注解對象的Element,以及route類型(見RouteType枚舉)存入。

????? ? 如果節點是activity的子類,那么還需要額外獲取所有該類中被使用Autowired注解的非IProvider類型的字段(應該是用來做注入的,沒看完源碼,還不清楚。)存儲為map <字段名,字段類型的枚舉值(見TypeUtils)> ? ?注意,以上Route分類后面將會頻繁用到

private void categories(RouteMeta routeMete) {if (routeVerify(routeMete)) {logger.info(">>> Start categories, group = " + routeMete.getGroup() + ", path = " + routeMete.getPath() + " <<<");Set<RouteMeta> routeMetas = groupMap.get(routeMete.getGroup());if (CollectionUtils.isEmpty(routeMetas)) {Set<RouteMeta> routeMetaSet = new TreeSet<>(new Comparator<RouteMeta>() {@Overridepublic int compare(RouteMeta r1, RouteMeta r2) {try {return r1.getPath().compareTo(r2.getPath());} catch (NullPointerException npe) {logger.error(npe.getMessage());return 0;}}});routeMetaSet.add(routeMete);groupMap.put(routeMete.getGroup(), routeMetaSet);} else {routeMetas.add(routeMete);}} else {logger.warning(">>> Route meta verify error, group is " + routeMete.getGroup() + " <<<");}}/*** Verify the route meta** @param meta raw meta*/private boolean routeVerify(RouteMeta meta) {String path = meta.getPath();if (StringUtils.isEmpty(path) || !path.startsWith("/")) { // The path must be start with '/' and not empty!return false;}if (StringUtils.isEmpty(meta.getGroup())) { // Use default group(the first word in path)try {String defaultGroup = path.substring(1, path.indexOf("/", 1));if (StringUtils.isEmpty(defaultGroup)) {return false;}meta.setGroup(defaultGroup);return true;} catch (Exception e) {logger.error("Failed to extract default group! " + e.getMessage());return false;}}return true;}

????? ? PS:考慮了下,還是把代碼貼上來吧,至少不會顯得全文都是空洞的文字,笑

? ? ? ? 首先驗證RouteMeta,Route的path不能為空,并且需要以“/”開頭,并且至少需要包含兩個"/"。如果沒有設置group,默認使用 path 的第一個單詞(前兩個"/"之間的內容)作為默認分組。

????? ? 然后根據group分組,放入HashMap<String , Set<RouteMeta>> groupMap中。相同的group會根據path進行字典排序,放入TreeSet中。

? ? ? ? 所以,以上代碼就是遍歷Route注解的Element,獲取基本信息,根據分組進行存放。

????? ? 為了便于理解,接下去的代碼有些穿插,可能會使你對源碼的結構產生迷惑,請自行結合源碼理解。

private void parseRoutes(Set<? extends Element> routeElements) throws IOException {............MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO).addAnnotation(Override.class).addModifiers(PUBLIC).addParameter(providerParamSpec);// Start generate java source, structure is divided into upper and lower levels, used for demand initialization.for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {String groupName = entry.getKey();..........// Build group method bodySet<RouteMeta> groupData = entry.getValue();for (RouteMeta routeMeta : groupData) {switch (routeMeta.getType()) {case PROVIDER: // Need cache provider's super classList<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();for (TypeMirror tm : interfaces) {if (typeUtil.isSubtype(tm, iProvider)) {// This interface extend the IProvider, so it can be used for mark providerloadIntoMethodOfProviderBuilder.addStatement("providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",tm.toString().substring(tm.toString().lastIndexOf(".") + 1), // Spite unuseless namerouteMetaCn,routeTypeCn,ClassName.get((TypeElement) routeMeta.getRawType()),routeMeta.getPath(),routeMeta.getGroup());}}break;default:break;}...............}............}.........// Wirte provider into diskString 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);............}}

????? ? 創建一個方法 @Override public void loadInto(Map<String, RouteMeta> providers){} 暫時稱為 B

? ? ? ? 然后遍歷上一步生成的groupMap中的每一個RouteMeta , 如果該RoteMeta是用來記錄IProvider信息的(看上文,就是Route注解的類是IProvider的子類),遍歷該類所有直接繼承的接口,如果該接口是IProvider或者其子類(代號 X),那么就在剛剛創建的loadInto B(不是最開始創建的那個方法A )方法中添加如類似如下語句:

providers.put("X的類名", RouteMeta.build(RouteType.PROVIDER, 被Route注解的類, Route的path, Route的group, null, Route的priority, Route的extra));

? ? ? ? 于是 B 方法的任務就是將所有被Route注解的IProvider類型類根據如上語句,存入傳入的參數中。

? ? 但是以上代碼是可以發現潛在問題的,如果有一個IProvider的子類,比如HelloService。HelloServiceImpl1和HelloServiceImpl2都實現了該接口,并且都是用了@Route注解,那么這里就差插入類似如下代碼:

在同一個map中put了兩個key相同的鍵值對!這回導致RouteMeta的丟失。 所以在實際使用中,我們需要避免如上這樣的繼承情況!

????????然后生成一個java文件,包為 ?com.alibaba.android.arouter.routes 類名為 ARouter$$Providers$$XXX (XXX為最開始傳入并處理的參數moduleName),繼承 IProviderGroup 接口,該類中只有一個方法,就是 B。

????? ? 繼續分析代碼段:

private void parseRoutes(Set<? extends Element> routeElements) throws IOException {...................// Start generate java source, structure is divided into upper and lower levels, used for demand initialization.for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {String groupName = entry.getKey();MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO).addAnnotation(Override.class).addModifiers(PUBLIC).addParameter(groupParamSpec);// Build group method bodySet<RouteMeta> groupData = entry.getValue();for (RouteMeta routeMeta : groupData) {................// Make map body for paramsTypeStringBuilder mapBodyBuilder = new StringBuilder();Map<String, Integer> paramsType = routeMeta.getParamsType();if (MapUtils.isNotEmpty(paramsType)) {for (Map.Entry<String, Integer> types : paramsType.entrySet()) {mapBodyBuilder.append("put(\"").append(types.getKey()).append("\", ").append(types.getValue()).append("); ");}}String mapBody = mapBodyBuilder.toString();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.get((TypeElement) routeMeta.getRawType()),routeMeta.getPath().toLowerCase(),routeMeta.getGroup().toLowerCase());}// Generate groupsString 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);logger.info(">>> Generated group: " + groupName + "<<<");rootMap.put(groupName, groupFileName);}.............}

?遍歷groupMap中的所有Group組,創建方法 @Override public void loadInto(Map<String, RouteMeta> atlas) 暫時稱為C

?接著遍歷該group組中的所有RouteMate,每個RouteMate?都會生成一個如下語句插入方法C中

atlas.put(Route的Path, RouteMeta.build(RouteMate類型, 注解類.class, Route的Path, Route的Group, 記錄Autowired的map, Route的priority, Route的extra));

其中map類似如下創建

?

new java.util.HashMap<String, Integer>(){{put("name", 18); put("boy", 0); put("age", 3); put("url", 18); }}

? ? ? 然后為該Group生成一個java文件,包名為?com.alibaba.android.arouter.routes 類名為 ARouter$$Group$$XXX (XXX為 該group組的 groupName),繼承接口 IRouteGroup ,方法就只有 C。

????? ? 最后還需要將 groupName : 該group生成的類名 的鍵值對存入rootMap中

????? ? 勝利就在眼前了,來看最后一段代碼

if (MapUtils.isNotEmpty(rootMap)) {// Generate root meta by group name, it must be generated before root, then I can findout the class of group.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()));}}.......// Write root meta into disk.String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;JavaFile.builder(PACKAGE_OF_GENERATE_FILE,TypeSpec.classBuilder(rootFileName).addJavadoc(WARNING_TIPS).addSuperinterface(ClassName.get(elementUtil.getTypeElement(ITROUTE_ROOT))).addModifiers(PUBLIC).addMethod(loadIntoMethodOfRootBuilder.build()).build()).build().writeTo(mFiler);

? ? ? ?還記得方法A嗎?創建之后被冷落了好久,回上去看下吧!!

????? ? 遍歷rootMap, 在方法A中類似添加如下語句

? ? ? ? routes.put(groupName, group對應的類名.class);

? ? ? ? 最后,生成一個java文件,包和之前生成的文件一樣,類名為 ARouter$$Root$$XXX (xxx表示moduleName),繼承接口IRouteRoot,添加方法 A!

總結

????? ? 至此RouteProcessor的內容就完全分析完成了,我們知道了該處理器會生成一些文件,就拿demo來說,它一共會生成如下文件

????????

????? ? arouter-api模塊中自導生成的類

????????

? ? PS:除了ARouter$$Interceptors$$app

? ? app 模塊生成的類

? PS:除了ARouter$$Interceptors$$testmodule1

? ? test-module-1模塊生成的代碼

? ? 結合具體生成的代碼內容來看源碼就非常容易理解了。

? ? PS:實際上,我們直接看生成的文件也許就能了解Processor所做的內容,但是我還是進行了詳細的記錄,目的還是想通過源代碼潛移默化提高自己,愿好!

? ? 下一篇我們將繼續在 ARouter 中探險。

轉載于:https://my.oschina.net/zzxzzg/blog/861510

總結

以上是生活随笔為你收集整理的ARouter 源码历险记 (一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。