一、路由方案
原生的路由方案缺点:
显式:直接的类依赖,耦合严重
隐式:规则集中式管理,协作困难
Manifest扩展性较差
跳转过程无法控制
失败无法降级
ARouter的优势:
使用注解,实现了映射关系自动注册 与 分布式路由管理
编译期间处理注解,并生成映射文件,没有使用反射,不影响运行时性能
映射关系按组分类、多级管理,按需初始化
灵活的降级策略,每次跳转都会回调跳转结果,避免StartActivity()一旦失败将会抛出运营级异常
自定义拦截器,自定义拦截顺序,可以对路由进行拦截,比如登录判断和埋点处理
支持依赖注入,可单独作为依赖注入框架使用,从而实现 跨模块API调用
支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
支持获取Fragment
支持多模块使用,支持组件化开发
…….
这么多好处,是时候来了解一下 ARouter
了。
二、ARouter框架
上图是根据 ARouter
一次基本的路由导航过程,整理的基本框架图,涉及到主要流程,下面进行详细介绍。
三、路由管理
1.注册
通过注解,在编译时收集使用了注解的类或变量并经过Android Process Tool处理进行统一管理。
包含三种注解@Autowired,@Interceptor,@Route。
@Route
注解定义
1
2
3
4
5
|
String path(); //路径URL字符串 String group() default "" ; //组名,默认为一级路径名;一旦被设置,跳转时必须赋值 String name() default "undefined" ; //该路径的名称,用于产生JavaDoc int extras() default Integer.MIN_VALUE; //额外配置的开关信息;譬如某些页面是否需要网络校验、登录校验等 int priority() default - 1 ; //该路径的优先级 |
实现 @Route 注解
1
2
|
BlankFragment @Route (path = "/test/fragment" ) Test1Activity @Route (path = "/test/activity1" ) |
该注解主要用于描述路由中的路径URL信息,使用该注解标注的类将被自动添加至路由表中。
@Autowired
注解定义
1
2
|
boolean required() default false ; String desc() default "No desc." ; |
实现 @Autowired 注解
1
2
3
4
|
@Autowired int age = 10 ; @Autowired HelloService helloService; |
该注解是在页面跳转时参数传递用的。目标Class中使用该注解标志的变量,会在页面被路由打开的时候,在调用 inject()
后自动赋予传递的参数值。
@Interceptor
注解定义
1
2
|
int priority(); //该拦截器的优先级 String name() default "Default" ; //该拦截器的名称,用于产生JavaDoc |
实现 @Interceptor 注解
一般应用于IInterceptor的实现类,是路由跳转过程中的拦截器,不分module,应用全局。
1
2
3
4
5
6
7
|
@Interceptor (priority = 7 ) public class Test1Interceptor implements IInterceptor { @Override public void process( final Postcard postcard, final InterceptorCallback callback) { ............ } } |
2.收集
在编译期间自动生成映射文件,arouter-compiler实现了一些注解处理器,目标在于生成映射文件与辅助文件。
三种类型的注解处理器,都实现了 AbstractProcessor
,主要功能如下:
首先通过注解处理器扫出被标注的类文件
按照不同种类的源文件进行分类
按照固定的命名格式生成映射文件
这样就可以在运行期初始化的时候通过固定的包名来加载映射文件。
关于注解处理的源码详解见 阿里路由框架--ARouter 源码解析之Compiler 。
以官方demo为例,通过注解处理器,按照固定的命名格式生成映射文件。
具体以 ARouter$$Root$$app
为例,看下注解处理器生成的类文件的内容:
1
2
3
4
5
6
7
|
public class ARouter$$Root$$app implements IRouteRoot { @Override public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) { routes.put( "service" , ARouter$$Group$$service. class ); routes.put( "test" , ARouter$$Group$$test. class ); } } |
通过调用 loadInto()
方法将其管理的 group
类文件加载到集合中,方便后续路由查找。
3.加载
前面的收集都是在编译器处理获得的,那么加载就是到了运行期。 ARouter
为了避免内存和性能损耗,提出了“分组管理,按需加载”的方式。在前面的编译处理的过程中,已经按照不同种类生成对应的映射文件。
以官方demo为示例,一个app模块有一个Root结点,管理各个Group分组,每个Group分组下有着多个界面;此外app模块下还有着Interceptor结点,以及provider结点。
其中Interceptor结点对应于自定义的拦截器,provider结点对应于IOC,以实现跨模块API调用。
ARouter
在初始化的时候只会一次性地加载所有的root结点,而不会加载任何一个Group结点,这样就会极大地降低初始化时加载结点的数量。当某一个分组下的某一个页面第一次被访问的时候,整个分组的全部页面都会被加载进去。
初始加载
ARouter
其实是一个代理类,它的所有函数实现都交给 _ARouter
去实现,两个都是单例模式。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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
的初始化方法
1
2
3
4
5
6
7
8
9
10
11
12
|
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
的初始化方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
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<String> 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
缓存了全局应用的组别的清单列表、IOC的动作路由清单列表、模块内的拦截器清单列表,3个map对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class Warehouse { // Cache route and metas static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); //组别的列表 包含了组名与对应组内的路由清单列表Class的映射关系 static Map<String, RouteMeta> routes = new HashMap<>(); //组内的路由列表 包含了对应分组下的,路由URL与目标对象Class的映射关系 // Cache provider static Map<Class, IProvider> providers = new HashMap<>(); //缓存IOC 目标class与已经创建了的对象 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<IInterceptor> interceptors = new ArrayList<>(); //已排序的拦截器实例对象 } |
四、路由查找
1
|
ARouter.getInstance().build( "/test/activity2" ).navigation();</pre> |
以上述例子为例,看一下 ARouter
路由查找的过程。首先看一下 build
过程
1. build()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public Postcard build(String path) { return _ARouter.getInstance().build(path); } protected Postcard build(String path) { if (TextUtils.isEmpty(path)) { throw new HandlerException(Consts.TAG + "Parameter is invalid!" ); } else { PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService. class ); if ( null != pService) { path = pService.forString(path); } return build(path, extractGroup(path)); } } |
其使用了代理类_ARouter的build()并构建和返回PostCard对象。 一个Postcard对象就对应了一次路由请求,作用于本次路由全过程。
这部分代码主要包含两个部分:
- 使用 IOC byType()方式寻找PathReplaceService.class接口的实现类,该实现类的作用就是实现 “运行期动态修改路由”。
- 继续进行本次路由导航
首先来看一下PathReplaceService.class接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public interface PathReplaceService extends IProvider { /** * For normal path. * * @param path raw path */ String forString(String path); /** * For uri type. * * @param uri raw uri */ Uri forUri(Uri uri); } |
主要包含forString()和forUri两个方法,针对路径进行预处理,实现 “运行期动态修改路由”。
接下下,继续通过build(path, extractGroup(path))进行路由导航,其中extractGroup()是从路径中获取默认的分组信息。
然后build()方法会返回一个Postcard对象,并把对应的路径和分组信息传入该对象。
分析完上面的过程,下面来详细看下PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);中的navigation()方法,该方法实际调用了代理类_ARouter的navigation(Class<? extends T> service)方法。
2. navigation(Class<? extends T> service)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
protected <T> T 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 ; } } |
首先 LogisticsCenter.buildProvider(service.getName())
根据 Warehouse
保存的 providersIndex
的信息查找并构建返回一个 PostCard
对象
然后执行 LogisticsCenter.completion(postcard)
,该方法会根据 Warehouse
保存的 routes
的路由信息完善postcard对象,该方法在下面还会出现,到时候具体介绍
再回到上文介绍 ARouter.getInstance().build("/test/activity2").navigation()
,返回 PostCard
对象后,开始调用对应的 navigation()
方法。
3. navigation()
观察 PostCard
中的该方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
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); } public void navigation(Activity mContext, int requestCode) { navigation(mContext, requestCode, null ); } public void navigation(Activity mContext, int requestCode, NavigationCallback callback) { ARouter.getInstance().navigation(mContext, this , requestCode, callback); } |
最终调用了 ARouter
中的 navigation()
方法,在其中其实是调用了 _ARouter
中的 navigation()
方法。
该方法包含查找回调的调用、降级处理、拦截器处理具体路由操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
protected Object navigation( final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { try { LogisticsCenter.completion(postcard); } 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); //触发路由查找失败 } else { // No callback for this invoke, then we use the global degrade service. DegradeService degradeService = ARouter.getInstance().navigation(DegradeService. class ); if ( null != degradeService) { degradeService.onLost(context, postcard); } } return null ; } //找到了路由元信息,触发路由查找的回调 if ( null != callback) { callback.onFound(postcard); } //绿色通道校验 需要拦截处理 if (!postcard.isGreenChannel()) { // 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); } /** * 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); } return null ; } |
其中最重要的两个方法就是 LogisticsCenter.completion()
和 _navigation()
,下面详细介绍。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
public synchronized static void completion(Postcard postcard) { if ( null == postcard) { throw new NoRouteFoundException(TAG + "No postcard!" ); } //根据路径URL获取到路径元信息 RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath()); if ( null == routeMeta) { // Maybe its does't exist, or didn't load. //可能没加载组内清单路径,从组别的清单列表拿到对应组 Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta. if ( null == groupMeta) { throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]" ); } else { //将该组的组内清单列表加入到内存仓库中,并把组别移除 try { if (ARouter.debuggable()) { logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]" , postcard.getGroup(), postcard.getPath())); } IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance(); iGroupInstance.loadInto(Warehouse.routes); Warehouse.groupsIndex.remove(postcard.getGroup()); if (ARouter.debuggable()) { logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]" , postcard.getGroup(), postcard.getPath())); } } catch (Exception e) { throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]" ); } completion(postcard); // 再次触发完善逻辑 } } else { postcard.setDestination(routeMeta.getDestination()); //目标 class postcard.setType(routeMeta.getType()); //路由类 postcard.setPriority(routeMeta.getPriority()); //路由优先级 postcard.setExtra(routeMeta.getExtra()); //额外的配置开关信息 Uri rawUri = postcard.getUri(); if ( null != rawUri) { // Try to set params into bundle. Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri); Map<String, Integer> paramsType = routeMeta.getParamsType(); if (MapUtils.isNotEmpty(paramsType)) { // Set value by its type, just for params which annotation by @Param for (Map.Entry<String, Integer> params : paramsType.entrySet()) { setValue(postcard, params.getValue(), params.getKey(), resultMap.get(params.getKey())); } // Save params name which need auto inject. postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray( new String[]{})); } // Save raw uri postcard.withString(ARouter.RAW_URI, rawUri.toString()); } switch (routeMeta.getType()) { case PROVIDER: // if the route is provider, should find its instance // Its provider, so it must implement IProvider Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination(); IProvider instance = Warehouse.providers.get(providerMeta); if ( null == instance) { // There's no instance of this provider IProvider provider; try { provider = providerMeta.getConstructor().newInstance(); provider.init(mContext); Warehouse.providers.put(providerMeta, provider); instance = provider; } catch (Exception e) { throw new HandlerException( "Init provider failed! " + e.getMessage()); } } postcard.setProvider(instance); postcard.greenChannel(); // Provider should skip all of interceptors break ; case FRAGMENT: postcard.greenChannel(); // Fragment needn't interceptors default : break ; } } } |
该方法就是完善 PostCard
,来实现一次路由导航。
接下来介绍另一个方法 _navigation()
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
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: //如果是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) { intent.setFlags(flags); } else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } // Navigation in main looper. new Handler(Looper.getMainLooper()).post( new Runnable() { @Override public void run() { if (requestCode > 0 ) { // Need start for result ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle()); } else { ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle()); } if ((- 1 != postcard.getEnterAnim() && - 1 != postcard.getExitAnim()) && currentContext instanceof Activity) { // Old version. ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim()); } if ( null != callback) { // Navigation over. callback.onArrival(postcard); } } }); break ; case PROVIDER: //如果是IOC,则返回目标对象实例 return postcard.getProvider(); case BOARDCAST: case CONTENT_PROVIDER: case FRAGMENT: //如果是Fragment,则返回实例,并填充bundle Class fragmentMeta = postcard.getDestination(); try { Object instance = fragmentMeta.getConstructor().newInstance(); if (instance instanceof Fragment) { ((Fragment) instance).setArguments(postcard.getExtras()); } else if (instance instanceof android.support.v4.app.Fragment) { ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras()); } return instance; } catch (Exception ex) { logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace())); } case METHOD: case SERVICE: default : return null ; } return null ; } |
至此我们就完成了一次路由跳转。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://www.jianshu.com/p/915d44720073