注册 登录
  • 欢迎访问Sharezer Blog

Android(阿里)路由框架ARouter学习

Android sharezer 45次浏览 未收录 0个评论

1 背景

什么是路由框架?说简单点就是映射页面跳转关系的,当然它也包含跳转相关的一切功能。

1.1 原生方案的不足

我们所使用的原生路由方案一般是通过显式intent和隐式intent两种方式实现的,均存在一定意义上的缺陷。

显式,即直接指定需要打开的activity对应的类。 以下多种方式都是一样的,实际上都是设置Component直接指定Activity类的显式Intent 。

// 1、构造方法传入Component,最常用的方式
Intent intent = new Intent(this, SecondActivity.class);  
startActivity(intent); 

// 2、setComponent方法
ComponentName componentName = new ComponentName(this, SecondActivity.class);  
// 或者ComponentName componentName = new ComponentName(this, "com.example.app016.SecondActivity");  
// 或者ComponentName componentName = new ComponentName(this.getPackageName(), "com.example.app016.SecondActivity");  
Intent intent = new Intent();  
intent.setComponent(componentName);  
startActivity(intent); 

// 3、setClass/setClassName方法
Intent intent = new Intent();  
intent.setClass(this, SecondActivity.class);  
// 或者intent.setClassName(this, "com.example.app016.SecondActivity");  
// 或者intent.setClassName(this.getPackageName(), "com.example.app016.SecondActivity");  
startActivity(intent);  
  • 由于需要直接持有对应class,从而导致了强依赖关系,提高了耦合度

隐式,即不是像显式的那样直接指定需要调用的Activity,隐式不明确指定启动哪个Activity,而是设置Action、Data、Category,让系统来筛选出合适的Activity。

<activity  
    android:name="com.example.app016.SecondActivity">  
    <intent-filter>  
        <action android:name="abcdefg"/>  
        <category android:name="android.intent.category.DEFAULT"/>  
    </intent-filter>  
</activity>  
Intent intent = new Intent();  
intent.setAction("abcdefg");  
startActivity(intent);  
  • action等属性的定义在Manifest,导致了扩展性较差
  • 规则集中式管理,导致协作变得非常困难

原生的路由方案会出现跳转过程无法控制的问题,因为一旦使用了StartActivity()就无法插手其中任何环节了,只能交给系统管理,这就导致了在跳转失败的情况下无法降级,而是会直接抛出运营级的异常。

1.2 自定义路由框架的适用场景

  • 动态跳转:在一些复杂的业务场景下(比如电商),页面跳转需要较强的灵活性,很多功能都是运营人员动态配置的,比如下发一个活动页面,我们事先并不知道具体的目标页面,期望根据下发的数据自动的选择页面并进行跳转
  • 组件化:随着业务量的不断增长,app也会不断的膨胀,开发团队的规模和工作量也会逐渐增大,面对所衍生的64K问题、协作开发问题等,app一般都会走向组件化。组件化就是将APP按照一定的功能和业务拆分成多个组件module,不同的组件独立开发,组件化不仅能够提供团队的工作效率,还能够提高应用性能。而组件化的前提就是解耦,那么我们首先要做的就是解耦页面之间的依赖关系
  • Native与H5的问题:现在的APP很少是纯Native的,也很少会有纯H5的,一般情况下都是将两者进行结合。这时候就需要非常便捷并且统一的跳转方案,因为在H5中是无法使用StartActivity()跳转到Native页面的,而从Native跳转到H5页面也只能通过配置浏览器的方式实现

1.3 ARouter自定义路由框架的设想

Android(阿里)路由框架ARouter学习

