一、前言
一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦。
1. 为什么要使用路由框架?
为了让模块之间相互通信,比如你你项目 引入Module A。同时引入了 Module B。项目可以同时访问A和B。但此时A要访问B,B要访问A怎么办呢。这时候就需要路由框架去解决了。
ARouter 的 GitHub 地址:
ARouter
二、功能介绍
- 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
- 支持多模块工程使用
- 支持添加多个拦截器,自定义拦截顺序
- 支持依赖注入,可单独作为依赖注入框架使用
- 支持InstantRun
- 支持 MultiDex(Google方案)
- 映射关系按组分类、多级管理,按需初始化
- 支持用户指定全局降级与局部降级策略
- 页面、拦截器、服务等组件均自动注册到框架
- 支持多种方式配置转场动画
- 支持获取Fragment
- 完全支持Kotlin以及混编(配置见文末 其他#5)
- 支持第三方 App 加固(使用 arouter-register 实现自动注册)
- 支持生成路由文档
- 提供 IDE 插件便捷的关联路径和目标类
- 支持增量编译(开启文档生成后无法增量编译)
- 支持动态注册路由信息
三、使用
1. 添加依赖
implementation 'com.alibaba:arouter-api:1.3.1'
annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
在 android 标签下的 defaultConfig 加上 javaCompileOptions :
android {
compileSdkVersion 28
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
}
2. 初始化
在 application 里初始化,记得在发布线上版本,要日志和 debug 关闭,代码如下:
public class MyApp extends Application {
//ARouter 调试开关
private boolean isDebugARouter = true;
@Override
public void onCreate() {
super.onCreate();
context = this;
if (isDebugARouter) {
//下面2行必须卸载ARouter init 之间,否则无效
//打印日志
ARouter.openLog();
//开启调试模式(如果在InstantRun的模式下必须开启,线上必须关闭)
ARouter.openDebug();
}
// 官方建议在Application中初始化
ARouter.init(this);
}
@Override
public void onTerminate() {
super.onTerminate();
ARouter.getInstance().destroy();
}
}
同时 ARouter 的使用有点像 Dagger2,需要调用
ARouter.getInstance().inject(Object obj);
我们把他封装在BaseActivity 中:
public abstract class BaseActivity extends AppCompatActivity {
...
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentViewId());
ARouter.getInstance().inject(this);
}
}
3. @Route 的使用
在我们使用的 Activity 或 Fragment 里。使用 @Route 标注,且 path = “/x/y”(x 为包名(取最后一位即可),y 为当前类名)
@Route(path = "/demo/MainActivity")
public class MainActivity extends BaseActivity {
}
4. ARouter 里 Activity 之间的跳转
Activity 与 Activity 里的跳转只需调用如下代码,build 里的路径,就是我们用标注 @Route path 标注的路径:
ARouter.getInstance().build("/demo/MainActivity").navigation();
从上面代码我们发现。路径
"/demo/MainActivity"
,得统一用个常量类管理起来,不然这样找起来,简直费事,如下:
public final class Constance {
public static final String ACTIVITY_PATH_MAIN = "/demo/MainActivity";
}
这里除了路径跳转外,从他的参数类型中,还能看到有 Uri 跳转:
Uri uri = Uri.parse(Constance.ACTIVITY_PATH_MAIN);
ARouter.getInstance().build(uri).navigation();
5. 带参数跳转 & 获取参数
-
带参数跳转
只需在后面.withString(),即可,里面包含了所有你想传的类型
ARouter.getInstance()
.build("/myapplication/MainActivity")
.withString("name", "lihang")
.withInt("age", 27)
.withSerializable("human", new Person("力哈", "11"))
.navigation();
-
获取参数
在获取参数页面:
@Route(path = "/demo/SimpleActivity")
public class SimpleActivity extends BaseActivity {
@Autowired()
String name;
@Autowired(name = "age")
int mAge;
// @Autowired(name = "human")这里注意,如果是传Serializable,注解是得不到的。除非是Paceable
Person mPerson;
@Override
public int getContentViewId() {
return R.layout.activity_simple;
}
@Override
public void progressLogic() {
// mPerson = (Person) getIntent().getSerializableExtra("human");
}
}
获取参数方式:
- 使用 @Autowired 标注后,下方的值是之间传值的 key。
@Autowired()
String name;
- 使用 @Autowired 标注后,name = key,name 等于之间传值的 key,下方参数名随便定
@Autowired(name = "age")
int mAge;
- 带序列化这里有些特殊,如果是 Parcelable 类型,可以用上方的方法获取。但是如果是Serializable类型,却会发现,没办法获取到。
mPerson = (Person) getIntent().getSerializableExtra("human");
6. 带动画跳转
之前我们要带动画跳转,要用到
overridePendingTransition(int anim_in,int anim_out);
ARouter.getInstance()
.build(Constance.ACTIVITY_PATH_SIMPLE)
.withTransition(R.anim.alpha_activity_in, R.anim.alpha_activity_out)
//navigation里,一定要加上当前的Context,不然动画不起效
.navigation(MainActivity.this, 99);
同时 ARouter 也提供了新版动画,但是需要 api>= 16 才能使用,效果还是很酷炫的:
if (Build.VERSION.SDK_INT >= 16) {
ActivityOptionsCompat compat = ActivityOptionsCompat.
makeScaleUpAnimation(btn, btn.getWidth(), btn.getHeight(), 0, 0);
ARouter.getInstance()
.build(Constance.ACTIVITY_PATH_SIMPLE)
.withOptionsCompat(compat)
.navigation();
}else {
ToastUtils.showToast("api <16,不支持新版动画");
}
7. 类似 Activity 里的
startActivityForResult(Intent intent, int requestCode);
-
带 requestCode 跳转
也是在.navigation() 里,只要带上 requestCode 就Ok了。然后onActivityResult和正常使用一样。
ARouter.getInstance()
.build(Constance.ACTIVITY_PATH_SIMPLE)
.navigation(Context context, int requestCode);
- setResult 返回界面,你没看错,就是这么复杂!
Postcard postcard = ARouter.getInstance().build(Constance.ACTIVITY_PATH_MAIN);
LogisticsCenter.completion(postcard);
Class<?> destination = postcard.getDestination();
Intent intent = new Intent(SimpleActivity.this, destination);
setResult(1, intent);
finish();
8. Fragment 的使用
这里Fragment的使用和Activity一样,但唯一的区别是,Activity是跳转,而Fragment是实例化Fragmen的实例,如下:
//记得要强转一下,这里传值什么的和Activity的用法一样。
HomeFragment homeFragment = (HomeFragment)ARouter.getInstance()
.build(Constance.Fragment_PATH_SIMPLE)
.navigation();
9. 拦截器的使用
@Interceptor(priority = 1)
public class FirstInterceptor implements IInterceptor {
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
if (postcard.getPath().equals(Constance.ACTIVITY_PATH_SIMPLE)){
LogUtils.i("ARouter拦截器", "FirstInterceptor 开始拦截 ======");
}
callback.onContinue(postcard);
}
@Override
public void init(Context context) {
LogUtils.i("ARouter拦截器", "first init");
}
}
-
拦截器使用很奇特,只要在你代码里写上这个类,就起作用了。估计
@Interceptor
标注,apt帮我们干了不少事。 - 首先用@Interceptor标注,(priority = 1)优先级,越小,越先执行。
- 实现IInterceptor接口。实现2个方法:init(),process()。
- init()方法,在项目启动时候首先执行,process()方法在Activity跳转时调用
- 如果我们要为拦截器加上一些帅选条件的话可以通过Postcard
注意,只要设置了拦截器,这里不调用
callback.onContinue(postcard)
;就会卡在当前的拦截器内。当然跳转就卡在当前页面。
10. 跳转时加监听,配合拦截器使用
直接上代码:
ARouter.getInstance()
.build(Constance.ACTIVITY_PATH_SIMPLE)
.navigation(MainActivity.this, new NavigationCallback() {
@Override
public void onFound(Postcard postcard) {
//路由目标被发现时调用(Activity执行跳转代码,第一个执行)
//group 为路径的组。如果不自定义,会默认成path的/x/y的x
//group 可以自定义,如:@Route(path = Constance.ACTIVITY_PATH_MAIN,group = Constance.GROUP_FIRST)
//当然自定义组之后,跳转的时候要带上组名。
String group = postcard.getGroup();
//path 为全路径@Route(path = Constance.ACTIVITY_PATH_MAIN)
String path = postcard.getPath();
LogUtils.i("ARouter拦截器", "onFound ------> group == " + group + " " + "path == " + path);
}
@Override
public void onArrival(Postcard postcard) {
//路由到达后调用(注意这里是所有拦截器执行完之后才会调用)
String group = postcard.getGroup();
String path = postcard.getPath();
LogUtils.i("ARouter拦截器", "onArrival ------> group == " + group + " " + "path == " + path);
}
@Override
public void onLost(Postcard postcard) {
//路由被丢失时调用
LogUtils.i("ARouter拦截器", "onLost");
}
@Override
public void onInterrupt(Postcard postcard) {
//路由被拦截时调用
LogUtils.i("ARouter拦截器", "onInterrupt");
}
});