2023年6月28日发(作者:)
Android路由框架ARouter分析⼀、路由⽅案 原⽣的路由⽅案缺点:显式:直接的类依赖,耦合严重隐式:规则集中式管理,协作困难Manifest扩展性较差跳转过程⽆法控制失败⽆法降级ARouter的优势:使⽤注解,实现了映射关系⾃动注册 与 分布式路由管理编译期间处理注解,并⽣成映射⽂件,没有使⽤反射,不影响运⾏时性能映射关系按组分类、多级管理,按需初始化灵活的降级策略,每次跳转都会回调跳转结果,避免StartActivity()⼀旦失败将会抛出运营级异常⾃定义拦截器,⾃定义拦截顺序,可以对路由进⾏拦截,⽐如登录判断和埋点处理⽀持依赖注⼊,可单独作为依赖注⼊框架使⽤,从⽽实现 跨模块API调⽤⽀持直接解析标准URL进⾏跳转,并⾃动注⼊参数到⽬标页⾯中⽀持获取Fragment⽀持多模块使⽤,⽀持组件化开发…….这么多好处,是时候来了解⼀下
ARouter 了。⼆、ARouter框架上图是根据
ARouter ⼀次基本的路由导航过程,整理的基本框架图,涉及到主要流程,下⾯进⾏详细介绍。三、路由管理 1.注册通过注解,在编译时收集使⽤了注解的类或变量并经过Android Process Tool处理进⾏统⼀管理。包含三种注解@Autowired,@Interceptor,@Route。@Route注解定义String path();//路径URL字符串String group() default "";//组名,默认为⼀级路径名;⼀旦被设置,跳转时必须赋值String name() default "undefined";//该路径的名称,⽤于产⽣JavaDocint extras() default _VALUE;//额外配置的开关信息;譬如某些页⾯是否需要⽹络校验、登录校验等int priority() default -1;//该路径的优先级实现 @Route 注解BlankFragment @Route(path = "/test/fragment")
Test1Activity @Route(path = "/test/activity1")该注解主要⽤于描述路由中的路径URL信息,使⽤该注解标注的类将被⾃动添加⾄路由表中。@Autowired注解定义boolean required() default false;String desc() default "No desc.";实现 @Autowired 注解@Autowiredint age = 10;@AutowiredHelloService helloService;该注解是在页⾯跳转时参数传递⽤的。⽬标Class中使⽤该注解标志的变量,会在页⾯被路由打开的时候,在调⽤
inject() 后⾃动赋予传递的参数值。@Interceptor注解定义int priority();//该拦截器的优先级String name() default "Default";//该拦截器的名称,⽤于产⽣JavaDoc实现 @Interceptor 注解⼀般应⽤于IInterceptor的实现类,是路由跳转过程中的拦截器,不分module,应⽤全局。@Interceptor(priority = 7)public class Test1Interceptor implements IInterceptor { @Override public void process(final Postcard postcard, final InterceptorCallback callback) { ............ }}2.收集在编译期间⾃动⽣成映射⽂件,arouter-compiler实现了⼀些注解处理器,⽬标在于⽣成映射⽂件与辅助⽂件。三种类型的注解处理器,都实现了
AbstractProcessor ,主要功能如下:⾸先通过注解处理器扫出被标注的类⽂件按照不同种类的源⽂件进⾏分类按照固定的命名格式⽣成映射⽂件这样就可以在运⾏期初始化的时候通过固定的包名来加载映射⽂件。关于注解处理的源码详解见 。以官⽅demo为例,通过注解处理器,按照固定的命名格式⽣成映射⽂件。具体以
ARouter$$Root$$app 为例,看下注解处理器⽣成的类⽂件的内容:public class ARouter$$Root$$app implements IRouteRoot { @Override public void loadInto(Map
loadInto() ⽅法将其管理的
group 类⽂件加载到集合中,⽅便后续路由查找。3.加载前⾯的收集都是在编译器处理获得的,那么加载就是到了运⾏期。
ARouter 为了避免内存和性能损耗,提出了“分组管理,按需加载”的⽅式。在前⾯的编译处理的过程中,已经按照不同种类⽣成对应的映射⽂件。以官⽅demo为⽰例,⼀个app模块有⼀个Root结点,管理各个Group分组,每个Group分组下有着多个界⾯;此外app模块下还有着Interceptor结点,以及provider结点。其中Interceptor结点对应于⾃定义的拦截器,provider结点对应于IOC,以实现跨模块API调⽤。ARouter 在初始化的时候只会⼀次性地加载所有的root结点,⽽不会加载任何⼀个Group结点,这样就会极⼤地降低初始化时加载结点的数量。当某⼀个分组下的某⼀个页⾯第⼀次被访问的时候,整个分组的全部页⾯都会被加载进去。初始加载ARouter 其实是⼀个代理类,它的所有函数实现都交给
_ARouter 去实现,两个都是单例模式。public static void init(Application application) {//静态函数进⾏初始化,不依赖对象 if (!hasInit) { logger = _; //持有 ⽇志打印的 全局静态标量 _(, "ARouter init start.");//打印 ARouter初始化⽇志 hasInit = _(application);//移交 _ARouter去 初始化 if (hasInit) { _nit(); } _(, "ARouter init over.");//打印 ARouter初始化⽇志 }}继续看⼀下
_ARouter 的初始化⽅法protected static synchronized boolean init(Application application) { mContext = application;// Application的上下⽂ (mContext, executor);//移交逻辑中⼼进⾏初始化,并传⼊线城池对象 (, "ARouter init success!");//打印⽇志 hasInit = true;//标⽰是否初始化完成 // It's not a good idea. // if (_INT > N__CREAM_SANDWICH) { // erActivityLifecycleCallbacks(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. // 通过指定包名,找到所有 编译期产⽣的routes⽬录下的类名(不包含装载类) List
Warehouse 缓存了全局应⽤的组别的清单列表、IOC的动作路由清单列表、模块内的拦截器清单列表,3个map对象。class Warehouse { // Cache route and metas static Map
static Map
}四、路由查找tance().build("/test/activity2").navigation();以上述例⼦为例,看⼀下
ARouter 路由查找的过程。⾸先看⼀下
build 过程1.
build()public Postcard build(String path) { return _tance().build(path);}protected Postcard build(String path) { if (y(path)) { throw new HandlerException( + "Parameter is invalid!"); } else { PathReplaceService pService = tance().navigation(); if (null != pService) { path = ing(path); } return build(path, extractGroup(path)); } }其使⽤了代理类_ARouter的build()并构建和返回PostCard对象。 ⼀个Postcard对象就对应了⼀次路由请求,作⽤于本次路由全过程。这部分代码主要包含两个部分:1. 使⽤ IOC byType()⽅式寻找接⼝的实现类,该实现类的作⽤就是实现 “运⾏期动态修改路由”。2. 继续进⾏本次路由导航⾸先来看⼀下接⼝: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 = tance().navigation();中的navigation()⽅法,该⽅法实际调⽤了代理类_ARouter的navigation(Class extends T> service)⽅法。2.
navigation(Class extends T> service)protected
rovider(e()) 根据
Warehouse 保存的
providersIndex 的信息查找并构建返回⼀个
PostCard 对象然后执⾏
tion(postcard) ,该⽅法会根据
Warehouse 保存的
routes 的路由信息完善postcard对象,该⽅法在下⾯还会出现,到时候具体介绍再回到上⽂介绍
tance().build("/test/activity2").navigation() ,返回
PostCard 对象后,开始调⽤对应的
navigation() ⽅法。3.
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 tance().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) { tance().navigation(mContext, this, requestCode, callback); }最终调⽤了
ARouter 中的
navigation() ⽅法,在其中其实是调⽤了
_ARouter 中的
navigation() ⽅法。该⽅法包含查找回调的调⽤、降级处理、拦截器处理具体路由操作。protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { try { tion(postcard); } catch (NoRouteFoundException ex) { g(, sage()); if (debuggable()) { // Show friendly tips for user. xt(mContext, "There's no route matched!n" + " Path = [" + h() + "]n" + " Group = [" + up() + "]", _LONG).show(); }
if (null != callback) { (postcard);//触发路由查找失败 } else { // No callback for this invoke, then we use the global degrade service. DegradeService degradeService = tance().navigation(); if (null != degradeService) { (context, postcard); } } return null; } //找到了路由元信息,触发路由查找的回调 if (null != callback) { d(postcard); } //绿⾊通道校验 需要拦截处理 if (!nChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR. //调⽤拦截器截⾯控制器,遍历内存仓库的⾃定义拦截器,并在异步线程中执⾏拦截函数 rceptions(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) { rrupt(postcard); } (, "Navigation failed, termination by interceptor : " + sage()); } }); } else { return _navigation(context, postcard, requestCode, callback); } return null; }其中最重要的两个⽅法就是
tion() 和
_navigation() ,下⾯详细介绍。public synchronized static void completion(Postcard postcard) { if (null == postcard) { throw new NoRouteFoundException(TAG + "No postcard!"); } //根据路径URL获取到路径元信息 RouteMeta routeMeta = (h()); if (null == routeMeta) { // Maybe its does't exist, or didn't load. //可能没加载组内清单路径,从组别的清单列表拿到对应组 Class extends IRouteGroup> groupMeta = (up()); // Load route meta. if (null == groupMeta) { throw new NoRouteFoundException(TAG + "There is no route match the path [" + h() + "], in group [" + up() + "]"); } else { //将该组的组内清单列表加⼊到内存仓库中,并把组别移除 try { if (able()) { (TAG, (ault(), "The group [%s] starts loading, trigger by [%s]", up(), h())); } IRouteGroup iGroupInstance = structor().newInstance(); to(); (up()); if (able()) { (TAG, (ault(), "The group [%s] has already been loaded, trigger by [%s]", up(), h())); } } catch (Exception e) { throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + sage() + "]"); } completion(postcard); // 再次触发完善逻辑 } } else { tination(tination());//⽬标 class e(e());//路由类 ority(ority());//路由优先级 ra(ra());//额外的配置开关信息 Uri rawUri = (); if (null != rawUri) { // Try to set params into bundle. Map
PostCard ,来实现⼀次路由导航。接下来介绍另⼀个⽅法
_navigation() 。private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { final Context currentContext = null == context ? mContext : context; switch (e()) { case ACTIVITY://如果是Acitvity,则实现Intent跳转 // Build intent final Intent intent = new Intent(currentContext, tination()); ras(ras()); // Set flags. int flags = gs(); if (-1 != flags) { gs(flags); } else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag. gs(_ACTIVITY_NEW_TASK); } // Navigation in main looper. new Handler(nLooper()).post(new Runnable() { @Override public void run() { if (requestCode > 0) { // Need start for result ctivityForResult((Activity) currentContext, intent, requestCode, ionsBundle()); } else { ctivity(currentContext, intent, ionsBundle()); } if ((-1 != erAnim() && -1 != tAnim()) && currentContext instanceof Activity) { // Old version. ((Activity) currentContext).overridePendingTransition(erAnim(), tAnim()); } if (null != callback) { // Navigation over. val(postcard); } } }); break; case PROVIDER://如果是IOC,则返回⽬标对象实例 return vider(); case BOARDCAST: case CONTENT_PROVIDER: case FRAGMENT://如果是Fragment,则返回实例,并填充bundle Class fragmentMeta = tination(); try { Object instance = structor().newInstance(); if (instance instanceof Fragment) { ((Fragment) instance).setArguments(ras()); } else if (instance instanceof nt) { ((nt) instance).setArguments(ras()); } return instance; } catch (Exception ex) { (, "Fetch fragment instance error, " + StackTrace(ckTrace())); } case METHOD: case SERVICE: default: return null; } return null; }⾄此我们就完成了⼀次路由跳转。以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
发布者:admin,转转请注明出处:http://www.yc00.com/news/1687954310a60501.html
评论列表(0条)