Android 路由框架之阿里ARouter详解
阅读原文时间:2021年04月21日阅读:2

一、起源

  • 由于业务的增长提高了项目的复杂性,为了更好的适应团队开发,提高开发效率,实行组件化乃大势所趋。
  • 组件化可以让我们程序更容易的扩展、更方便的维护,更快捷的同步开发与更简单的单元调试。
  • 但是由于各个组件在不同的model甚至在不同的项目中,这样他们之间的通信就成了问题,而 ARouter的出现就是让组件间、模块间是实现完全的独立。
  • 并且后台还可以通过路由的机制控制Android界面调用实现远程控制和高度解耦。

二、是什么?

一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦


三、功能介绍

  1. 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
  2. 支持多模块工程使用
  3. 支持添加多个拦截器,自定义拦截顺序
  4. 支持依赖注入,可单独作为依赖注入框架使用
  5. 支持InstantRun
  6. 支持MultiDex(Google方案)
  7. 映射关系按组分类、多级管理,按需初始化
  8. 支持用户指定全局降级与局部降级策略
  9. 页面、拦截器、服务等组件均自动注册到框架
  10. 支持多种方式配置转场动画
  11. 支持获取Fragment
  12. 完全支持Kotlin以及混编
  13. 支持第三方 App 加固(使用 arouter-register 实现自动注册)
  14. 支持生成路由文档
  15. 提供 IDE 插件便捷的关联路径和目标类

四、原理解析

1、调用关系
  • 两个界面之间的跳转,调用关系如下:
  1. 注册:B界面先在ARouter里注册一个唯一标识;
  2. 查询:A界面调用时,ARouter会先查询B界面是否已注册;
  3. 整合:ARouter查询到B界面后,会将A界面传入信息进行整合;
  4. 调用:整合好信息后,会调起B界面并将传入信息传递到B界面。
2、流程解析
  • 界面跳转,arouter做了以下工作:


五、原理解析

  1. 从外部URL映射到内部页面,以及参数传递与解析
  2. 跨模块页面跳转,模块间解耦
  3. 拦截跳转过程,处理登陆、埋点等逻辑
  4. 跨模块API调用,通过控制反转来做组件解耦

六、基础功能

1.添加依赖和配置
android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

dependencies {
    // 替换成最新版本, 需要注意的是api
    // 要与compiler匹配使用,均使用最新版可以保证兼容
    compile 'com.alibaba:arouter-api:x.x.x'
    annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
}
2.初始化SDK
if (isDebug()) {           // 这两行必须写在init之前,否则这些配置在init过程中将无效
    ARouter.openLog();     // 打印日志
    ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化
3.简单跳转
  • 目标界面

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

    }

  • 跳转操作

    // 1. 应用内简单的跳转
    ARouter.getInstance().build("/test/activity").navigation();

4.带参跳转
  • 目标界面

    @Route(path = "/test/1")
    public class YourActivity extend Activity {
    //获取数据三种方式
    //1.通过Autowired注解表明key & 需要在onCreate中调用ARouter.getInstance().inject(this);配合使用
    // 通过name来映射URL中的不同参数
    @Autowired(name = "key1")
    public long data;

    //2.通过Autowired注解 & 将key1作为属性的名称   &  需要在onCreate中调用ARouter.getInstance().inject(this);配合使用
    @Autowired()
    public long key2;
    
    //3.通过Bundle获取
    getIntent().getExtras().getLong("key3")

    }

  • 跳转操作

    // 2. 跳转并携带参数
    ARouter.getInstance().build("/test/1")
    .withLong("key1", 666L)
    .withString("key2", "888")
    .withObject("key3", new Test("Jack", "Rose"))
    .navigation();


七、进阶用法

1.通过URL跳转

方法一

  • AndroidManifest.xml




        <action android:name="android.intent.action.VIEW"/>
    &lt;category android:name="android.intent.category.DEFAULT"/&gt;
    &lt;category android:name="android.intent.category.BROWSABLE"/&gt;
    </intent-filter>

  • 跳转操作

    // 新建一个Activity用于监听Schame事件,之后直接把url传递给ARouter即可
    public class SchameFilterActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Uri uri = getIntent().getData();
    ARouter.getInstance().build(uri).navigation();
    finish();
    }

    }

方法二

  • 跳转操作

    Uri testUriMix = Uri.parse("arouter://m.aliyun.com/test/activity2");
    ARouter.getInstance().build(testUriMix)
    .withString("key1", "value1")
    .navigation();

  • 目标界面

    @Route(path = "/test/activity2")
    public class Test2Activity extends AppCompatActivity {
    }

