写点什么

ARouter 系列 2:源码分析,移动端跨平台开发

用户头像
Android架构
关注
发布于: 10 小时前

public Person(String name, int age) {


this.name = name;


this.age = age;


}


protected Person(Parcel in) {


name = in.readString();


age = in.readInt();


}


@Override


public String toString() {


return "Person{" +


"name='" + name + ''' +


", age=" + age +


'}';


}


public static final Creator<Person> CREATOR = new Creator<Person>() {


@Override


public Person createFromParcel(Parcel in) {


return new Person(in);


}


@Override


public Person[] newArray(int size) {


return new Person[size];


}


};


public String getName() {


return name;


}


public void setName(String name) {


this.name = name;


}


public int getAge() {


return age;


}


public void setAge(int age) {


this.age = age;


}


@Override


public int describeContents() {


return 0;


}


@Override


public void writeToParcel(Parcel dest, int flags) {


dest.writeString(name);


dest.writeInt(age);


}


}


public class MainLooper extends Handler {


private static MainLooper instance = new MainLooper(Looper.getMainLooper());


protected MainLooper(Looper looper) {


super(looper);


}


public static MainLooper getInstance() {


return instance;


}


public static void runOnUiThread(Runnable runnable) {


if(Looper.getMainLooper().equals(Looper.myLooper())) {


runnable.run();


} else {


instance.post(runnable);


}


}


}


2、源码分析


======


ARouter 是通过 APT 生成代码在框架内部进行操作,那么,项目编译生成的文件位置在那里?



既然生成了这些源码,我们就先随便点点看看这些都是啥?


/**


  • DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */


public class ARouter



app implements IRouteGroup {


@Override


public void loadInto(Map<String, RouteMeta> atlas) {


atlas.put("/app/Test1Activity", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/app/test1activity", "app", null, -1, -2147483648));


atlas.put("/app/Test2Activity", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/app/test2activity", "app", new java.util.HashMap<String, Integer>(){{put("test", 10); put("name", 8); put("age", 3); }}, -1, -2147483648));


atlas.put("/app/Test3Activity", RouteMeta.build(RouteType.ACTIVITY, TestInterceptorActivity.class, "/app/test3activity", "app", null, -1, -2147483648));


}


}


/**


  • DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */


public class ARouter



app implements IRouteRoot {


@Override


public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {


routes.put("app", ARouter



app.class);


}


}


具体源码大家可以自行查看,不过多粘贴了。


这里简简单单随便截图了 APT 生成的部分源码,是不是感觉跟上一篇文章使用到的代码很多相似性呐~比如拦截器的优先级是 1、跳转匹配的路径也是一样的、跳转传递的参数、定义的组名等等。既然这么多一样的那肯定是在内部某部分进行封装使用,带着这个问题我们开始逐步分析。


首先,我们从该框架使用到的注解开始分析(因为注解是使用这个框架的起点)


2.1、注解分析




首先,我们知道要使用 ARouter 的首先需要在类的注释上面写上 @Route 这个注解,点进源码看看


@Target({ElementType.TYPE})


@Retention(RetentionPolicy.CLASS)


public @interface Route {


String path();


S


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


tring group() default "";


String name() default "";


int extras() default Integer.MIN_VALUE;


int priority() default -1;


}


使用该注解标注的类将被自动添加至路由表中。而且,ARouter 并非仅提供页面(Activity)的路由功能,还可以用来路由模块想要暴露给其他模块调用的接口。也就是说?@Route?不仅可用于 Activity 类,还可用于模块对外接口的实现类。那么具体它可以实现那些类型?


从上面的源码可以看到,除了拦截器,里面通过实现接口重写方法,方法里面都有一个 RouteMeta,那么我们点进去 RouteMeta 这个类看看:


/**


  • It contains basic route information.


*/


public class RouteMeta {


private RouteType type; // Type of route


private Element rawType; // Raw type of route


private Class<?> destination; // Destination


private String path; // Path of route


private String group; // Group of route


private int priority = -1; // The smaller the number, the higher the priority


private int extra; // Extra data


private Map<String, Integer> paramsType; // Param type


private String name;


private Map<String, Autowired> injectConfig; // Cache inject config.


public RouteMeta() {


}


}


注释的意思是:它包含基本路由信息。


RouteType,就是路由的类型。那么,这款路由框架的具体路由类型又有那些?带着这个疑问,点进 RouteType?源码看看。


public enum RouteType {


ACTIVITY(0, "android.app.Activity"),


SERVICE(1, "android.app.Service"),


PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"),


CONTENT_PROVIDER(-1, "android.app.ContentProvider"),


BOARDCAST(-1, ""),


METHOD(-1, ""),


FRAGMENT(-1, "android.app.Fragment"),


UNKNOWN(-1, "Unknown route type");


}


首先这是一个枚举,这些就是框架可以具体使用到的路由类型


说完 Route 这个注解,我们在来看看 @Interceptor?拦截器注解,


@Target({ElementType.TYPE})


@Retention(RetentionPolicy.CLASS)


public @interface Interceptor {


/**


  • The priority of interceptor, ARouter will be excute them follow the priority.


*/


int priority();


/**


  • The name of interceptor, may be used to generate javadoc.


*/


String name() default "Default";


}


哦,我们发现拦截器的注解就 2 个方法,第一个是定义优先级的,第二个就是拦截器的名字。


接着,@Autowired 注解:


@Target({ElementType.FIELD})


@Retention(RetentionPolicy.CLASS)


public @interface Autowired {


// Mark param's name or service name.


String name() default "";


// If required, app will be crash when value is null.


// Primitive type wont be check!


boolean required() default false;


// Description of the field


String desc() default "";


}


我们知道这个注解是界面跳转时参数传递用的。Activity 中使用该注解标志的变量,会在页面被路由打开的时候自动赋予传递的参数值。


2.2、初始化分析




我们知道,ARouter 框架使用的第一个步骤,是要先初始化,也就是调用:ARouter.init( Application.this );这个 API,那么,它的初始化究竟是做了那些东西?我们先点进源码看看:


/**


  • Init, it must be call before used router.


*/


public static void init(Application application) {


if (!hasInit) {


logger = _ARouter.logger;


_ARouter.logger.info(Consts.TAG, "ARouter init start.");


hasInit = _ARouter.init(application);


if (hasInit) {


_ARouter.afterInit();


}


_ARouter.logger.info(Consts.TAG, "ARouter init over.");


}


}



那么,实际上它调用的是绿色矩形内的代码,绿色矩形内有两个初始化,一个是?_ARouter.init(application),还有一个是 _ARouter.afterInit( )


我们首先点进 **_ARouter . init()**看看,



A:红色箭头的是类注释,翻译过来就是:ARouter 核心( 外观模式 )


B:绿色箭头代表的是一个线程池,对线程池概念不是很清楚的朋友可以点进?[必须要理清的 Java 线程池](


)?先了解一下


C:蓝色箭头代表的是 这个_ARouter init()实际是调用的 LogisticsCenter 里面的 init 方法。


首先,什么是外观模式?


简单点理解就是,通过创建一个统一的类,用来包装子系统中一个或多个复杂的类,客户端可以通过调用外观类的方法来调用内部子系统中所有方法。大概意思就是这样,想深入理解的话可以自行查阅资料。


其次,这个线程池做了什么功能?


点进去 DefaultPoolExecutor 这个类看看:



由源码得知就是创建了一个数组阻塞队列的线程池


最后,我们点进 LogisticsCenter,看看里面的 init 方法执行了什么操作(因为源码太长,我就分别截图)


图一:



图二:



通过 LogisticsCenter - init(1)这幅源码图可以得知:如果是 Debug 模式,则执行从 if 代码块; 如果是 Release 模式:通过 PackageUtils.isNewVersion(context)判断当前应用是否是第一次启动。接着,获取 arouter-compiler 生成的文件,然后将该文件,存储在 sp 中,下次启动应用的时候,直接从 sp 缓存中读取。


然后,在 LogisticsCenter - init(2)红色矩形的代码块可以得知:首先遍历 arouter-compiler 生成的文件,将他们按照类型分别存储到 Warehouse 的对应字段中。也就是,如果类名前缀符合文件拼接规则,比如为 com.alibaba.android.arouter.routes.ARouter$$Root 的文件,就将其添加到具体的 Warehouse 里面的集合中。也就是对应的这里



那么,这个文件拼接规则(也就是字符串拼接)是?



Warehouse 又是什么?点进源码看看



其中,Warehouse 的类注释写的非常好,翻译过来就是:路由元数据和其他数据的存储。这个类本质就是路由文件映射表。里面提供了各种 HashMap 集合(Map 不允许重复的 key),去存储 SP 存储的值。


我们再看看这里存储拦截器的集合,UniqueKeyTreeMap,这个类是存储拦截器的,我们看看它做了什么?



原来,这个 UniqueKeyTreeMap,就是继承了 TreeMap,哦,我们知道 TreeMap 是一种有序的集合(底层帮我们排序)所以数值越小,它就优先添加拦截器。这样也就解释了为什么拦截器属性值设置的越低,优先级越高。


综上,针对?_ARouter init( )?这个初始化的源码我们可以得知以下:


A:初始化这一操作,表面上是_ARouter ,实则是 LogisticsCenter 在帮我们管理逻辑


B:LogisticsCenter 内部通过先存储 SP,然后遍历、匹配(满足条件则添加到具体的集合中,按照文件的前缀不同,将他们添加到路由映射表 Warehouse 的 groupsIndex、interceptorsIndex、providersIndex?中)


C:具体的路由清单是 Warehouse ,不仅保存实例,还给我们提供了缓存。也就是说?同一个目标(RouteMeta、IProvider、IInterceptor)仅会在第一次使用的时候创建一次,然后缓存起来。后面都是直接使用的缓存。


初始化还有一个 API,_ARouter.afterInit( ) ; 这个 API 在上面的截图也有,那么这个 API 是用来干什么的?我们点进去看看。



首先,它实例化了一个 InterceptorService。这里的 InterceptorService 下面会说。 这里的 build 方法,最终返回的是一个 Postcard 对象:



从源码可以得知,实际上它返回的却是,_ARouter.getInstance().build(path)这个方法,这个方法是一个方法重载(一般用的最多的就是这一个,也就是默认分组,不进行自定义分组),跟进去看看(源码很长,分为以下两个截图):




发现这里有一个 PathReplaceService(也就是红色矩形),这个 PathReplaceService 又是什么,点进去看看



这个类注释翻译过来就是:预处理路径。这个接口是 IProvider 的子类


让我们在看回绿色箭头,其中,navigation(clazz)这种方式是属于根据类型查找,而 build(path)是根据名称进行查找。如果应用中没有实现 PathReplaceService 这个接口,则 pService=null。PathReplaceService 可以对所有的路径进行预处理,然后返回一个新的值(返回一个新的 String 和 Uri)。绿色箭头有一个 extractGroup()方法,点进去看看:



extractGroup(path)这个方法,核心逻辑是红色矩形内的代码,这个方法主要是获取分组名称。切割 path 字符串,默认为 path 中第一部分为组名。这就证明了如果我们不自定义分组,默认就是第一个分号的内容。


分析完了 build,我们在看看 navigation



继续点进源码看看,发现进入了_ARouter 这个类里面的 navigation 方法:




其中,navigation - 1 这幅图中的红色矩形代表的意思是,如果(两个)路径没写对,ARouter 会 Toast 提示客户端,路径不对。


navigation - 2 这幅图中的红色矩形代表的是忽略拦截器。interceptorService 实例化对象的时机,是在_ARouter 这类中的 afterInit( )进行实例化的。这幅图中的蓝色矩形和箭头的方法真实逻辑是调用了下图的方法:




通过这段代码我们可以得知:

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
ARouter系列2:源码分析,移动端跨平台开发