[Alibaba-ARouter]浅谈简单好用的Android页面路由框架
开发一款app,总会遇到各种各样的需求和业务,这时候选择一个简单好用的*,就可以事半功倍
前言
intent intent = new intent(mcontext, xxxactivity.class); intent.putextra("key","value"); startactivity(intent); intent intent = new intent(mcontext, xxxactivity.class); intent.putextra("key","value"); startactivityforresult(intent, 666);
上面一段代码,在android开发中,最常见也是最常用的功能就是页面的跳转,我们经常需要面对从浏览器或者其他app跳转到自己app中页面的需求,不过就算是简简单单的页面跳转,随着时间的推移,也会遇到一些问题:
- 集中式的url管理:谈到集中式的管理,总是比较蛋疼,多人协同开发的时候,大家都去androidmanifest.xml中定义各种intentfilter,使用隐式intent,最终发现androidmanifest.xml中充斥着各种schame,各种path,需要经常解决path重叠覆盖、过多的activity被导出,引发安全风险等问题
- 可配置性较差:manifest限制于xml格式,书写麻烦,配置复杂,可以自定义的东西也较少
- 跳转过程中无法插手:直接通过intent的方式跳转,跳转过程开发者无法干预,一些面向切面的事情难以实施,比方说登录、埋点这种非常通用的逻辑,在每个子页面中判断又很不合理,毕竟activity已经实例化了
- 跨模块无法显式依赖:在app小有规模的时候,我们会对app做水平拆分,按照业务拆分成多个子模块,之间完全解耦,通过打包流程控制app功能,这样方便应对大团队多人协作,互相逻辑不干扰,这时候只能依赖隐式intent跳转,书写麻烦,成功与否难以控制。
另一个*
为了解决以上问题,我们需要一款能够解耦、简单、功能多、定制性较强、支持拦截逻辑的路由组件:我们选择了alibaba的arouter。
一、功能介绍
- 支持直接解析url进行跳转、参数按类型解析到bundle,支持java基本类型(*)
- 支持应用内的标准页面跳转,api接近android原生接口
- 支持多模块工程中使用,允许分别打包,包结构符合android包规范即可(*)
- 支持跳转过程中插入自定义拦截逻辑,自定义拦截顺序(*)
- 支持服务托管,通过byname,bytype两种方式获取服务实例,方便面向接口开发与跨模块调用解耦(*)
- 映射关系按组分类、多级管理,按需初始化,减少内存占用提高查询效率(*)
- 支持用户指定全局降级策略
- 支持获取单次跳转结果
- 丰富的api和可定制性
- 被arouter管理的页面、拦截器、服务均无需主动注册到arouter,被动发现
- 支持android n推出的jack编译链
二、不支持的功能
- 自定义url解析规则(考虑支持)
- 不能动态加载代码模块和添加路由规则(考虑支持)
- 多路径支持(不想支持,貌似是导致各种混乱的起因)
- 生成映射关系文档(考虑支持)
三、典型应用场景
- 从外部url映射到内部页面,以及参数传递与解析
- 跨模块页面跳转,模块间解耦
- 拦截跳转过程,处理登陆、埋点等逻辑
- 跨模块api调用,模块间解耦(注册arouter服务的形式,通过接口互相调用)
四、基础功能
添加依赖和配置
apply plugin: 'com.neenbedankt.android-apt'
buildscript { repositories { jcenter() } dependencies { classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } } apt { arguments { modulename project.getname(); } } dependencies { apt 'com.alibaba:arouter-compiler:x.x.x' compile 'com.alibaba:arouter-api:x.x.x' ... }
添加注解
// 在支持路由的页面、服务上添加注解(必选) // 这是最小化配置,后面有详细配置 @route(path = "/test/1") public class youractivity extend activity { ... }
初始化sdk
arouter.init(mapplication); // 尽可能早,推荐在application中初始化
发起路由操作
// 1. 应用内简单的跳转(通过url跳转在'中阶使用'中) arouter.getinstance().build("/test/1").navigation(); // 2. 跳转并携带参数 arouter.getinstance().build("/test/1") .withlong("key1", 666l) .withstring("key3", "888") .navigation();
添加混淆规则(如果使用了proguard)
-keep public class com.alibaba.android.arouter.routes.**{*;}
五、进阶用法
通过url跳转
// 新建一个activity用于监听schame事件 // 监听到schame事件之后直接传递给arouter即可 // 也可以做一些自定义玩法,比方说改改url之类的 // http://www.example.com/test/1 public class schamefilteractivity extends activity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); // 外面用户点击的url uri uri = getintent().getdata(); // 直接传递给arouter即可 arouter.getinstance().build(uri).navigation(); finish(); } } // androidmanifest.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> <!-- app links --> <intent-filter android:autoverify="true"> <action android:name="android.intent.action.view"/> <category android:name="android.intent.category.default"/> <category android:name="android.intent.category.browsable"/> <data android:host="m.aliyun.com" android:scheme="http"/> <data android:host="m.aliyun.com" android:scheme="https"/> </intent-filter> </activity>
使用arouter协助解析参数类型
// url中的参数会默认以string的形式保存在bundle中 // 如果希望arouter协助解析参数(按照不同类型保存进bundle中) // 只需要在需要解析的参数上添加 @param 注解 @route(path = "/test/1") public class test1activity extends activity { @param // 声明之后,arouter会从url中解析对应名字的参数,并按照类型存入bundle public string name; @param private int age; @param(name = "girl") // 可以通过name来映射url中的不同参数 private boolean boy; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); name = getintent().getstringextra("name"); age = getintent().getintextra("age", -1); boy = getintent().getbooleanextra("girl", false); // 注意:使用映射之后,要从girl中获取,而不是boy } }
开启arouter参数自动注入(实验性功能,不建议使用,正在开发保护策略)
// 首先在application中重写 attachbasecontext方法,并加入arouter.attachbasecontext(); @override protected void attachbasecontext(context base) { super.attachbasecontext(base); arouter.attachbasecontext(); } // 设置arouter的时候,开启自动注入 arouter.enableautoinject(); // 至此,activity中的属性,将会由arouter自动注入,无需 getintent().getstringextra("xxx")等等
声明拦截器(拦截跳转过程,面向切面搞事情)
// 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查 // 拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行 @interceptor(priority = 666, name = "测试用拦截器") public class testinterceptor implements iinterceptor { /** * the operation of this interceptor. * * @param postcard meta * @param callback cb */ @override public void process(postcard postcard, interceptorcallback callback) { ... callback.oncontinue(postcard); // 处理完成,交还控制权 // callback.oninterrupt(new runtimeexception("我觉得有点异常")); // 觉得有问题,中断路由流程 // 以上两种至少需要调用其中一种,否则会超时跳过 } /** * do your init work in this method, it well be call when processor has been load. * * @param context ctx */ @override public void init(context context) { } }
处理跳转结果
// 通过两个参数的navigation方法,可以获取单次跳转的结果 arouter.getinstance().build("/test/1").navigation(this, new navigationcallback() { @override public void onfound(postcard postcard) { ... } @override public void onlost(postcard postcard) { ... } });
自定义全局降级策略
// 实现degradeservice接口,并加上一个path内容任意的注解即可 @route(path = "/xxx/xxx") // 必须标明注解 public class degradeserviceimpl implements degradeservice { /** * router has lost. * * @param postcard meta */ @override public void onlost(context context, postcard postcard) { // do something. } /** * do your init work in this method, it well be call when processor has been load. * * @param context ctx */ @override public void init(context context) { } }
为目标页面声明更多信息
// 我们经常需要在目标页面中配置一些属性,比方说"是否需要登陆"之类的 // 可以通过 route 注解中的 extras 属性进行扩展,这个属性是一个 int值,换句话说,单个int有4字节,也就是32位,可以配置32个开关 // 剩下的可以自行发挥,通过字节操作可以标识32个开关 @route(path = "/test/1", extras = consts.xxxx)
使用arouter管理服务(一) 暴露服务
/** * 声明接口 */ public interface iservice extends iprovider { string hello(string name); } /** * 实现接口 */ @route(path = "/service/1", name = "测试服务") public class serviceimpl implements iservice { @override public string hello(string name) { return "hello, " + name; } /** * do your init work in this method, it well be call when processor has been load. * * @param context ctx */ @override public void init(context context) { } }
使用arouter管理服务(二) 发现服务
1. 可以通过两种api来获取service,分别是byname、bytype
iservice service = arouter.getinstance().navigation(iservice.class); // bytype iservice service = (iservice) arouter.getinstance().build("/service/1").navigation(); // byname service.hello("zz");
2. 注意:推荐使用byname方式获取service,bytype这种方式写起来比较方便,但如果存在多实现的情况时,sdk不保证能获取到你想要的实现
使用arouter管理服务(三) 管理依赖
可以通过arouter service包装您的业务逻辑或者sdk,在service的init方法中初始化您的sdk,不同的sdk使用arouter的service进行调用,每一个service在第一次使用的时候会被初始化,即调用init方法。
这样就可以告别各种乱七八糟的依赖关系的梳理,只要能调用到这个service,那么这个service中所包含的sdk等就已经被初始化过了,完全不需要关心各个sdk的初始化顺序。
六、更多功能
初始化中的其他设置
arouter.openlog(); // 开启日志 arouter.printstacktrace(); // 打印日志的时候打印线程堆栈
详细的api说明
// 构建标准的路由请求 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(); // 觉得接口不够多,可以直接拿出bundle赋值 arouter.getinstance() .build("/home/main") .getextra(); // 使用绿色通道(跳过所有的拦截器) arouter.getinstance().build("/home/main").greenchannal().navigation();
附录
最新版本
- arouter-annotation : 1.0.0
- arouter-compiler : 1.0.1
- arouter-api : 1.0.2
gradle依赖
dependencies { apt 'com.alibaba:arouter-compiler:1.0.1' compile 'com.alibaba:arouter-api:1.0.2' }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。