2024年安卓最全阿里开源路由框架ARouter的源码分析,2024江苏省考面试题目

学习交流

群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

@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 “No desc.”;
}

4.1.4 RouteMeta路由元信息

如果全部路由信息认为是一张表格,那么RouteMeta就是表格的一行,代表路由表的一条元信息。

public class RouteMeta {
private RouteType type; // 路由的类型:
private Element rawType; // Raw type of route
private Class<?> destination; // 目标Class
private String path; // 路径URL
private String group; // 分组
private int priority = -1; // 路由的优先级
private int extra; // 目标Class的额外配置开关信息;譬如某些页面是否需要网络校验、登录校验等
private Map<String, Integer> paramsType; // 目标Class的需要注入的参数 的参数名称:参数类型TypeKind
}

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”);

int id;
String className;
}

public enum TypeKind { //目标Class的需要注入的参数的参数类型
//基本类型
BOOLEAN,
BYTE,
SHORT,
INT,
LONG,
CHAR,
FLOAT,
DOUBLE,

//封装类型
STRING,
PARCELABLE,
OBJECT; //使用Json解析
}

4.2 arouter-compiler注解编译器

实现了“自动注册映射关系”也就是在编译期间自动生成映射文件,所以该module其实就是实现了一些注解处理器,目标在于生成映射文件与辅助文件(构造路由表逻辑的创建)

开源最佳实践:Android平台页面路由框架ARouter中“二、ARouter的技术方案”的“页面注册:注解&注解处理器”
1. 首先通过注解处理器扫出被标注的类文件;
2. 然后按照不同种类的源文件进行分类,这是因为ARouter是一个框架,其能够提供的功能非常多,所以不仅仅提供了跳转功能,它也能够实现模块之间的解耦,除此之外ARouter还能够提供很多的功能,像刚才提到的拦截器可以实现自动注册,其实ARouter中的所有组件都是自动注册的
3. 在按照不同种类的源文件进行分类完成之后,就能够按照固定的命名格式(工程名+ + G r o u p + +Group+ +Group++分组名)生成映射文件,这部分完成之后就意味着编译期的部分已经结束了

一般来说arouter-compiler所产生的class文件有以下几种:

  • 项目目录下的 XXX 工程名 工程名 工程名Autowired. 为自动装载的辅助类,核心为inject函数,实现对目标对象的成员变量的赋值

  • routes目录下

  • 工程名 G r o u p Group Group分组名 【组内的路由清单列表】
    (Map< String, RouteMeta> atlas
    包含了对应分组下的,路由URL与目标对象Class的映射关系;
    注意Router注解中无分组的话,默认以“/xx/xx”的第一个xx为分组名

  • 工程名 R o o t Root Root$模块名 【组别的清单列表】
    Map< String, Class< ? extends IRouteGroup>> routes
    包含了组名与对应组内的路由清单列表Class的映射关系
    是Arouter的“分组管理,按需加载”的实现。
    ARouter在初始化的时候只会一次性地加载所有的root结点,而不会加载任何一个Group结点,这样就会极大地降低初始化时加载结点的数量
    那么什么时候加载分组结点呢?其实就是当某一个分组下的某一个页面第一次被访问的时候,整个分组的全部页面都会被加载进去,这就是ARouter的按需加载

  • 工程名 P r o v i d e r s Providers Providers模块名 【Ioc的动作路由清单列表】
    Map< String, RouteMeta> providers
    PROVIDER 类型的路由节点的清单列表
    包含了使用依赖注入方式的某class(实现了IProvide接口的直接子类)的 路由URL 与class映射关系
    目标Class都实现了IProvider接口,借此实现部分路由转到该清单中
    需要注意的是:Ioc动作路由清单其实只是 Route注解的一种特殊用法,总的来说,还是一种URL与目标类的映射关系
    其实想一下,依赖注入,无非也就是指定好目标接口的目标类,然而实例化后进行赋值。URL就是指定说明

  • 工程名 I n t e r c e p t o r s Interceptors Interceptors模块名 【模块内的拦截器清单列表】
    Map< Integer, Class< ? extends IInterceptor>> interceptors
    包含了某个模块下的拦截器 与 优先级的映射关系
    一个模块下的所有拦截器都在该类中包含,无分组特性,所以直接以模块名命名类文件

  • 映射文件的生成并不是随意的,而是遵循一定的规则。是参照API Module的Temple的接口,进行创建的
  • 可以认为像是一个人一样,他记录了API Module-Temple中接口的名称,函数的名称参数等信息,然后借用一定的工具,写出了继承这些函数的接口
  • 注意:上述描述中,我们说的是“写出了接口”,就类似于在txt文档中写了一些文本,并不涉及引用,这也就意味着不需要依赖API Module,同时也标示API Module的Template中的模板不能随意更改

4.2.1 Route注解处理

区分路由类型是通过“是否为 Activity 、IProvider 、 Service 的子类”来判断的,详见“com.alibaba.android.arouter.compiler.processor.RouteProcessor”

在app中所定义的路由路径有:

@Route(path = “/test/activity1”)
public class Test1Activity extends AppCompatActivity {

}

@Route(path = “/test/activity2”)
public class Test2Activity extends AppCompatActivity {

}

@Route(path = “/test/activity3”)
public class Test3Activity extends AppCompatActivity {

}

@Route(path = “/test/activity4”)
public class Test4Activity extends AppCompatActivity {

}

/以下为Ioc的应用,都实现了IProvider接口**************/
@Route(path = “/service/hello”)
public class HelloServiceImpl implements HelloService {

}

@Route(path = “/service/json”)
public class JsonServiceImpl implements SerializationService {

}

@Route(path = “/service/single”)
public class SingleService implements IProvider {

}

我们先看下使用 annotationProcessor ‘com.alibaba:arouter-compiler:1.0.3’ 处理官方Demo的app所生成的映射文件:
【图4.2.1】
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

很明显,ARouter G r o u p Group Groupservice和ARouter G r o u p Group Grouptest分别是分组 service 和 test 的组内路由清单列表,我们可以把它想象成分别是 1班 和 2 班 的学生名单。
ARouter R o o t Root Rootapp 则是组别的清单列表,我们亦可以把它想成班级的清单,里面有两个班级,1班 和 2 班。

4.2.1.1 工程名 G r o u p Group Group分组名 【组内的路由清单列表】

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

public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put(“/service/hello”, RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, “/service/hello”, “service”, (Map)null, -1, -2147483648));
atlas.put(“/service/json”, RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, “/service/json”, “service”, (Map)null, -1, -2147483648));
atlas.put(“/service/single”, RouteMeta.build(RouteType.PROVIDER, SingleService.class, “/service/single”, “service”, (Map)null, -1, -2147483648));
}
}

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

public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put(“/test/activity1”, RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, “/test/activity1”, “test”, new HashMap() {
{
this.put(“pac”, Integer.valueOf(9));
this.put(“obj”, Integer.valueOf(10));
this.put(“name”, Integer.valueOf(8));
this.put(“boy”, Integer.valueOf(0));
this.put(“age”, Integer.valueOf(3));
this.put(“url”, Integer.valueOf(8));
}
}, -1, -2147483648));
atlas.put(“/test/activity2”, RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, “/test/activity2”, “test”, new HashMap() {
{
this.put(“key1”, Integer.valueOf(8));
}
}, -1, -2147483648));
atlas.put(“/test/activity3”, RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, “/test/activity3”, “test”, new HashMap() {
{
this.put(“name”, Integer.valueOf(8));
this.put(“boy”, Integer.valueOf(0));
this.put(“age”, Integer.valueOf(3));
}
}, -1, -2147483648));
atlas.put(“/test/activity4”, RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, “/test/activity4”, “test”, (Map)null, -1, -2147483648));
atlas.put(“/test/fragment”, RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, “/test/fragment”, “test”, (Map)null, -1, -2147483648));
atlas.put(“/test/webview”, RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, “/test/webview”, “test”, (Map)null, -1, -2147483648));
}
}

