Aouter的一款由Ali团队开发的Android路由框架,本文旨在讲解Arouter官方网站的Demo,而不是自己的Demo,因为我觉得官方的Demo已经足够满足开发的业务需求,就不必自己再轮一篇,本文是对文档的一些补充。
官方地址
git clone .git 神传送下载源码
我们先来闲聊下我们项目中可能用到的跳转业务功能:
1、Activity跳转,并传参数(参数包括基本类型和引用类型)
2、Fragment跳转,并传参数(参数包括基本类型和引用类型)
3、我想在跳转的时候做点判断和控制什么的,甚至是拦截一些界面不给没有权限的用户访问等需求
4、我想解耦,不依赖某个Activity的引用
以上四点基本满足我们对跳转的需求,但是原生我们无法拦截界面的跳转,1、2点倒是可以实现,但是重复性劳动非常大,而且显示跳转的时候,依赖跳转之后的Activity.class ,这使得我们在并行开发的时候非常被动,如果使用隐式跳转,当Activity不存在的时候又会崩溃,所以基于以上考虑,我不得不引入一个路由框架,进行对Aouter的进行调研后和阅读关键性源码后,决定使用这个框架进行开发。
提供下刘志龙大神对Arouter的架构和原理进行讲解概况地址,大家有兴致可以看下:
点击打开链接
Aouter Api包括三种类型:
api :com.alibaba:
注解器:com.alibaba:
自动注册插件:com.alibaba:
(以目前最新版本为列)在app的adle里面添加:
android {defaultConfig {...javaCompileOptions {annotationProcessorOptions {arguments = [ moduleName : Name() ]}}}
}
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配置列子:
项目层的adle,添加插件:
buildscript {ext.kotlin_version = '1.1.3-2'dependencies {classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"}
}
app层的adle:
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可以放在基础库中。
配置自动注册插件,在项目层的adle:
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();
跳转
Instance().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 {@Overridepublic void init(Context context) {}@Overridepublic <T> T json2Object(String text, Class<T> clazz) {return JSON.parseObject(text, clazz);}@Overridepublic String object2Json(Object instance) {JSONString(instance);}
}
Parcelable和Serializable对象传输是直接支持的。
接收数据的时候有两种方式
一种是原生使用的接收方式
String value = getIntent().getXXXExtra("key");
另一种是使用注释获取到,先在Activity中添加
@Autowired(name="key")
String key;
通过name来匹配,默认通过类型匹配。
Instance().build("/test/activity2").navigation(this, 666);
setResult(666);
@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {ActivityResult(requestCode, resultCode, data);switch (requestCode) {case 666:Log.e("activityResult", String.valueOf(resultCode));break;default:break;}}
定义协议:
<activity android:name=".SchemeFilterActivity"><!-- Schame --><intent-filter><dataandroid:host="m.aliyun"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是host
当我们跳转的时候需要一个中间者进行调度,如:SchemeFilterActivity就是用来对协议进行中间调度的
public class SchemeFilterActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {Create(savedInstanceState);// 直接通过ARouter处理外部UriUri uri = getIntent().getData();Instance().build(uri).navigation(this, new NavCallback() {@Overridepublic void onArrival(Postcard postcard) {finish();}});}
}
我们想要跳转时,可以这样, 比如需要跳转到 test/activity2
Uri testUriMix = Uri.parse("arouter://m.aliyun/test/activity2");Instance().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"/><dataandroid:host="m.aliyun"android:scheme="http"/><dataandroid:host="m.aliyun"android:scheme="https"/></intent-filter>
html中使用标签<a></a>
<a href="arouter://m.aliyun/test/activity3?name=alex&age=18&boy=true&high=180">arouter://m.aliyun/test/activity3?name=alex&age=18&boy=true&high=180</a>
后面?之后是传参数。相当于一个get请求一样。
Instance().build("/xxx/xxx").navigation(this, new NavCallback() {@Overridepublic void onFound(Postcard postcard) {Log.d("ARouter", "找到了");}@Overridepublic void onLost(Postcard postcard) {Log.d("ARouter", "找不到了");}@Overridepublic void onArrival(Postcard postcard) {Log.d("ARouter", "跳转完了");}@Overridepublic void onInterrupt(Postcard postcard) {Log.d("ARouter", "被拦截了");}});
使用NavCallback进行监听跳转过程中是否被拦截,或者是否完成跳转。比如我们可以在跳转后进行一些保存数据的操作,当没有发现Activity的时候提示模块未开放等功能。
跳转动画在Arouter里面实现也很简单,只需要定义好出入的动画然后调用如下:
Instance().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);Instance().build("/test/activity2").withOptionsCompat(compat).navigation();} else {Instance().build("/test/activity2").navigation();}
因为只有API大于16才支持共享元素,所以当少于16的时候我们直接跳转。
如果跳转的时候我们不设置Activity,比如Instance().build("/xxx/xxx").navigation();
那么默认的Flag就是:Intent.FLAG_ACTIVITY_NEW_TASK,Intent.FLAG_ACTIVITY_NEW_TASK类型launchMode="SingleTask"这是非常影响性能的操作,所以不建议这些写.
还有一种就是不设置,使用的是注册清单里面的luanchMode.
如果想要指定Flag,如下:
Instance().build("").withFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).navigation(this);
其中Flags值是有限制的,具体查阅@ FlagInt
获取Fragment
添加路由:
@Route(path = "/test/fragment")
public class BlankFragment extends Fragment
带参数的跳转
Fragment fragment = (Fragment) Instance().build("/test/fragment").withString("key","关键词").withObject("obj",testObj1).navigation();
在Fragment中接收,先添加
public BlankFragment() {Instance().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;/**拦截进行一些操作后 决定是否放行*/@Overridepublic void process(final Postcard postcard, final InterceptorCallback callback) {if ("/test/activity4".Path())) {MainLooper.runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(mContext,"您需要进行登录才能使用该功能",Toast.LENGTH_SHORT).show();}});} else {Continue(postcard);}}/*** 初始化*/@Overridepublic void init(Context context) {mContext = context;Log.e("testService", Name() + " has init.");}
}
其中priority决定优先级,一个优先级对应一个拦截器,拦截器是对所有Actvity都有效的,除非你在内部通过path剔除。通过
注意:process方法内部不是主线程。
当我们不希望拦截器拦截到我们,还可以设置绿色通道:
// 使用绿色通道(跳过所有的拦截器)
Instance().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();}@Overridepublic void init(Context context) {mContext = context;}
}
Service也需要定义路由,以便框架能够查找到。
我们可以使用三种方式获取到Service:
1、依赖注入 、
@Autowired
HelloService helloService;
2、根据HelloService .class类型获取
helloServiceInstance().navigation(HelloService.class);
类型查找只能实现单一的服务的时候才能用,多实现服务还是得用依赖注入和根据路径查找的方法。
3、根据路径查找
helloService = (HelloService) Instance().build("service/hello").navigation();服务还有Aouter内部的一些服务,比如PathReplaceService用来替换路径,SerializationService用来序列化对象,InterceptorServiceImpl用来处理拦截器的执行等服务,我们将在源码篇进行解读。
-keep public class com.alibaba.utes.**{*;}
-keep class * implements com.alibaba.android.plate.ISyringe{*;}# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口
-keep interface * implements com.alibaba.android.plate.IProvider# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
-keep class * implements com.alibaba.android.plate.IProvider
本文发布于:2024-01-29 19:45:38,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170652874417841.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |