ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
文档当前状态:**beta0.4** * [x] 选题收集:2017/11/21 * [x] 初稿整理: * [ ] 补充校对: * [ ] 入库存档: --- [TOC] --- 上篇对模块化实践中 Gradle的配置做了简单的说明,接下来会说明 如何在代码中使用ARouter 来实现模块化设计。主要会有以下两个部分: ARouter的使用、常见问题说明; 作为一个正在更新的库,没有什么比官方文档,能更好的了解他的使用。 [官方文档README_CN](https://github.com/alibaba/ARouter/blob/master/README_CN.md ) 下面是一些使用简要说明基于arouter-api:1.2.4、arouter-compiler:1.1.4: #### 一、功能介绍 1. **支持直接解析标准URL进行跳转,并自动注入参数到目标页面中** 2. **支持多模块工程使用** 3. **支持添加多个拦截器,自定义拦截顺序** 4. **支持依赖注入,可单独作为依赖注入框架使用** 5. **支持InstantRun** 6. **支持MultiDex**(Google方案) 7. 映射关系按组分类、多级管理,按需初始化 8. 支持用户指定全局降级与局部降级策略 9. 页面、拦截器、服务等组件均自动注册到框架 10. 支持多种方式配置转场动画 11. 支持获取Fragment 12. 完全支持Kotlin以及混编(配置见文末 其他#5) #### 二、典型应用 1. 从外部URL映射到内部页面,以及参数传递与解析 2. 跨模块页面跳转,模块间解耦 3. 拦截跳转过程,处理登陆、埋点等逻辑 4. 跨模块API调用,通过控制反转来做组件解耦 #### 三、基础功能 1. 添加依赖和配置 这是java的配置 ``` gradle 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插件,配置方法见文末'其他#4' // Kotlin配置参考文末'其他#5' ``` 这是Kotlin的配置 ``` // 可以参考 module-kotlin 模块中的写法 apply plugin: 'kotlin-kapt' kapt { arguments { arg("moduleName", project.getName()) } } dependencies { compile 'com.alibaba:arouter-api:x.x.x' kapt 'com.alibaba:arouter-compiler:x.x.x' ... } ``` 2. 添加注解 ``` java // 在支持路由的页面上添加注解(必选) // 这里的路径需要注意的是至少需要有两级,/xx/xx @Route(path = "/test/activity") public class YourActivity extend Activity { ... } //为了更好关联引用关系,使用静态常量来代替常量 @Route(path = PathIndex.YouActivity) public class YourActivity extend Activity { ... } ``` 这里需要重点说一下的就是,纯粹使用@Route(path = "/test/activity"),这种字符串的样式 去写路径,在查看相互引用关系会一团糟,你永远不能快速 准确的知道 这个activity 到底被谁引用到,在荣耀的开发中,我们进行了一个简单的方式,在中间共用层,建立一个存储路由路径目录的常量类 PathIndex。 3. 初始化SDK ``` java if (isDebug()) { // 这两行必须写在init之前,否则这些配置在init过程中将无效 ARouter.openLog(); // 打印日志 ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) } ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化 ``` 4. 发起路由操作 ``` java // 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中) ARouter.getInstance().build("/test/activity").navigation(); // 2. 跳转并携带参数 ARouter.getInstance().build("/test/1") .withLong("key1", 666L) .withString("key3", "888") .withObject("key4", new Test("Jack", "Rose")) .navigation(); ``` 5. 添加混淆规则(如果使用了Proguard) ``` -keep public class com.alibaba.android.arouter.routes.**{*;} -keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;} # 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口 -keep interface * implements com.alibaba.android.arouter.facade.template.IProvider # 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现 -keep class * implements com.alibaba.android.arouter.facade.template.IProvider ``` #### 四、进阶用法 1. 通过URL跳转 这个功能主要是点击外部url链接时候,可以直接跳转进原生的页面,类型淘宝、京东、贴吧。 ``` java // 新建一个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(); } } ``` AndroidManifest.xml ``` xml <activity android:name=".activity.SchameFilterActivity"> <!-- Schame --> <intent-filter> <data android:host="m.aliyun.com" android:scheme="arouter"/> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> </intent-filter> </activity> ``` 2. 解析URL中的参数 ``` java // 为每一个参数声明一个字段,并使用 @Autowired 标注 // URL中不能传递Parcelable类型数据,通过ARouter api可以传递Parcelable对象 @Route(path = "/test/activity") public class Test1Activity extends Activity { @Autowired public String name; @Autowired int age; @Autowired(name = "girl") // 通过name来映射URL中的不同参数 boolean boy; @Autowired TestObj obj; // 支持解析自定义对象,URL中使用json传递 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ARouter.getInstance().inject(this); // ARouter会自动对字段进行赋值,无需主动获取 Log.d("param", name + age + boy); } } // 如果需要传递自定义对象,需要实现 SerializationService,并使用@Route注解标注(方便用户自行选择序列化方式),例如: @Route(path = "/service/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); } } ``` 3. 声明拦截器(拦截跳转过程,面向切面编程) ``` java // 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查 // 拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行 @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. 处理跳转结果 ``` java // 使用两个参数的navigation方法,可以获取单次跳转的结果 ARouter.getInstance().build("/test/1").navigation(this, new NavigationCallback() { @Override public void onFound(Postcard postcard) { ... } @Override public void onLost(Postcard postcard) { ... } }); ``` 5. 自定义全局降级策略 主要是为了 没有找到对应路由时候,给出相对友好的提示,一般提供一个H5页面即可 ``` java // 实现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. 为目标页面声明更多信息 ``` java // 我们经常需要在目标页面中配置一些属性,比方说"是否需要登陆"之类的 // 可以通过 Route 注解中的 extras 属性进行扩展,这个属性是一个 int值,换句话说,单个int有4字节,也就是32位,可以配置32个开关 // 剩下的可以自行发挥,通过字节操作可以标识32个开关,通过开关标记目标页面的一些属性,在拦截器中可以拿到这个标记进行业务逻辑判断 @Route(path = "/test/activity", extras = Consts.XXXX) ``` 7. 通过依赖注入解耦:服务管理(一) 暴露服务 ``` java // 声明接口,其他组件通过接口来调用服务 public interface HelloService extends IProvider { String sayHello(String name); } // 实现接口 @Route(path = "/service/hello", name = "测试服务") public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "hello, " + name; } @Override public void init(Context context) { } } ``` 9. 通过依赖注入解耦:服务管理(二) 发现服务 ``` java public class Test { @Autowired HelloService helloService; @Autowired(name = "/service/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("/service/hello").navigation(); helloService3.sayHello("Vergil"); helloService4.sayHello("Vergil"); } } ``` #### 五、更多功能 1. 初始化中的其他设置 ``` java ARouter.openLog(); // 开启日志 ARouter.openDebug(); // 使用InstantRun的时候,需要打开该开关,上线之后关闭,否则有安全风险 ARouter.printStackTrace(); // 打印日志的时候打印线程堆栈 ``` 2. 详细的API说明 ``` java // 构建标准的路由请求(会自动分组) ARouter.getInstance().build("/home/main").navigation(); // (不推荐使用,作者已经将其标记为"过时")构建标准的路由请求,并指定分组 //ARouter.getInstance().build("/home/main", "ap").navigation(); // 构建标准的路由请求,通过Uri直接解析 Uri uri; ARouter.getInstance().build(uri).navigation(); // 构建标准的路由请求,startActivityForResult // navigation的第一个参数必须是Activity,第二个参数则是RequestCode ARouter.getInstance().build("/home/main", "ap").navigation(this, 5); // 直接传递Bundle Bundle params = new Bundle(); ARouter.getInstance() .build("/home/main") .with(params) .navigation(); // 指定Flag ARouter.getInstance() .build("/home/main") .withFlags(); .navigation(); // 获取Fragment Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation(); // 对象传递 ARouter.getInstance() .withObject("key", new TestObj("Jack", "Rose")) .navigation(); // 觉得接口不够多,可以直接拿出Bundle赋值 ARouter.getInstance() .build("/home/main") .getExtra(); // 转场动画(常规方式) ARouter.getInstance() .build("/test/activity2") .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom) .navigation(this); // 转场动画(API16+) ActivityOptionsCompat compat = ActivityOptionsCompat. makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0); // ps. makeSceneTransitionAnimation 使用共享元素的时候,需要在navigation方法中传入当前Activity ARouter.getInstance() .build("/test/activity2") .withOptionsCompat(compat) .navigation(); // 使用绿色通道(跳过所有的拦截器) ARouter.getInstance().build("/home/main").greenChannel().navigation(); // 使用自己的日志工具打印日志 ARouter.setLogger(); ``` 3. 获取原始的URI ``` java String uriStr = getIntent().getStringExtra(ARouter.RAW_URI); ``` 4. 重写跳转URL ``` java // 实现PathReplaceService接口,并加上一个Path内容任意的注解即可 @Route(path = "/xxx/xxx") // 必须标明注解 public class PathReplaceServiceImpl implements PathReplaceService { /** * For normal path. * * @param path raw path */ String forString(String path) { return path; // 按照一定的规则处理之后返回处理后的结果 } /** * For uri type. * * @param uri raw uri */ Uri forUri(Uri uri) { return url; // 按照一定的规则处理之后返回处理后的结果 } } ``` #### 六、其他 1. 路由中的分组概念 - SDK中针对所有的路径(/test/1 /test/2)进行分组,分组只有在分组中的某一个路径第一次被访问的时候,该分组才会被初始化 - (不推荐,已标记过时)可以通过 @Route 注解主动指定分组,否则使用路径中第一段字符串(/*/)作为分组 - 注意:一旦主动指定分组之后,应用内路由需要使用 ARouter.getInstance().build(path, group) 进行跳转,手动指定分组,否则无法找到 ``` java @Route(path = "/test/1", group = "app") ``` 2. 拦截器和服务的异同 - 拦截器和服务所需要实现的接口不同,但是结构类似,都存在 init(Context context) 方法,但是两者的调用时机不同 - 拦截器因为其特殊性,会被任何一次路由所触发,拦截器会在ARouter初始化的时候异步初始化,如果第一次路由的时候拦截器还没有初始化结束,路由会等待,直到初始化完成。 - 服务没有该限制,某一服务可能在App整个生命周期中都不会用到,所以服务只有被调用的时候才会触发初始化操作