4.2.1.2 工程名 R o o t Root Root$模块名 【组别的清单列表】

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

public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put(“service”, service.class);
routes.put(“test”, test.class);
}
}

4.2.1.3 工程名 P r o v i d e r s Providers Providers模块名 【Ioc的动作路由清单列表】

特别的,请注意:PROVIDER 类型的路由节点既存在于存在于对应的分组中,也存在于该类型的清单列表中

这就好像学生中有一些是少先队员,每个班级中都有可能有少先队员,而学校又有一份少先队员的总列表。这就意味着我们有两种方式来查找到一个可确定的少先队员。

所以,ARouter 可通过两种方式来获取 PROVIDER 类型的路由节点

  • ByName的方式:通过【组内的路由清单列表】
    根据 注解的name对HelloService进行注入
    ((HelloService) ARouter.getInstance().build(“/service/hello”).navigation()).sayHello(“mike”);
  • ByType的方式 :通过【Ioc的动作路由清单列表】
    仅在HelloService接口只有一个实现时可用 = 根据classType实现注入
    当同一接口有多个实现的时候,必须使用byName的方式发现服务
    ARouter.getInstance().navigation(HelloService.class).sayHello(“mike”);

注意名称是 (实现了IProvide接口的直接子类)
- HelloServiceImpl的名称是 其父类HelloService,HelloService实现了IProvide接口
- JsonServiceImpl的名称是 其父类SerializationService,SerializationService实现了IProvide接口
- SingleService的名称是 自身SingleService,SingleService实现了IProvide接口

public class ARouter$$Providers$$app implements IProviderGroup {
public ARouter$$Providers$$app() {
}

public void loadInto(Map<String, RouteMeta> providers) {
providers.put(“com.alibaba.android.arouter.demo.testservice.HelloService”, RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, “/service/hello”, “service”, (Map)null, -1, -2147483648));
providers.put(“com.alibaba.android.arouter.facade.service.SerializationService”, RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, “/service/json”, “service”, (Map)null, -1, -2147483648));
providers.put(“com.alibaba.android.arouter.demo.testservice.SingleService”, RouteMeta.build(RouteType.PROVIDER, SingleService.class, “/service/single”, “service”, (Map)null, -1, -2147483648));
}
}

4.2.2 Autowired注解处理

图4.2.1并没有显示自动装载的辅助类,是由于test-module-1中没有使用 Autowired 注解,最后我们再给出下app的映射文件结构
【图4.2.2】
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

@Route(path = “/test/activity1”)
public class Test1Activity extends AppCompatActivity {

@Autowired
String name;

@Autowired
int age;

@Autowired(name = “boy”)
boolean girl;

@Autowired
TestParcelable pac;

@Autowired
TestObj obj;

private long high;

@Autowired
String url;

@Autowired
HelloService helloService;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test1);

ARouter.getInstance().inject(this);

// name = getIntent().getStringExtra(“name”);
// age = getIntent().getIntExtra(“age”, 0);
// girl = getIntent().getBooleanExtra(“girl”, false);
// high = getIntent().getLongExtra(“high”, 0);
// url = getIntent().getStringExtra(“url”);

String params = String.format(
“name=%s,\n age=%s,\n girl=%s,\n high=%s,\n url=%s,\n pac=%s,\n obj=%s”,
name,
age,
girl,
high,
url,
pac,
obj
);
helloService.sayHello(“Hello moto.”);

((TextView)findViewById(R.id.test)).setText("I am " + Test1Activity.class.getName());
((TextView)findViewById(R.id.test2)).setText(params);
}
}

public class Test1Activity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;

public Test1Activity$$ARouter$$Autowired() {
}