一款路由框架至少要满足以下3个功能:

  • 分发:把一个URL或者请求按照一定的规则分配给一个服务或者页面来处理,这个流程就是分发,分发是路由框架最基本的功能,当然也可以理解成为简单的跳转。
  • 管理:将组件和页面按照一定的规则管理起来,在分发的时候提供搜索、加载、修改等操作,这部分就是管理,也是路由框架的基础,上层功能都是建立在管理之上。
  • 控制:就像路由器一样,路由的过程中,会有限速、屏蔽等一些控制操作,路由框架也需要在路由的过程中,对路由操作做一些定制性的扩展,比方刚才提到的AOP,后期的功能更新,也是围绕这个部分来做的。

2 概述

作为阿里开发团队的产品,ARouter在2016年年底就已经出现,并在2017年3月份的技术峰会上由阿里云资深开发工程师刘志龙公开分享,目前在GitHub具有2k+ star, ARouter的开发团队质量、后期维护都是相对比较稳定的。

ARouter不仅实现了对基础组件的路由,而且其具有的Ioc功能也实现了对动作的路由,也就是跨模块API调用。可以理解为“通过URL找Class能实现的业务,ARouter都能实现”。

2.1 优势

  • 使用注解,实现了映射关系自动注册 与 分布式路由管理
  • 编译期间处理注解,并生成映射文件,没有使用反射,不影响运行时性能
  • 映射关系按组分类、多级管理,按需初始化
  • 灵活的降级策略,每次跳转都会回调跳转结果,避免StartActivity()一旦失败将会抛出运营级异常
  • 自定义拦截器,自定义拦截顺序,可以对路由进行拦截,比如登录判断和埋点处理
  • 支持依赖注入,可单独作为依赖注入框架使用,从而实现 跨模块API调用对动作的路由
  • 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
  • 支持获取Fragment
  • 支持多模块使用,支持组件化开发
  • 支持多种方式配置转场动画
  • 支持MultiDex(Google方案)

2.2 劣势

  • 不支持插件化 [已纳入开发计划]
  • 不支持生成可读的映射关系文档 [已纳入开发计划]
  • 不支持 多路径
  • 不支持 手动分配路由
  • 不支持 自定义路由匹配规则

3 使用

官方文档已经很清晰易懂,详见https://github.com/alibaba/ARouter

在这里简单列举下:

3.1 添加依赖和配置

android {
    defaultConfig {
    ...
    javaCompileOptions {
        annotationProcessorOptions {
        arguments = [ moduleName : project.getName() ]
        }
    }
    }
}

dependencies {
    // 替换成最新版本, 需要注意的是api
    // 要与compiler匹配使用,均使用最新版可以保证兼容
    compile 'com.alibaba:arouter-api:x.x.x'
    // 注解处理器
    annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
    ...
}
// 旧版本gradle插件(< 2.2),可以使用apt插件

3.2 添加注解

// 在支持路由的页面上添加注解(必选)
// 这里的路径需要注意的是至少需要有两级,/xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity {
    ...
}

3.3 初始化SDK

if (isDebug()) {           // 这两行必须写在init之前,否则这些配置在init过程中将无效
    ARouter.openLog();     // 打印日志
    ARouter.openDebug();   // 开启调试模式
}
ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化

3.4 界面跳转类

ARouter.getInstance().build("/test/activity").navigation();

ARouter.getInstance().build("/test/activity").navigation(this, requestCode);

/*这种使用URi的方式中,URi的Scheme 和 host不影响结果,可以随便设,关键的是path
*  - build(URI)会把URI解析为path,并把当前URI存入PostCard
*  - build(String)构造的PostCard不存储URI*/
Uri testUriMix = Uri.parse("xx://xxx/test/activity");
ARouter.getInstance().build(testUriMix)
                        .withString("name", "老王")
                        .withInt("age", 18)
                        .withBoolean("boy", true)
                        .withLong("high", 180)
                        .withString("url", "https://a.b.c")
                        .withParcelable("pac", testParcelable)
                        .withObject("obj", testObj)
                        .navigation();

ARouter.getInstance().build("/test/activity").navigation(Context mContext, int requestCode, NavigationCallback callback);

3.5 Ioc依赖注入类

Ioc依赖注入类

