一、起源
- 由于业务的增长提高了项目的复杂性,为了更好的适应团队开发,提高开发效率,实行组件化乃大势所趋。
- 组件化可以让我们程序更容易的扩展、更方便的维护,更快捷的同步开发与更简单的单元调试。
- 但是由于各个组件在不同的model甚至在不同的项目中,这样他们之间的通信就成了问题,而
ARouter
的出现就是让组件间、模块间是实现完全的独立。 - 并且后台还可以通过路由的机制控制Android界面调用实现远程控制和高度解耦。
二、是什么?
一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
三、功能介绍
- 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
- 支持多模块工程使用
- 支持添加多个拦截器,自定义拦截顺序
- 支持依赖注入,可单独作为依赖注入框架使用
- 支持InstantRun
- 支持MultiDex(Google方案)
- 映射关系按组分类、多级管理,按需初始化
- 支持用户指定全局降级与局部降级策略
- 页面、拦截器、服务等组件均自动注册到框架
- 支持多种方式配置转场动画
- 支持获取Fragment
- 完全支持Kotlin以及混编
- 支持第三方 App 加固(使用 arouter-register 实现自动注册)
- 支持生成路由文档
- 提供 IDE 插件便捷的关联路径和目标类
四、原理解析
1、调用关系
-
两个界面之间的跳转,调用关系如下:
界面调用用例图
- 注册:B界面先在ARouter里注册一个唯一标识;
- 查询:A界面调用时,ARouter会先查询B界面是否已注册;
- 整合:ARouter查询到B界面后,会将A界面传入信息进行整合;
- 调用:整合好信息后,会调起B界面并将传入信息传递到B界面。
2、流程解析
- 界面跳转,arouter做了以下工作:
五、原理解析
- 从外部URL映射到内部页面,以及参数传递与解析
- 跨模块页面跳转,模块间解耦
- 拦截跳转过程,处理登陆、埋点等逻辑
- 跨模块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();
//等同于startActivityForResult
ARouter.getInstance().build("/test/activity").navigation(getActivity(),REQUEST_CODE);
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
<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>
- 跳转操作
// 新建一个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");
}
}
网友评论