2.解析参数(传递Object)
  • 定义解析Obeject的SerializationService

    // 如果需要传递自定义对象,新建一个类(并非自定义对象类),然后实现 SerializationService,并使用@Route注解标注(方便用户自行选择序列化方式),例如:
    @Route(path = "/yourservicegroupname/json")
    public class JsonServiceImpl implements SerializationService {
    @Override
    public void init(Context context) {

    }
    
    @Override
    public <T> T json2Object(String text, Class<T> clazz) {
        return JSON.parseObject(text, clazz);
    }
    
    @Override
    public String object2Json(Object instance) {
        return JSON.toJSONString(instance);
    }

    }

  • 跳转操作

    ARouter.getInstance().build("/test/1")
    .withObejct("obj", new TestObj ("Jack", "Rose"))
    .navigation();

  • 目标界面

    // 为每一个参数声明一个字段,并使用 @Autowired 标注
    // URL中不能传递Parcelable类型数据,通过ARouter api可以传递Parcelable对象
    @Route(path = "/test/activity")
    public class Test1Activity extends Activity {

     // 支持解析自定义对象,URL中使用json传递
    @Autowired
    TestObj obj;      
    
    // 使用 withObject 传递 List 和 Map 的实现了
    // Serializable 接口的实现类(ArrayList/HashMap)
    // 的时候,接收该对象的地方不能标注具体的实现类类型
    // 应仅标注为 List 或 Map,否则会影响序列化中类型
    // 的判断, 其他类似情况需要同样处理        
    @Autowired
    List<TestObj> list;
    @Autowired
    Map<String, List<TestObj>> map;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ARouter.getInstance().inject(this);
     SerializationService serializationService = ARouter.getInstance().navigation(SerializationService.class);
    serializationService.init(this);
    obj = serializationService.parseObject(getIntent().getStringExtra("key4"), User.class);
     }

    }

3.声明拦截器(拦截跳转过程,面向切面编程)
// 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查
// 拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行
@Interceptor(priority = 8, name = "测试用拦截器")
public class TestInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
    ...
    callback.onContinue(postcard);  // 处理完成,交还控制权
    // callback.onInterrupt(new RuntimeException("我觉得有点异常"));      // 觉得有问题,中断路由流程

    // 以上两种至少需要调用其中一种,否则不会继续路由
    }

    @Override
    public void init(Context context) {
    // 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次
    }
}
4.处理跳转结果
// 使用两个参数的navigation方法,可以获取单次跳转的结果
ARouter.getInstance().build("/test/1").navigation(this, new NavigationCallback() {
    @Override
    public void onFound(Postcard postcard) {
    ...
    }

    @Override
    public void onLost(Postcard postcard) {
    ...
    }
});.

ARouter.getInstance().build("/test/activity2")
                .navigation(this, new NavCallback() {
                    @Override
                    public void onArrival(Postcard postcard) {
                    }
                    @Override
                    public void onInterrupt(Postcard postcard) {
                        Log.d("ARouter", "被拦截了");
                    }
                });
5.自定义全局降级策略
// 实现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) {

            }
        }
6.为目标页面声明更多信息
// 我们经常需要在目标页面中配置一些属性,比方说"是否需要登陆"之类的
// 可以通过 Route 注解中的 extras 属性进行扩展,这个属性是一个 int值,换句话说,单个int有4字节,也就是32位,可以配置32个开关
// 剩下的可以自行发挥,通过字节操作可以标识32个开关,通过开关标记目标页面的一些属性,在拦截器中可以拿到这个标记进行业务逻辑判断
@Route(path = "/test/activity", extras = Consts.XXXX)
7.通过依赖注入解耦:服务管理
  • 暴露服务

    // 声明接口,其他组件通过接口来调用服务
    public interface HelloService extends IProvider {
    String sayHello(String name);
    }

    // 实现接口
    @Route(path = "/yourservicegroupname/hello", name = "测试服务")
    public class HelloServiceImpl implements HelloService {

    @Override
    public String sayHello(String name) {
    return "hello, " + name;
    }
    
    @Override
    public void init(Context context) {
    
    }

    }

  • 发现服务

    public class Test {
    @Autowired
    HelloService helloService;

    @Autowired(name = "/yourservicegroupname/hello")
    HelloService helloService2;
    
    HelloService helloService3;
    
    HelloService helloService4;
    
    public Test() {
    ARouter.getInstance().inject(this);
    }
    
    public void testService() {
    // 1. (推荐)使用依赖注入的方式发现服务,通过注解标注字段,即可使用,无需主动获取
    // Autowired注解中标注name之后,将会使用byName的方式注入对应的字段,不设置name属性,会默认使用byType的方式发现服务(当同一接口有多个实现的时候,必须使用byName的方式发现服务)
    helloService.sayHello("Vergil");
    helloService2.sayHello("Vergil");
    
    // 2. 使用依赖查找的方式发现服务,主动去发现服务并使用,下面两种方式分别是byName和byType
    helloService3 = ARouter.getInstance().navigation(HelloService.class);
    helloService4 = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation();
    helloService3.sayHello("Vergil");
    helloService4.sayHello("Vergil");
    }

    }

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章