Aouter的一款由Ali团队开发的Android路由框架,本文旨在讲解Arouter官方网站的Demo,而不是自己的Demo,因为我觉得官方的Demo已经足够满足开发的业务需求,就不必自己再轮一篇,本文是对文档的一些补充。
git clone https://github.com/alibaba/ARouter.git 神传送下载源码
我们先来闲聊下我们项目中可能用到的跳转业务功能:
1、Activity跳转,并传参数(参数包括基本类型和引用类型)
2、Fragment跳转,并传参数(参数包括基本类型和引用类型)
3、我想在跳转的时候做点判断和控制什么的,甚至是拦截一些界面不给没有权限的用户访问等需求
4、我想解耦,不依赖某个Activity的引用
以上四点基本满足我们对跳转的需求,但是原生我们无法拦截界面的跳转,1、2点倒是可以实现,但是重复性劳动非常大,而且显示跳转的时候,依赖跳转之后的Activity.class ,这使得我们在并行开发的时候非常被动,如果使用隐式跳转,当Activity不存在的时候又会崩溃,所以基于以上考虑,我不得不引入一个路由框架,进行对Aouter的进行调研后和阅读关键性源码后,决定使用这个框架进行开发。
提供下刘志龙大神对Arouter的架构和原理进行讲解概况地址,大家有兴致可以看下:
Aouter Api包括三种类型:
api :com.alibaba:arouter-api:x.x.x
注解器:com.alibaba:arouter-compiler:x.x.x
自动注册插件:com.alibaba:arouter-register:x.x.x
(以目前最新版本为列)在app的build.gradle里面添加:
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : project.getName() ]
}
}
}
}
dependencies {
// 替换成最新版本, 需要注意的是api
// 要与compiler匹配使用,均使用最新版可以保证兼容
compile 'com.alibaba:arouter-api:1.3.1'
annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
}
如果AndroidStudio 3.0 gradle插件3.0 以上 compile 使用 api替换
如果gradle插件少于2.2的版本 annotationProcessor需要使用apt插件替换
如果是Kotlin项目,请使用k-apt插件替换
kotilin配置列子:
项目层的build.gradle,添加插件:
buildscript {
ext.kotlin_version = '1.1.3-2'
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
app层的build.gradle:
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
dependencies {
kapt 'com.alibaba:arouter-compiler:1.0.3'
}
注:假如是组件化项目需要注意 javaCompileOptions和 annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'每个组件的配置都需要有 ,api可以放在基础库中。
配置自动注册插件,在项目层的build.gradle:
dependencies {
classpath 'com.alibaba:arouter-register:1.0.2'
}
版本api 1.3.1对应1.0.2 搞错了无效。
注解:
1、@Route 路由注解。 定义一个可以找到某个目标的字符串,用于Activity、Fragment、Service的路径。
path:路径字符串 比如“test/activity1”
注意:path新版必须设置一个“XXX/XXX”斜杠的类型,因为一个斜杠之前的字符串作为一个组
group:组,新版本不建议使用
name:定义路径的名字,默认为 “undefined”。用来添加注释
priority:优先级 默认为-1。
extras :额外的数据,Interget类型,可以用来控制目标页面的业务逻辑,比如1 or 0表示某个页面是否展示,把他当前2禁止的时候可以控制32个业务开关。一般不常用。
2、@Interceptor 定义拦截器,添加该注释可以定义一个全局Activity跳转的拦截器。
priority:优先级
name:默认Default
3、@Autowired 注入参数,用于接收传送参数 或者 注入Service
name:参数名,默认""
required:是否必须,如果为true, 数据为null的话会导致app崩溃
desc:对参数进行描述 相当于注释
if (isDebug()) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化
使用Router定义路径
@Route(path = "/test/activity1")
public class Test1Activity extends AppCompatActivity {
跳转
ARouter
.getInstance()
.build("/test/activity1").navigation();
跳转
ARouter.getInstance()
.build("/test/activity2")
.withString("key1", "value1")
.navigation();
withXX (key,value)传送的是基本类型,可以通过key接收到这个数据
使用withObject(key,value)传送自定义对象,使用自定义对象要实现SerializationService接口,并且规定路由设置成@Route(path = "/service/json") 。比如使用FastJSON进行序列化:
@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);
}
}
Parcelable和Serializable对象传输是直接支持的。
接收数据的时候有两种方式
一种是原生使用的接收方式
String value = getIntent().getXXXExtra("key");
另一种是使用注释获取到,先在Activity中添加 ARouter.getInstance().inject(this);
@Autowired(name="key")
String key;
通过name来匹配,默认通过类型匹配。
ARouter.getInstance()
.build("/test/activity2")
.navigation(this, 666);
setResult(666);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 666:
Log.e("activityResult", String.valueOf(resultCode));
break;
default:
break;
}
}
定义协议:
<activity android:name=".SchemeFilterActivity">
<!-- 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"/>
</activity>
其中scheme定义协议头,可以随便自己定制,m.aliun.com是host
当我们跳转的时候需要一个中间者进行调度,如:SchemeFilterActivity就是用来对协议进行中间调度的
public class SchemeFilterActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 直接通过ARouter处理外部Uri
Uri uri = getIntent().getData();
ARouter.getInstance().build(uri).navigation(this, new NavCallback() {
@Override
public void onArrival(Postcard postcard) {
finish();
}
});
}
}
我们想要跳转时,可以这样, 比如需要跳转到 test/activity2
Uri testUriMix = Uri.parse("arouter://m.aliyun.com/test/activity2");
ARouter.getInstance().build(testUriMix)
.withString("key1", "value1")
.navigation();
上面的SchemeFilterActivity只是一个中间调度,不是我们的目标Activity,我们的目标activity是 test/activity2,Uri需要这样拼接:协议字符串+目标跳转的路径。
当SchemeFilterActivity接收到uri后再进行一次跳转,从SchemeFilterActivity中取出来的getIntent().getData()==" test/activity2"。
我们还可以在网页直接跳转到应用内的activity,在SchemeFilterActivity清单文件中 intent-filter添加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>
<a href="arouter://m.aliyun.com/test/activity3?name=alex&age=18&boy=true&high=180">arouter://m.aliyun.com/test/activity3?name=alex&age=18&boy=true&high=180</a>
后面?之后是传参数。相当于一个get请求一样。
ARouter.getInstance().build("/xxx/xxx").navigation(this, new NavCallback() {
@Override
public void onFound(Postcard postcard) {
Log.d("ARouter", "找到了");
}
@Override
public void onLost(Postcard postcard) {
Log.d("ARouter", "找不到了");
}
@Override
public void onArrival(Postcard postcard) {
Log.d("ARouter", "跳转完了");
}
@Override
public void onInterrupt(Postcard postcard) {
Log.d("ARouter", "被拦截了");
}
});
使用NavCallback进行监听跳转过程中是否被拦截,或者是否完成跳转。比如我们可以在跳转后进行一些保存数据的操作,当没有发现Activity的时候提示模块未开放等功能。
跳转动画在Arouter里面实现也很简单,只需要定义好出入的动画然后调用如下:
ARouter.getInstance()
.build("/test/activity2")
.withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
.navigation(this);
很多时候大家都会问,为什么设置了无效,这可能并不是Arouter的问题,而是因为你的样式为了不出现黑屏设置了 android:windowIsTranslucent为true导致的。
if (Build.VERSION.SDK_INT >= 16) {
ActivityOptionsCompat compat = ActivityOptionsCompat.
makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0);
ARouter.getInstance()
.build("/test/activity2")
.withOptionsCompat(compat)
.navigation();
} else {
ARouter.getInstance()
.build("/test/activity2")
.navigation();
}
因为只有API大于16才支持共享元素,所以当少于16的时候我们直接跳转。
如果跳转的时候我们不设置Activity,比如:ARouter.getInstance().build("/xxx/xxx").navigation();
那么默认的Flag就是:Intent.FLAG_ACTIVITY_NEW_TASK,Intent.FLAG_ACTIVITY_NEW_TASK类型launchMode="SingleTask"这是非常影响性能的操作,所以不建议这些写.
还有一种就是不设置,使用的是注册清单里面的luanchMode.
如果想要指定Flag,如下:
ARouter.getInstance()
.build("")
.withFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
.navigation(this);
其中Flags值是有限制的,具体查阅@ FlagInt
获取Fragment
添加路由:
@Route(path = "/test/fragment")
public class BlankFragment extends Fragment
带参数的跳转
Fragment fragment = (Fragment) ARouter
.getInstance()
.build("/test/fragment")
.withString("key","关键词")
.withObject("obj",testObj1)
.navigation();
在Fragment中接收,先添加
public BlankFragment() {
ARouter.getInstance().inject(this);
}
使用注释接收参数:
@Autowired(name = "key",desc = "名称")
String name;
@Autowired(required = true,name = "obj",desc = "一个测试对象")
TestObj obj;
name代表 键值对的key,required设置为true不能为null否则会崩溃,desc是一段对字段的描述。
当我们需要对页面跳转进行拦截时,我们可以使用Arouter内置的拦截进行处理,比如常见的场景有:判断页面是否需要登录状态,未登录跳转到登录页面登录。
比如:我们需要拦截“/test/activity4”页面,如果是就提示需要登录:
@Interceptor(priority = 7,name = "aasj")
public class Test1Interceptor implements IInterceptor {
Context mContext;
/**
拦截进行一些操作后 决定是否放行
*/
@Override
public void process(final Postcard postcard, final InterceptorCallback callback) {
if ("/test/activity4".equals(postcard.getPath())) {
MainLooper.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext,"您需要进行登录才能使用该功能",Toast.LENGTH_SHORT).show();
}
});
} else {
callback.onContinue(postcard);
}
}
/**
* 初始化
*/
@Override
public void init(Context context) {
mContext = context;
Log.e("testService", Test1Interceptor.class.getName() + " has init.");
}
}
其中priority决定优先级,一个优先级对应一个拦截器,拦截器是对所有Actvity都有效的,除非你在内部通过path剔除。通过
callback.onContinue(postcard)放行,通过callback.onInterrupt(new RuntimeException(""));中断。
注意:process方法内部不是主线程。
当我们不希望拦截器拦截到我们,还可以设置绿色通道:
// 使用绿色通道(跳过所有的拦截器)
ARouter.getInstance().build("/home/main").greenChannel().navigation();
什么情况下我们会使用到服务呢?
目前市面上比较流行,组件化,组件化的要求就是热拔插,能随时分离各个APP的业务和分开打包,所以有时候我们需要执行一些功能的时候,可以需要调用另外一个组件的数据,就是通过Service来进行解耦的。
比如ModuleA 提供一个HelloService,HelloService输出一个Toast,ModuleB就可以从内置的Router容器中获取到HelloService来进行调用方法,输出Toast.
服务的声明:
服务应该先定义一个HelloService接口,这个接口需要放置在ModuleA和ModuleB都能引用到的父Lib中,然后Module实现HelloService接口:
public interface HelloService extends IProvider {
void sayHello(String name);
}
Service必须继承IProvider
@Route(path = "/service/single")
public class SingleService implements IProvider {
Context mContext;
public void sayHello(String name) {
Toast.makeText(mContext, "Hello " + name, Toast.LENGTH_SHORT).show();
}
@Override
public void init(Context context) {
mContext = context;
}
}
Service也需要定义路由,以便框架能够查找到。
我们可以使用三种方式获取到Service:
1、依赖注入 、
@Autowired
HelloService helloService;
2、根据HelloService .class类型获取
helloService=ARouter.getInstance().navigation(HelloService.class);
类型查找只能实现单一的服务的时候才能用,多实现服务还是得用依赖注入和根据路径查找的方法。
3、根据路径查找
helloService = (HelloService) ARouter.getInstance().build("service/hello").navigation();
服务还有Aouter内部的一些服务,比如PathReplaceService用来替换路径,SerializationService用来序列化对象,InterceptorServiceImpl用来处理拦截器的执行等服务,我们将在源码篇进行解读。
-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
手机扫一扫
移动阅读更方便
你可能感兴趣的文章