【Android 组件化】路由组件 ( 生成 Root 类记录模块中的路由表 )
文章目錄
- 一、Root 表作用
- 二、生成 Root 表
- 三、完整注解處理器代碼 及 生成的 Java 代碼 ( 僅供參考 )
- 1、注解處理器代碼
- 2、app 模塊中的注解類生成的 Java 源碼
- 3、library2 模塊中的注解類生成的 Java 源碼
- 四、博客資源
組件化系列博客 :
- 【Android 組件化】從模塊化到組件化
- 【Android 組件化】使用 Gradle 實現組件化 ( Gradle 變量定義與使用 )
- 【Android 組件化】使用 Gradle 實現組件化 ( 組件模式與集成模式切換 )
- 【Android 組件化】使用 Gradle 實現組件化 ( 組件 / 集成模式下的 Library Module 開發 )
- 【Android 組件化】路由組件 ( 路由組件結構 )
- 【Android 組件化】路由組件 ( 注解處理器獲取被注解的節點 )
- 【Android 組件化】路由組件 ( 注解處理器中使用 JavaPoet 生成代碼 )
- 【Android 組件化】路由組件 ( 注解處理器參數選項設置 )
- 【Android 組件化】路由組件 ( 構造路由表中的路由信息 )
- 【Android 組件化】路由組件 ( 使用 JavaPoet 生成路由表類 )
- 【Android 組件化】路由組件 ( 組件間共享的服務 )
一、Root 表作用
注解處理器 為每個 Module 模塊生成一個路由表 , 該模塊下凡是被 @Route 標注的路由節點都在該路由表中維護 ;
package kim.hsl.router;import java.lang.Override; import java.lang.String; import java.util.Map; import kim.hsl.library2.StringService; import kim.hsl.route_core.template.IRouteGroup; import kim.hsl.router_annotation.model.RouteBean;public class Router_Group_library2 implements IRouteGroup {@Overridepublic void loadInto(Map<String, RouteBean> atlas) {atlas.put("library2", new RouteBean(RouteBean.Type.ISERVICE, StringService.class, "/library2/StringService", "library2"));} }模塊名稱是 app , 則路由表的名稱是 Router_Group_app.java ;
一個模塊中的路由表可能有多個 , 需要為若干路由表再生成一個 Root 表, 用于作為路由表的導航 ;
生成的 Root 表樣式 : 其中 “app” 是組名 , Router_Group_app.class 是 app 組對應的路由表類 ;
package kim.hsl.router;import java.lang.Class; import java.lang.Override; import java.lang.String; import java.util.Map; import kim.hsl.route_core.template.IRouteGroup; import kim.hsl.route_core.template.IRouteRoot;public class Router_Root_app implements IRouteRoot {@Overridepublic void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {routes.put("app", Router_Group_app.class);} }二、生成 Root 表
定義 IRouteRoot 接口 , 所有的 Root 類都實現該接口 , 該接口定義在 route-core 模塊中 ;
package kim.hsl.route_core.template;import java.util.Map;public interface IRouteRoot {void loadInto(Map<String, Class<? extends IRouteGroup>> routes); }IRoot 接口定義位置 :
首先 , 獲取 IRouteGroup 和 IRouteRoot 接口的節點 ;
// 獲取 kim.hsl.route_core.template.IRouteGroup 類節點 TypeElement iRouteGroup = mElementUtils.getTypeElement("kim.hsl.route_core.template.IRouteGroup"); // 獲取 kim.hsl.route_core.template.IRouteRoot 類節點 TypeElement iRouteRoot = mElementUtils.getTypeElement("kim.hsl.route_core.template.IRouteRoot");其次 , 生成 loadInto 函數的參數 Map<String, Class<? extends IRouteGroup>> routes ;
// 生成參數類型名稱 // Map<String,Class<? extends IRouteGroup>> routes> ParameterizedTypeName routesTypeName = ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(String.class),ParameterizedTypeName.get(ClassName.get(Class.class),WildcardTypeName.subtypeOf(ClassName.get(iRouteGroup))) );// 生成參數 // Map<String,Class<? extends IRouteGroup>> routes> routes ParameterSpec rootParameterSpec = ParameterSpec.builder(routesTypeName, "routes").build();再次 , 生成函數及函數體 ;
// 生成函數 // public void loadInfo(Map<String,Class<? extends IRouteGroup>> routes> routes) MethodSpec.Builder loadIntoMethodBuilder = MethodSpec.methodBuilder("loadInto").addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(rootParameterSpec);// 生成函數體 for (Map.Entry<String, String> entry : mRootMap.entrySet()) {loadIntoMethodBuilder.addStatement("routes.put($S, $T.class)",entry.getKey(),ClassName.get("kim.hsl.router", entry.getValue())); }最后 , 生成 Router_Root_app 類 , 并寫出到文件中 ;
// 生成 Root 類 String rootClassName = "Router_Root_" + mModuleName;// 創建 Java 類 TypeSpec typeSpec = TypeSpec.classBuilder(rootClassName).addSuperinterface(ClassName.get(iRouteRoot)).addModifiers(PUBLIC).addMethod(loadIntoMethodBuilder.build()).build();// 生成 Java 源文件 JavaFile javaFile = JavaFile.builder("kim.hsl.router", typeSpec).build();// 寫出到文件中 try {javaFile.writeTo(mFiler); } catch (IOException e) {e.printStackTrace(); }三、完整注解處理器代碼 及 生成的 Java 代碼 ( 僅供參考 )
1、注解處理器代碼
package kim.hsl.router_compiler;import com.google.auto.service.AutoService; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.WildcardTypeName;import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeMap;import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Filer; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedOptions; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic;import kim.hsl.router_annotation.Route; import kim.hsl.router_annotation.model.RouteBean;import static javax.lang.model.element.Modifier.PUBLIC;// 注解處理器接收的參數 @SupportedOptions("moduleName") // 自動注冊注解處理器 @AutoService(Processor.class) // 支持的注解類型 @SupportedAnnotationTypes({"kim.hsl.router_annotation.Route"}) // 支持的 Java 版本 @SupportedSourceVersion(SourceVersion.RELEASE_8) public class RouterProcessor extends AbstractProcessor {/*** 注解處理器中使用 Messager 對象打印日志*/private Messager mMessager;/*** 用于寫出生成的 Java 代碼*/private Filer mFiler;/*** 注解節點工具*/private Elements mElementUtils;/*** 類工具*/private Types mTypeUtils;/*** 獲取的 moduleName 參數*/private String mModuleName;/*** 管理路由信息* 鍵 ( Key ) : 路由分組名稱* 值 ( Value ) : 路由信息集合*/private HashMap<String, ArrayList<RouteBean>> mGroupMap = new HashMap<>();/*** 管理 路由表信息* 鍵 ( Key ) : 組名* 值 ( Value ) : 類名*/private Map<String, String> mRootMap = new TreeMap<>();/*** 該函數在初始化時調用 , 相當于構造函數* @param processingEnvironment*/@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);// 獲取打印日志接口this.mMessager = processingEnvironment.getMessager();// 測試日志打印mMessager.printMessage(Diagnostic.Kind.NOTE, "Messager Print Log");this.mFiler = processingEnvironment.getFiler();this.mElementUtils = processingEnvironment.getElementUtils();this.mTypeUtils = processingEnvironment.getTypeUtils();// 獲取 moduleName 參數// 先獲取 注解處理器 選項Map<String, String> options = processingEnvironment.getOptions();if (options != null){mModuleName = options.get("moduleName");mMessager.printMessage(Diagnostic.Kind.NOTE, "打印 moduleName 參數 : " + mModuleName);}}/*** 該函數在注解處理器注冊時自動執行, 是處理注解的核心函數** Set<? extends TypeElement> set 參數 : 該集合表示使用了相關注解的節點的集合** @param set* @param roundEnvironment* @return*/@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {if (set == null || set.isEmpty()){// 如果沒有檢測到注解 , 直接退出return false;}// 獲取被 @Route 注解的節點// 這些 注解節點 都是類節點 , TypeElement 類型的Set<? extends Element> routeElements = roundEnvironment.getElementsAnnotatedWith(Route.class);generateRouteClass(routeElements);// 生成 路由組件 分組表 對應的 Java 路由表 類generateGroupTable();// 生成 Root 路由表 , 組名 <-> 路由表類generateRootTable();return true;}/*** 生成 Root 表*/private void generateRootTable() {// 獲取 kim.hsl.route_core.template.IRouteGroup 類節點TypeElement iRouteGroup = mElementUtils.getTypeElement("kim.hsl.route_core.template.IRouteGroup");// 獲取 kim.hsl.route_core.template.IRouteRoot 類節點TypeElement iRouteRoot = mElementUtils.getTypeElement("kim.hsl.route_core.template.IRouteRoot");// 生成參數類型名稱// Map<String,Class<? extends IRouteGroup>> routes>ParameterizedTypeName routesTypeName = ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(String.class),ParameterizedTypeName.get(ClassName.get(Class.class),WildcardTypeName.subtypeOf(ClassName.get(iRouteGroup))));// 生成參數// Map<String,Class<? extends IRouteGroup>> routes> routesParameterSpec rootParameterSpec = ParameterSpec.builder(routesTypeName, "routes").build();// 生成函數// public void loadInfo(Map<String,Class<? extends IRouteGroup>> routes> routes)MethodSpec.Builder loadIntoMethodBuilder = MethodSpec.methodBuilder("loadInto").addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(rootParameterSpec);// 生成函數體for (Map.Entry<String, String> entry : mRootMap.entrySet()) {loadIntoMethodBuilder.addStatement("routes.put($S, $T.class)",entry.getKey(),ClassName.get("kim.hsl.router", entry.getValue()));}// 生成 Root 類String rootClassName = "Router_Root_" + mModuleName;// 創建 Java 類TypeSpec typeSpec = TypeSpec.classBuilder(rootClassName).addSuperinterface(ClassName.get(iRouteRoot)).addModifiers(PUBLIC).addMethod(loadIntoMethodBuilder.build()).build();// 生成 Java 源文件JavaFile javaFile = JavaFile.builder("kim.hsl.router", typeSpec).build();// 寫出到文件中try {javaFile.writeTo(mFiler);} catch (IOException e) {e.printStackTrace();}}/*** 生成 路由組件 分組表 對應的 Java 類*/private void generateGroupTable() {// 獲取要生成的類 需要實現的接口節點TypeElement iRouteGroup = mElementUtils.getTypeElement("kim.hsl.route_core.template.IRouteGroup");// 打印類節點全類名mMessager.printMessage(Diagnostic.Kind.NOTE,"打印 路由表 需要實現的接口節點 iRouteGroup : " + iRouteGroup.getQualifiedName());// 生成參數類型 Map<String, RouteBean> atlasParameterizedTypeName atlasType = ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(String.class),ClassName.get(RouteBean.class));// 生成參數 Map<String, RouteBean> atlasParameterSpec atlasValue = ParameterSpec.builder(atlasType, "atlas").build();// 遍歷 HashMap<String, ArrayList<RouteBean>> mGroupMap = new HashMap<>() 路由分組// 為每個 路由分組 創建一個類for (Map.Entry<String, ArrayList<RouteBean>> entry : mGroupMap.entrySet()){// 創建函數 loadIntoMethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("loadInto").addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).addParameter(atlasValue);// 函數體中的代碼生成// 獲取 ArrayList<RouteBean> 數據ArrayList<RouteBean> groupRoutes = entry.getValue();// 組名String groupName = "";// 生成函數體代碼for (RouteBean routeBean : groupRoutes){// 獲取組名groupName = routeBean.getRouteGroup();// $S 表示字符串// $T 表示類// $L 表示字面量 , 原封不動的字符串替換methodBuilder.addStatement("atlas.put($S, new $T($T.$L, $T.class, $S, $S))",// $S 字符串 : "main"routeBean.getRouteGroup(),// $T 類名 : RouteBeanClassName.get(RouteBean.class),// $T 類名 : TypeClassName.get(RouteBean.Type.class),// $L 字面量 : ACTIVITYrouteBean.getType(),// $T 類名 : kim.hsl.component.MainActivity 類ClassName.get((TypeElement) routeBean.getElement()),// $S 字符串 : "/app/MainActivity"routeBean.getRouteAddress(),// $S 字符串 : "app"routeBean.getRouteGroup());}// 創建類// 構造類名 Router_Group_mainString groupClassName = "Router_Group_" + groupName;// 創建類TypeSpec typeSpec = TypeSpec.classBuilder(groupClassName).addSuperinterface(ClassName.get(iRouteGroup)).addModifiers(PUBLIC).addMethod(methodBuilder.build()).build();// 生成 Java 源碼文件JavaFile javaFile = JavaFile.builder("kim.hsl.router", typeSpec).build();// 將 Java 源文件寫出到相應目錄中try {mMessager.printMessage(Diagnostic.Kind.NOTE,"輸出文件 : " + groupClassName);javaFile.writeTo(mFiler);} catch (IOException e) {e.printStackTrace();mMessager.printMessage(Diagnostic.Kind.NOTE,"輸出文件出現異常");}finally {mMessager.printMessage(Diagnostic.Kind.NOTE,"輸出文件完畢");}// 統計路由表信息mRootMap.put(groupName, groupClassName);}}private void generateRouteClass(Set<? extends Element> routeElements) {// 獲取 android.app.Activity 類型的注解節點TypeElement activityElement = mElementUtils.getTypeElement("android.app.Activity");// 獲取 組件間共享服務 的接口, 該接口僅用于表示組件類型TypeElement iServiceElement = mElementUtils.getTypeElement("kim.hsl.route_core.template.IService");// 處理 @Route(path = "app/MainActivity") 節點for (Element element : routeElements) {// 獲取 Route 注解Route route = element.getAnnotation(Route.class);// 路由表中的單個路由對象RouteBean routeBean = null;// 判斷 typeMirror 注解節點是否是 Activity 類型if (mTypeUtils.isSubtype(element.asType(), activityElement.asType())) {// 該節點是 android.app.Activity 類型的routeBean = new RouteBean(RouteBean.Type.ACTIVITY, // 路由對象類型element, // 路由節點null, // 類對象route.path(), // 路由地址route.group()); // 路由組}else if (mTypeUtils.isSubtype(element.asType(), iServiceElement.asType())) {// 該節點是 kim.hsl.route_core.template.IService 類型的routeBean = new RouteBean(RouteBean.Type.ISERVICE, // 路由對象類型element, // 路由節點null, // 類對象route.path(), // 路由地址route.group()); // 路由組}else{// 該節點不是 android.app.Activity 類型的throw new RuntimeException("@Route 注解節點類型錯誤");}// 檢查路由地址checkRouteAddress(routeBean);// 打印路由信息mMessager.printMessage(Diagnostic.Kind.NOTE,"打印路由信息 : " + routeBean.toString());// 處理路由信息分組routeGroup(routeBean);}}/*** 處理路由信息分組* @param routeBean*/private void routeGroup(RouteBean routeBean) {// 首先從 groupMap 集合中獲取該分組的所有 路由信息ArrayList<RouteBean> routeBeans = mGroupMap.get(routeBean.getRouteGroup());if (routeBeans == null){// 如果從 mGroupMap 獲取的該分組的路由信息集合為空// 則創建新集合, 放置路由信息, 并加入到 mGroupMap 中routeBeans = new ArrayList<>();routeBeans.add(routeBean);mGroupMap.put(routeBean.getRouteGroup(), routeBeans);}else{// 從 mGroupMap 獲取的路由分組對應的路由信息集合不為空// 直接添加 路由信息 即可routeBeans.add(routeBean);}}/*** 驗證路由地址* @Route(path = "/app/MainActivity")* @param routeBean*/private void checkRouteAddress(RouteBean routeBean){// 獲取路由地址String routeAddress = routeBean.getRouteAddress();// 獲取路由分組String routeGroup = routeBean.getRouteGroup();// 驗證路由地址是否以 "/" 開頭if (!routeAddress.startsWith("/")) {throw new RuntimeException("路由地址 " + routeAddress + " 格式錯誤");}// 如果路由地址的分組為空 ,// 則截取第 0 和 第 1 個 "/" 之間的字符串作為分組名稱if (routeGroup == null || "".equals(routeGroup)){String group = routeAddress.substring(routeAddress.indexOf("/", 0) + 1,routeAddress.indexOf("/", 1));if (group == null || "".equals(group)){throw new RuntimeException("路由地址 " + routeAddress + " 獲取分組錯誤");}// 打印組名mMessager.printMessage(Diagnostic.Kind.NOTE,"打印路由地址 " + routeAddress + " 的組名為 " + group);// 正式設置路由地址分組routeBean.setRouteGroup(group);}} }
2、app 模塊中的注解類生成的 Java 源碼
Module 模塊中 , 使用注解生成的源碼 , 都在對應模塊的 " build\generated\ap_generated_sources\debug\out\ " 目錄中 ;
app 中的注解類 :
@Route(path = "/app/MainActivity") public class MainActivity extends Activity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);} }生成的 路由表 源碼 : 生成源碼路徑 D:\002_Project\002_Android_Learn\Component\app\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Group_app.java ;
package kim.hsl.router;import java.lang.Override; import java.lang.String; import java.util.Map; import kim.hsl.component.MainActivity; import kim.hsl.route_core.template.IRouteGroup; import kim.hsl.router_annotation.model.RouteBean;public class Router_Group_app implements IRouteGroup {@Overridepublic void loadInto(Map<String, RouteBean> atlas) {atlas.put("app", new RouteBean(RouteBean.Type.ACTIVITY, MainActivity.class, "/app/MainActivity", "app"));} }生成的 Root 表源碼 : 生成源碼路徑 D:\002_Project\002_Android_Learn\Component\app\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Root_app.java ;
package kim.hsl.router;import java.lang.Class; import java.lang.Override; import java.lang.String; import java.util.Map; import kim.hsl.route_core.template.IRouteGroup; import kim.hsl.route_core.template.IRouteRoot;public class Router_Root_app implements IRouteRoot {@Overridepublic void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {routes.put("app", Router_Group_app.class);} }3、library2 模塊中的注解類生成的 Java 源碼
Module 模塊中 , 使用注解生成的源碼 , 都在對應模塊的 " build\generated\ap_generated_sources\debug\out\ " 目錄中 ;
library2 中的注解類 :
package kim.hsl.library2;import android.util.Log;import kim.hsl.base.ComponentService; import kim.hsl.router_annotation.Route;@Route(path = "/library2/StringService") public class StringService implements ComponentService {@Overridepublic void doSomething() {Log.i("StringService", "library2 組件中的 StringService 服務 ");} }生成的 路由表 源碼 : 生成源碼路徑 D:\002_Project\002_Android_Learn\Component\library2\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Group_library2.java ;
package kim.hsl.router;import java.lang.Override; import java.lang.String; import java.util.Map; import kim.hsl.library2.StringService; import kim.hsl.route_core.template.IRouteGroup; import kim.hsl.router_annotation.model.RouteBean;public class Router_Group_library2 implements IRouteGroup {@Overridepublic void loadInto(Map<String, RouteBean> atlas) {atlas.put("library2", new RouteBean(RouteBean.Type.ISERVICE, StringService.class, "/library2/StringService", "library2"));} }生成的 Root 表源碼 : 生成源碼路徑 D:\002_Project\002_Android_Learn\Component\library2\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Root_library2.java ;
package kim.hsl.router;import java.lang.Class; import java.lang.Override; import java.lang.String; import java.util.Map; import kim.hsl.route_core.template.IRouteGroup; import kim.hsl.route_core.template.IRouteRoot;public class Router_Root_library2 implements IRouteRoot {@Overridepublic void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {routes.put("library2", Router_Group_library2.class);} }四、博客資源
博客源碼 :
- GitHub : https://github.com/han1202012/Component
- CSDN 下載 :
總結
以上是生活随笔為你收集整理的【Android 组件化】路由组件 ( 生成 Root 类记录模块中的路由表 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 组件化】路由组件 (
- 下一篇: 【Android 组件化】路由组件 (