前言 在組件化開發(fā)中一個必須要面對的問題就是組件間頁面跳轉,實現的方法有很多,簡單的可以通過反射獲取,但是比較耗費性能,也可以通過隱式跳轉,但是隨著頁面的增多,過濾條件會隨之增多,后期維護麻煩。那還有什么方法呢,沒錯,就是接下來要介紹的Arouter路由框架,該框架是阿里巴巴開源項目,大廠出品,必屬精品。使用過Arouter得同學都知道Arouter是通過給每個頁面添加@Route注解然后調用一定的方法實現跳轉的,而Arouter的核心就是這個注解。 ??這里要介紹一個概念,APT(Annotation Processing Tool)即注解處理器,是一種處理注解的工具,它用來在編譯時掃描和處理注解,注解處理器以Java代碼(或者編譯過的字節(jié)碼)作為輸入,生成.java文件作為輸出。簡單來說就是在編譯期,通過注解生成.java文件。 ??Arouter的路由表就是在該工具下在編譯期生成的,說簡單了,就是利用注解在編譯期生成了一些java文件,我們在這些新的java文件中將所有被注解的頁面添加進了路由表中。
設計思路arouter-compiler:注解編譯處理器,引入“arouter-annotation”,在編譯器把注解標注的相關目標類生成映射文件,包含路由框架所使用的全部注解,及其相關類 arouter-api:實現路由控制
實現效果
步驟- 新建module java library(router_compiler)(因為在主 Module 中無法找到 AbstractProcessor 類)
- 新建module android library(router_api),步驟如上,但是要注意選擇Android library。
- 在route-compiler的gradle文件中導入依賴和jdk版本支持
apply plugin: 'java-library'dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) api 'com.squareup:javapoet:1.11.1' api 'org.apache.commons:commons-collections4:4.4' api 'org.apache.commons:commons-lang3:3.5'}sourceCompatibility = "8"targetCompatibility = "8" 在app工程gradle文件中添加jdk版本支持compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8}
- 在route_api和app模塊等其他組件化模塊的gradle文件中導入route_compiler模塊
annotationProcessor project(':router_compiler')api project(':router_compiler')
- 在每個module模塊中的gradle文件中添加下列語句用來獲取每個module的包名
javaCompileOptions { annotationProcessorOptions { arguments = [ROUTER_MODULE_NAME: project.getName()] }}
- 在router_compiler模塊中創(chuàng)建RouteProcessor類并繼承自AbstractProcessor
- 在router_compiler模塊中的main文件夾下創(chuàng)建文件夾resources/META-INF/services,然后創(chuàng)建javax.annotation.processing.Processor文件,并添加下列語句
com.nsyw.routerdemo.router_compiler.RouteProcessor
- 在router-compiler根目錄下新建注解類Route
public @interface Route { /** * Path of route */ String path();}
- 創(chuàng)建接口IRoute,自動生成的java文件都要繼承自該接口
public interface IRoute { /** * * @param routes 模塊下的路由集合 */ void loadInto(Map routes);}
/** * @SupportedAnnotationTypes表示支持的注解類型 */@SupportedAnnotationTypes("com.nsyw.routerdemo.router_compiler.annotation.Route")public class RouteProcessor extends AbstractProcessor { ...... @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { ...... } @Override public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) { ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get( ClassName.get(Map.class), ClassName.get(String.class), ClassName.get(RouteMeta.class) ); ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "routes").build(); /* methodBuilder 方法名 addAnnotation 方法添加注解 addModifiers 方法訪問限制類型 addParameter 添加參數 */ MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO) .addAnnotation(Override.class) .addModifiers(PUBLIC) .addParameter(groupParamSpec); ClassName routeMetaCn = ClassName.get(RouteMeta.class); ClassName routeTypeCn = ClassName.get(RouteType.class); //遍歷@Route注解的所有Activity for (Element element : routeElements) { TypeMirror tm = element.asType(); //獲取注解 Route route = element.getAnnotation(Route.class); RouteMeta routeMeta = null; if (types.isSubtype(tm, type_Activity)) { routeMeta = new RouteMeta(route.path(), RouteType.ACTIVITY); } //獲取被注解的類的類名 ClassName className = ClassName.get((TypeElement) element); /* 方法內的添加路由語句 routes.put(routeMeta.getPath(),RouteMeta.build(routeMeta.getPath(),RouteType.ACTIVITY,className.class)) */ loadIntoMethodOfGroupBuilder.addStatement( "routes.put($S,$T.build($S,$T." + routeMeta.getRouteType() + ", $T.class))", routeMeta.getPath(), routeMetaCn, routeMeta.getPath(), routeTypeCn, className); } /* 構建java文件 */ try { JavaFile.builder(PACKAGE_OF_GENERATE_FILE, TypeSpec.classBuilder(NAME_OF_ROUTE + moduleName) .addJavadoc(WARNING_TIPS) .addSuperinterface(ClassName.get(mElementsUtil.getTypeElement(IROUTE_LOAD))) .addModifiers(PUBLIC) .addMethod(loadIntoMethodOfGroupBuilder.build()) .build() ).build().writeTo(mFiler); } catch (IOException e) { e.printStackTrace(); } return true; } return false; }}
- 在router_api模塊下創(chuàng)建Router
public class Router { private static volatile Router mInstance = new Router(); private Context mContext; private String path; private Map map = new HashMap<>(); public static void init(Application application) { mInstance.mContext = application; Set routerMap; try { routerMap = ClassUtils.getFileNameByPackageName(mInstance.mContext, consts.PACKAGE_OF_GENERATE_FILE); Log.e("Router", routerMap.toString()); for (String className : routerMap) { ((IRoute) (Class.forName(className).getConstructor().newInstance())).loadInto(mInstance.map); } } catch (PackageManager.NameNotFoundException | InterruptedException | IOException | ClassNotFoundException | NoSuchMethodException | InstantiationException | InvocationTargetException | IllegalAccessException e) { e.printStackTrace(); } } public static synchronized Router getInstance() { return mInstance; } public Router build(String path) { mInstance.path = path; return mInstance; } public void navigation(Context context) { RouteMeta routeMeta = mInstance.map.get(mInstance.path); if (routeMeta != null) { context.startActivity(new Intent(context, routeMeta.getClazz())); } }}
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Router.init(this); }}
在AndroidManifest文件的application節(jié)點添加下列語句 android:name=".MyApplication"
- 項目build的完之后會在各個模塊的相應的文件夾下生成java文件,這些文件會被Router依次獲取將路由信息存入路由表中。
以下代碼是編譯器自動生成的 package com.nsyw.routerdemo.routes;import com.nsyw.routerdemo.MainOneActivity;import com.nsyw.routerdemo.MainTwoActivity;import com.nsyw.routerdemo.router_compiler.IRoute;import com.nsyw.routerdemo.router_compiler.RouteMeta;import com.nsyw.routerdemo.router_compiler.RouteType;import java.lang.Override;import java.lang.String;import java.util.Map;/** * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */public class Router$$App$$app implements IRoute { @Override public void loadInto(Map routes) { routes.put("/main/one",RouteMeta.build("/main/one",RouteType.ACTIVITY, MainOneActivity.class)); routes.put("/main/two",RouteMeta.build("/main/two",RouteType.ACTIVITY, MainTwoActivity.class)); }}
Router.getInstance().build("/main/one").navigation(MainActivity.this);
小結Router只是參照ARouter手動實現的路由框架,剔除掉了很多東西,只實現了組件間Activity之間的跳轉,如果想要用在項目里,建議還是用ARouter更好,畢竟這只是個練手項目,功能也不夠全面,當然有同學想對demo擴展后使用那當然更好,遇到什么問題可以及時聯(lián)系我。我的目的是通過自己手動實現路由框架來加深對知識的理解,如這里面涉及到的知識點apt、javapoet和組件化思路、編寫框架的思路等??吹竭@里,如果感覺干貨很多,歡迎關注我的github,里面會有更多干貨! |