ARouter基本使用与原理浅析

ARouter,A framework for assisting in the renovation of Android componentization (帮助 Android App 进行组件化改造的路由框架) —— 支持模块间的路由、通信、解耦

官方中文介绍:
https://github.com/alibaba/ARouter/blob/master/README_CN.md
(中文比英文文档,详尽得多…,良心文档啊)

基本使用

1.添加依赖
android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
            	//注解处理器需要的模块名,作为路径映射的前缀
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

dependencies {
	implementation 'com.alibaba:arouter-api:1.4.1'
    annotationProcessor 'com.alibaba:arouter-compiler:1.2.2' //注解处理器,会将注解编译成Java类
}
2.添加注解
//注意:路劲至少两级,即xx/xx,前一个xx用于分组
@Route(path = "/test/second")
public class SecondActivity extends AppCompatActivity {
}
3.初始化SDK

一般在Application中初始化

ARouter.init(this);
4.使用
//很简单,一句话完成,可携带参数
ARouter.getInstance().build("/test/second").navigation();

原理浅析

从ARouter.getInstance().build("/test/second").navigation();出发,解释其跳转基本过程。
先上一张时序图:
ARouter基本使用与原理浅析_第1张图片

1.ARouter.build(path)构建Postcard

ARouter只是对外统一的api接口,实现基本由_ARouter完成,所以构建Postcard也是由_ARouter,构建,build(path, extractGroup(path))中extractGroup方法,就是把/xx/xx中前面的xx转换为默认group的方法,这也是之前必须使用2级以上目录的原因。build到此就完成了,此时还没有完成映射到activity的任务,只是把path浅析了下。

2.Postcard.navigation()实现跳转

最后也是由_ARouter完成,在_ARouter.navigation时,首先调用LogisticsCenter.completion(postcard)完善postcard的信息,而completion方法,则完成了path到activity的转换关系。完善后,再调用_navigation完成最终跳转。

3.LogisticsCenter.completion(postcard)将path映射到activity

核心部分:

public synchronized static void completion(Postcard postcard) {
		RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {
            Class groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());
            if (null == groupMeta) {
                throw new NoRouteFoundException();
            } else {
                // Load route and cache it into memory, then delete from metas.
                try {
                    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    iGroupInstance.loadInto(Warehouse.routes);
                    Warehouse.groupsIndex.remove(postcard.getGroup());
                } catch (Exception e) {
                    throw new HandlerException();
                }

                completion(postcard);   // Reload
            }
        } else {
            postcard.setDestination(routeMeta.getDestination()); //destination就是需要跳转的Activity.class
            ......
        }
}

首先去Warehouse的routes中寻找RouteMeta(路由元数据)。
Warehouse可以理解为存储路由元数据的容器,包括:路由关系、拦截器、provider的映射关系等。RouteMeta既持有activity等对应跳转类信息。
首次navigation时,RouteMeta == null,故会用postcard build时的group path先找到对一个的IRouteGroup信息[IRouteGroup何时加载到Warehouse的,见下条],然后通过iGroupInstance.loadInto将改分组下的RouteMeta都加载到缓存中,这可以理解为延迟加载,降低初始化时的一些压力。加载后,再重新调用completion(postcard)。

4.IRouteGroup的加载

路由元数据RouteMeta是从实现了IRouteGroup接口的实例中load进来的,这个实现了IRouteGroup接口的类在哪?何时load进Warehouse?在LogisticsCenter.init()方法中,找到了踪迹。

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        Set routerMap;
        // It will rebuild router map every times when debuggable.
        if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
            // These class was generated by arouter-compiler.
            routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
            if (!routerMap.isEmpty()) {
                context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
            }

            PackageUtils.updateVersion(context);    // Save new version name when router map update finishes.
        } else {
            routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet()));
        }

        for (String className : routerMap) {
            if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                // This one of root elements, load root.
                ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
            }
            ......
        }
    }

首次init时,通过ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE)将ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes"下的所有类,都加载进routerMap中。然后通过区分routerMap类名,实例化com.alibaba.android.arouter.routes.ARouter##Root开头的类[由于csdn对$的支持并不友好,故以下$都使用#代替],调用其loadInto将对应的IRouteGroup类都加载到Warehouse.groupsIndex中。
PS:
init方法有一些小细节,针对debug版本或者新版本,才会有一次完整的类find和load的过程,加载完后会将类路径都存入sp,之后启动从sp拿,以增加启动速度。
ClassUtils.getFileNameByPackageName方法并不简单,其中牵扯了在多dex或者特殊rom下加载类的一些处理,有兴趣的同学可以阅读源码了解。

ARouter##Root##app

public class ARouter$$Root$$app implements IRouteRoot {
    public ARouter$$Root$$app() {
    }

    public void loadInto(Map> routes) {
        routes.put("test", test.class);
    }
}

ARouter##Group##test

public class ARouter$$Group$$test implements IRouteGroup {
    public ARouter$$Group$$test() {
    }

    public void loadInto(Map atlas) {
        atlas.put("/test/second", RouteMeta.build(RouteType.ACTIVITY, SecondActivity.class, "/test/second", "test", (Map)null, -1, -2147483648));
    }
}
5.Route类的生成

接着上一步的解析,下面要了解的是,ARouter##Root##app,ARouter##Group##test这些编译好的类,是何时/如何生成的。这里使用的是android的apt(Annotation Processing Tool)技术,即在编译时,将注解生成为Java代码。所以在build.gradle添加依赖时,会添加注解处理器 annotationProcessor ‘com.alibaba:arouter-compiler:1.2.2’ ,具体的处理过程,可以参见arouter-compiler的RouteProcessor,RouteProcessor代码较长,这里就不详述了。感兴趣的童鞋,可以自己写一写注解处理器,com.squareup.javapoet神器了解一下。

你可能感兴趣的:(Android,架构,Java)