@Route(path = "/service/hello", name = "测试服务")
public class MyService implements IService {

    @Override
    public void sayHello( Context context ) {
        Toast.makeText(  context , "hello", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void init(Context context) {

    }
}
 //【ByName方式】根据 注解的name对HelloService进行注入
 ((MyService) ARouter.getInstance().build("/service/hello").navigation()).sayHello(this);

 //【ByType方式】MyService = 根据classType实现注入
 //当同一接口有多个实现的时候,必须使用byName的方式发现服务
 ARouter.getInstance().navigation(MyService.class).sayHello(this);

3.6 降解策略类

//【1】实现DegradeService(实现了全局降级逻辑, IOC.byType方式被_ARouter调用)
// 实现DegradeService接口,并加上一个Path内容任意的注解即可
@Route(path = "/xxx/xxx")
public class DegradeServiceImpl implements DegradeService {
  @Override
  public void onLost(Context context, Postcard postcard) {
    // do something.
  }

  @Override
  public void init(Context context) {

  }
}
//【2】单次,使用NavigationCallback
ARouter.getInstance().build("/xxx/xxx").navigation(this, new NavCallback() {
     @Override
     public void onFound(Postcard postcard) {
          Log.d("ARouter", "找到了");
     }

     @Override
     public void onLost(Postcard postcard) {
          Log.d("ARouter", "找不到了");
     }

     @Override
     public void onArrival(Postcard postcard) {
          Log.d("ARouter", "跳转完了");
     }

     @Override
      public void onInterrupt(Postcard postcard) {
           Log.d("ARouter", "被拦截了");
      }
});

3.7 interceptor拦截器

@Interceptor(priority = 4)
public class TestInterceptor implements IInterceptor {

    @Override
    public void process(final Postcard postcard, final InterceptorCallback callback) {
        Log.d("interceptor",postcard.getPath() + ".." + postcard.getGroup());
    }

    @Override
    public void init(Context context) {
        Log.d("init",TestInterceptor.class.getSimpleName() + " has been inited");
    }
}

4 分析

4.1 工程结构

Android(阿里)路由框架ARouter学习

  • arouter-annotation: ARouter路由框架所使用的全部注解,及其相关类
  • arouter-compiler:注解编译处理器,引入“arouter-annotation”,在编译器把注解标注的相关目标类生成映射文件
  • arouter-api:实现路由控制

4.2 arouter-annotation注解

Android(阿里)路由框架ARouter学习

@Target:注解的作用目标

@Target(ElementType.TYPE)   //接口、类、枚举、注解
@Target(ElementType.FIELD) //字段、枚举的常量
@Target(ElementType.METHOD) //方法
@Target(ElementType.PARAMETER) //方法参数
@Target(ElementType.CONSTRUCTOR)  //构造函数
@Target(ElementType.LOCAL_VARIABLE)//局部变量
@Target(ElementType.ANNOTATION_TYPE)//注解
@Target(ElementType.PACKAGE) ///包   

@Retention:注解的保留位置

@Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS)     // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME)  // 注解会在class字节码文件中存在,在运行时可以通过反射获取到

4.2.1 @Route路由注解

@Route 是 ARouter 最重要的注解,也是路由最基本的节点,该注解主要用于描述路由中的路径URL信息,使用该注解标注的类将被自动添加至路由表中。

值得说明的一点是 ARouter 并非仅提供页面(Activity)的路由功能,还可以用来路由模块想要暴露给其他模块调用的接口。

也就是说 @Route 不仅可用于 Activity 类,还可用于模块对外接口的实现类,实现类似于 AIDL 的功能,也就是IoC

// 注解的作用目标
@Target({ElementType.TYPE})
// 编译的保留位置
@Retention(RetentionPolicy.CLASS)
public @interface Route {

    //路径URL字符串
    String path();

    //组名,默认为一级路径名;一旦被设置,跳转时必须赋值
    String group() default "";