public void inject(Object target) {
this.serializationService = (SerializationService)ARouter.getInstance().navigation(SerializationService.class);
Test1Activity substitute = (Test1Activity)target;
substitute.name = substitute.getIntent().getStringExtra(“name”);
substitute.age = substitute.getIntent().getIntExtra(“age”, 0);
substitute.girl = substitute.getIntent().getBooleanExtra(“boy”, false);
substitute.pac = (TestParcelable)substitute.getIntent().getParcelableExtra(“pac”);
if(null != this.serializationService) {
substitute.obj = (TestObj)this.serializationService.json2Object(substitute.getIntent().getStringExtra(“obj”), TestObj.class);
} else {
Log.e(“ARouter::”, “You want automatic inject the field ‘obj’ in class ‘Test1Activity’ , then you should implement ‘SerializationService’ to support object auto inject!”);
}

substitute.url = substitute.getIntent().getStringExtra(“url”);
substitute.helloService = (HelloService)ARouter.getInstance().navigation(HelloService.class);
}
}

public class BlankFragment$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;

public BlankFragment$$ARouter$$Autowired() {
}

public void inject(Object target) {
this.serializationService = (SerializationService)ARouter.getInstance().navigation(SerializationService.class);
BlankFragment substitute = (BlankFragment)target;
substitute.name = substitute.getArguments().getString(“name”);
if(null != this.serializationService) {
substitute.obj = (TestObj)this.serializationService.json2Object(substitute.getArguments().getString(“obj”), TestObj.class);
} else {
Log.e(“ARouter::”, “You want automatic inject the field ‘obj’ in class ‘BlankFragment’ , then you should implement ‘SerializationService’ to support object auto inject!”);
}

if(null == substitute.obj) {
Log.e(“ARouter::”, “The field ‘obj’ is null, in class '” + BlankFragment.class.getName() + “!”);
}

}
}

我们分析下Test1Activity A R o u t e r ARouter ARouterAutowired的代码,核心就是inject(Object target)

  • 通过Arouter路由框架的IOc的ByType方式对SerializationService进行注入,该类为Json转换的工具类
  • 注意助理使用的是ByType方式,也就是直接找到实现了SerializationService接口的唯一类进行实例化并注入,如果实现了SerializationService接口的有多个类,那么就会出现问题
  • 所以全局应用的所有模块中,只能存在一个实现了SerializationService接口的类
  • 获取目标对象实例
  • 利用目标对象的对应传值方式,对目标对象的实例中的成员变量进行赋值
  • Acitivty使用的getIntent()—–由框架自身的参数传递决定,详见 4.3API部分
  • Fragment使用getArguments()—–由框架自身的参数传递决定,详见 4.3API部分
  • OBJ对象使用JSon辅助类进行实例化转换—– 详见 4.3API部分,传递参数时会将对象封装为json字符串
  • IOc依赖注入对象,默认使用byType方式,如果Autowired注解中有标识name,则使用name指向的类实例并赋值

4.2.3 Interceptor注解处理

@Interceptor(priority = 7)
public class Test1Interceptor implements IInterceptor {

}

public class ARouter$$Interceptors$$app implements IInterceptorGroup {
public ARouter$$Interceptors$$app() {
}

public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
interceptors.put(Integer.valueOf(7), Test1Interceptor.class);
}
}

4.3 arouter-api路由控制

【图4.3.1 图4.3.2】
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 最上层是Launcher层,这一层是开发者可以直接用到的,其实所有的API都是在这一层中。
  • 在Launcher层的下一层就是Frossard层,从上图中可以看到Frossard层也是绿色的,表示这一层也是可以被外部调用的,Frossard层其实包含了三部分,分别是:Service、Callback和Template.
  • 这里的Service概念和服务端的Service概念是相似的,也是在客户端的简单引申,但是却不同于Android组件中的Service,这里的Service是ARouter抽象出来的概念,从本质上讲,这里的Service是接口,从意义上讲是将一定的功能和组件封装成接口,并对外提供能力。
  • Template则是模板。Compiler模块在编译期会生成一些映射文件,而这些映射文件的生成规则就是根据Template来生成的,通过记录Template的相关接口函数名称+参数等,生成加载逻辑的代码,这样按照一定的规则和约束生成映射文件也方便Route在运行的时候进行读取。
  • 再往下一层就完全是SDK的内部实现了,这一层包括了Ware House、Thread、Log、Exception以及Class工具。
  • Ware House主要存储了ARouter在运行期间加载的一些配置文件以及映射关系;
  • 而Thread则是提供了线程池,因为存在多个拦截器的时候以及跳转过程中都是需要异步执行的;
  • Class工具则是用于解决不同类型APK的兼容问题的。
  • 再下一层就是Logistics Center,从名字上翻译就是物流中心,整个SDK的流转以及内部调用最终都会下沉到这一层,当然也会按照功能模块进行划分。

4.3.1 ARouter init 初始化过程分析

经过前面的分析,我们几乎可以断定,ARouter 的初始化过程最重要的一步一定是把前面编译产生的路由清单文件加载到内存,形成一个路由表,以供后面路由查找之用。

Class ARouter 其实是一个代理类,它的所有函数实现都交给Class _ARouter去实现,两个都是单例模式

我们先来看 arouter-api 中的 com.alibaba.android.arouter.launcher.ARouter

/**

  • 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.”);//打印 ARouter初始化日志
    hasInit = _ARouter.init(application);//移交 _ARouter去 初始化

if (hasInit) {
_ARouter.afterInit();
}

_ARouter.logger.info(Consts.TAG, “ARouter init over.”);//打印 ARouter初始化日志
}
}

既然代码移交到了 _ARouter去 初始化,我们再来看

protected static synchronized boolean init(Application application) {
mContext = application;// Application的上下文
LogisticsCenter.init(mContext, executor);//移交逻辑中心进行初始化,并传入线城池对象
logger.info(Consts.TAG, “ARouter init success!”);//打印日志
hasInit = true;//标示是否初始化完成

// It’s not a good idea.
// if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// application.registerActivityLifecycleCallbacks(new AutowiredLifecycleCallback());
// }
return true;
}

继续往下走, LogisticsCenter 中进行初始化

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context; //静态持有Application的上下文
executor = tpe;//静态持有 线城池

try {
// These class was generate by arouter-compiler.
// 通过指定包名com.alibaba.android.arouter.routes,找到所有 编译期产生的routes目录下的类名(不包含装载类)
List classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);

for (String className : classFileNames) {//【组别的清单列表】com.alibaba.android.arouter.routes.ARouter$$Root
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);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {//【模块内的拦截器清单列表】com.alibaba.android.arouter.routes.ARouter$$Interceptors
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {//【Ioc的动作路由清单列表】com.alibaba.android.arouter.routes.ARouter$$Providers
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}

if (Warehouse.groupsIndex.size() == 0) {
logger.error(TAG, “No mapping files were found, check your configuration please!”);
}

if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), “LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]”, Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
}
} catch (Exception e) {
throw new HandlerException(TAG + “ARouter init logistics center exception! [” + e.getMessage() + “]”);
}
}

首先,我们要有一个概念。前文说了,我们断定初始化操作是要把 映射信息加载到内存,那么在哪里存储呢?这里就引入了 内存仓库Warehouse的概念,实际上它就是持有了多个statice 的Map对象,在下文我们给出其源码。

我们继续分析LogisticsCenter.init(),首先就是根据包名com.alibaba.android.arouter.routes找到了下边的所有类,那些类呢?所有模块下的这几个类:
【图4.3.1.1】
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
这些类都是编译期间通过“手动”的方式创建出来的,他们分别继承了IRouteRoot、IInterceptorGroup、IProviderGroup接口,实现了其loadInto()方法
接下来就是,根据反射将类实例化出来后,调用其loadInto()将对应的路由信息加载到 内存仓库中

需要注意的是:初始化阶段只加载了Root【组别的清单列表】,,并没有具体载入每个 Group 中包含的具体的路由节点清单,这就与 ARouter 的官方说明一致了:映射关系按组分类、多级管理,按需初始化,只有我们使用到具体的 Group 时,才会加载对应的 Group 列表。

class Warehouse {
// Cache route and metas
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();//【组别的清单列表】 包含了组名与对应组内的路由清单列表Class的映射关系(这里只存储了未导入到 routes在键盘每个的组)
static Map<String, RouteMeta> routes = new HashMap<>();//【组内的路由清单列表】包含了对应分组下的,路由URL与目标对象Class的映射关系;

// Cache provider
static Map<Class, IProvider> providers = new HashMap<>(); //缓存 IOC 目标class与已经创建了的对象 TODO ?全局应用共享一个IOc依赖注入对象?
static Map<String, RouteMeta> providersIndex = new HashMap<>();//【Ioc的动作路由清单列表】包含了使用依赖注入方式的某class的 路由URL 与class映射关系

// Cache interceptor
//【模块内的拦截器清单列表】包含了某个模块下的拦截器 与 优先级的映射关系
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>(“More than one interceptors use same priority [%s]”);
static List interceptors = new ArrayList<>();//已排序的拦截器实例对象

}

最后,别忘了最开始的_ARouter.afterInit(),根据 Ioc.ByName()方式获取 拦截器界面,注意这个拦截器并不是我们定义的拦截器,而是Arouter实现的拦截器逻辑,它持有我们定义的拦截器,可以理解为“拦截器截面控制器”

static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build(“/arouter/service/interceptor”).navigation();
}

至此,初始化工作全部完成,其中 内存仓库Warehouse缓存了全局应用的【组别的清单列表】、【Ioc的动作路由清单列表】、【模块内的拦截器清单列表】,3个以_index为结尾的Map对象

4.3.2 ARouter 运行时 API 调用过程分析

入口:

ARouter.getInstance()
.build(“/test/activity2”)
.navigation();

单例模式我们不过多赘述,我们看build的最终调用,其使用了代理类_ARouter的build()并构建和返回PostCard对象,先简单的提一下 一个Postcard 对象就对应了一次路由请求,该对象作用于本次路由全过程

public Postcard ARouterbuild(String path) {
return _ARouter.getInstance().build(path);
}

protected Postcard _ARouter.build(String path) {
if (TextUtils.isEmpty(path)) {//如果路径为null
throw new HandlerException(Consts.TAG + “Parameter is invalid!”);
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);//通过ARouter的Ioc方式(IProvider的ByType())方式找到 动态修改路由类
if (null != pService) {
path = pService.forString(path); //如果全局应用有实现 PathReplaceService.class接口,则执行 “运行期动态修改路由”逻辑。生成转换后的路由
}
return build(path, extractGroup(path));
}
}

其实整个build的过程分为两个顺序部分:

  1. 使用 Ioc byType()方式寻找PathReplaceService.class接口的实现类,以期实现 “运行期动态修改路”
  2. 正常的本次路由导航
4.3.2.1 运行期动态修改路由PathReplaceService的实现(IOC 也就是IProvider.byType() = navigation(class)的实现方式,用于获取 路由目标实例)

在_ARouter的build()中又调用了ARouter的T navigation(Class

protected T _ARouter.navigation(Class<? extends T> service) {
try {
Postcard postcard = LogisticsCenter.buildProvider(service.getName());

// Compatible 1.0.5 compiler sdk.
if (null == postcard) { // No service, or this service in old version.
postcard = LogisticsCenter.buildProvider(service.getSimpleName());
}

LogisticsCenter.completion(postcard);
return (T) postcard.getProvider();
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
return null;
}
}

//1. 从内存仓库的【Ioc的动作路由清单列表】中找到,对应Name对应的 路由元信息
//2. 根据路由元信息 生成 Postcard对象,赋值其 路径URL 和 组名 信息
public static Postcard LogisticsCenter.buildProvider(String serviceName) {
RouteMeta meta = Warehouse.providersIndex.get(serviceName);

if (null == meta) {
return null;
} else {
return new Postcard(meta.getPath(), meta.getGroup());
}
}

找到Postcard后,执行 LogisticsCenter.completion(postcard)函数,这个函数是一个至关重要的函数,其在于完善postcard对象,而后调用其getProvider()获取目标对象实例。具体的细节我们在4.2.2.2中讲解

4.3.2.2 一次简单的路由导航

入口:_ARouter.build(path, extractGroup(path)), 其中 _ARouter.extractGroup(string)从路径中找到,默认的 分组,也就是 一级路径,例如”/11/22”—11 可以看到,该函数也执行了一次 “4.3.2.1 运行期动态修改路由”逻辑,这是由于,该函数可以被单独调用所必须要求的

//根据路径名 和 分组名构建 PostCard对象
protected Postcard _ARouter.build(String path, String group) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + “Parameter is invalid!”);
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return new Postcard(path, group);
}
}

在得到Postcard对象后,调用其navigation()函数,我们再看看PostCard类中的该函数

public Object navigation() {
return navigation(null);
}
public Object navigation(Context context) {
return navigation(context, null);
}
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}

可见最终,还是调用ARouter中的navigation方法:

public Object ARouter.navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}

//一次路由跳转的最终调用函数,包含 查找回调的调用、拦截器处理、绿色通道校验、和具体路由操作
protected Object _ARouter.navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
LogisticsCenter.completion(postcard);//【1】完善postcard。当前只有 path和group
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());

if (debuggable()) { // Show friendly tips for user.
Toast.makeText(mContext, “There’s no route matched!\n” +
" Path = [" + postcard.getPath() + “]\n” +
" Group = [" + postcard.getGroup() + “]”, Toast.LENGTH_LONG).show();
}

if (null != callback) {
callback.onLost(postcard);//【2】执行到这里,触发查找失败
} else { // No callback for this invoke, then we use the global degrade service.
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);//【2】执行到这里,使用IOc.byType()的方式 全局降级策略的实现
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}

return null;
}

if (null != callback) {//【2】执行到这里,说明找到了路由元信息,触发 路由查找的回调
callback.onFound(postcard);
}

if (!postcard.isGreenChannel()) {//【3】绿色通道校验
// It must be run in async thread, maybe interceptor cost too mush time made ANR.
interceptorService.doInterceptions(postcard, new InterceptorCallback() {//调用拦截器截面控制器,遍历内存仓库的自定义拦截器,并在异步线程中执行拦截函数
/**

  • Continue process
  • @param postcard route meta
    */
    @Override
    public void onContinue(Postcard postcard) {
    _navigation(context, postcard, requestCode, callback);//【4】根据 路由类型执行具体路由操作
    }

/**

  • Interrupt process, pipeline will be destory when this method called.
  • @param exception Reson of interrupt.
    */
    @Override
    public void onInterrupt(Throwable exception) {
    if (null != callback) {
    callback.onInterrupt(postcard);
    }

logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
return _navigation(context, postcard, requestCode, callback);//【4】根据 路由类型执行具体路由操作
}

return null;
}

具体的路由操作是交由 _ARouter._navigation(“`)执行的

//根据 路由类型执行具体路由操作
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = null == context ? mContext : context;

switch (postcard.getType()) {
case ACTIVITY: //【1】如果是Acitvity,则实现Intent跳转
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());

// Set flags.
int flags = postcard.getFlags();
if (-1 != flags) {

总结

开发是面向对象。我们找工作应该更多是面向面试。哪怕进大厂真的只是去宁螺丝,但你要进去得先学会面试的时候造飞机不是么?

作者13年java转Android开发,在小厂待过,也去过华为,OPPO等,去年四月份进了阿里一直到现在。等大厂待过也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

960页全网最全Android开发笔记

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

的只是去宁螺丝,但你要进去得先学会面试的时候造飞机不是么?

作者13年java转Android开发,在小厂待过,也去过华为,OPPO等,去年四月份进了阿里一直到现在。等大厂待过也面试过很多人。深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。

这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

相信它会给大家带来很多收获:

[外链图片转存中…(img-B4FhoD0y-1715764482252)]

[外链图片转存中…(img-0BYpRdFl-1715764482253)]

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

当程序员容易,当一个优秀的程序员是需要不断学习的,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和能力提升中甩开同龄人。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值