    //该路径的名称,用于产生JavaDoc
    String name() default "undefined";

    //额外配置的开关信息;譬如某些页面是否需要网络校验、登录校验等
    int extras() default Integer.MIN_VALUE;

    //该路径的优先级
    int priority() default -1;
}

4.2.2 @Interceptor拦截器注解

@Interceptor 是拦截器注解,拦截器是全应用全局的,不分module,只要集成进apk就起效

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
    //该拦截器的优先级
    int priority();

    //该拦截器的名称,用于产生JavaDoc
    String name() default "Default";
}

4.2.3 @Autowired自动装载注解

@Autowired 是页面跳转时参数传递用的。目标Class中使用该注解标志的变量,会在页面被路由打开的时候,在调用Inject(“`)后自动赋予传递的参数值

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

4.2.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.3 arouter-compiler注解编译器

Android(阿里)路由框架ARouter学习

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

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

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

routes目录下

  • 工程名\$\$Group\$\$分组名 【组内的路由清单列表】
  • 工程名\$\$Root\$\$\$模块名 【组别的清单列表】
  • 工程名\$\$Providers\$\$模块名 【Ioc的动作路由清单列表】
  • 工程名\$\$Interceptors\$\$模块名 【模块内的拦截器清单列表】

Android(阿里)路由框架ARouter学习

4.4 arouter-api路由控制

Android(阿里)路由框架ARouter学习

Android(阿里)路由框架ARouter学习

最上层是Launcher层,这一层是开发者可以直接用到的,其实所有的API都是在这一层中。
在Launcher层的下一层就是Facade层,从上图中可以看到Facade层也是绿色的,表示这一层也是可以被外部调用的,Facade层其实包含了三部分,分别是: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.4.1 ARouter init 初始化过程

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<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的概念,实际上它就是持有了多个static的Map对象。

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<IInterceptor> 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.4.2 ARouter 运行时 API 调用过程

ARouter.getInstance().build("/test/activity")
                     .navigation();

其使用了代理类_ARouter的build()并构建和返回PostCard对象,先简单的提一下 一个Postcard 对象就对应了一次路由请求,该对象作用于本次路由全过程。

public final class Postcard extends RouteMeta {
    // Base
    private Uri uri;                //Nullable,仅当根据Uri方式跳转时有值
    private Object tag;             // 拦截器中出现异常的tag
    private Bundle mBundle;         // Intent中存储的Bundle以传递信息
    private int flags = -1;         // Intenet中的flag,譬如Clear_TOP
    private int timeout = 300;      //  TimeUnit.Second  拦截器的耗时阈值
    private IProvider provider;     // Nullable 当使用Ioc的时候,结果会导致该变量被赋值为目标对象实例
    private boolean greenChannel;   // 是否跳过拦截器
    private SerializationService serializationService;//用于对象的json转换的工具,在构建参数withObject是会被ByType方式赋值进来

    // Animation
    private Bundle optionsCompat;    // The transition animation of activity
    private int enterAnim;
    private int exitAnim;
}   
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));
        }
    }

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) {
                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 ((0 != postcard.getEnterAnim() || 0 != 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: //【2】如果是Ioc,则返回目标对象实例
            return postcard.getProvider();
        case BOARDCAST: //【4】如果是board,则返回实例
        case CONTENT_PROVIDER: //【5】如果是Cp,则返回实例
        case FRAGMENT://【6】如果是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;
}

5 参考内容

开源最佳实践:Android平台页面路由框架ARouter

ARouter github

6 扩展阅读

Android模块开发之APT技术

ARouter源码分析初始化以及跳转

Ioc控制反转–依赖注入类

ARouter 源码历险记 (一)

ARouter 源码历险记 (二)

ARouter 源码历险记 (三)

ARouter 源码历险记 (四)

ARouter 源码历险记 (五)


Sharezer , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明Android(阿里)路由框架ARouter学习
喜欢 (0)
[liangshaoze@sina.cn]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址