ARouter入门使用篇 您所在的位置:网站首页 postcard的读音 ARouter入门使用篇

ARouter入门使用篇

#ARouter入门使用篇| 来源: 网络整理| 查看: 265

Android原生的路由方案是通过Intent来实现显式和隐式两种Activity跳转方案,显式Intent需要对目标Activity直接应用,会导致不同页面直接存在耦合的情况,隐式Intent存在Action集中配置在Manifest中,不便于管理的问题。而且在组件化开发中,各模块无法直接相互引用,路由跳转管理问题变成了必须要解决的问题。

ARouter是阿里开源的Android端路由框架,就是为了解决组件化开发中的路由跳转问题而被开发出来的。

一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦

GitHub项目地址:ARouter

浅谈路由框架原理

我们以组件化开发中Activity跳转为例,简单聊一下路由框架的实现原理。无论上层框架如何封装,activity的底层跳转总是要通过startActivity()实现的,那么就需要获取到目标Activity的实例或路径。为了实现模块间解耦,又不能直接引用目标Activity,最简单的办法就是给目标Activity设置一个简单的别名,然后通过映射表的方式Map维护别名与Activity的关系,那么这个映射表的实现只能下沉到所以模块都引用的基础模块中,比如base中。那么整个流程就很清楚了:

Activity提前将映射关系注入到Map中,当AActivity发起跳转到B的请求时,基础模块会从映射表中查找对应的Activity实例,然后进行跳转。如果找不到对应的Activity实例,可以将跳转结果回传避免引起异常。

ARouter的使用 添加依赖 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' ... } // 旧版本gradle插件(< 2.2),可以使用apt插件,配置方法见文末'其他#4' // Kotlin配置参考文末'其他#5'

目前最新版本(2021年12月7日)是1.5.2版本,以下介绍皆基于此版本说明。

混淆

添加混淆规则(如果使用了Proguard)

-keep public class com.alibaba.android.arouter.routes.**{*;} -keep public class com.alibaba.android.arouter.facade.**{*;} -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 初始化

官方建议尽早初始化,放到Application中

if (isDebug()) { // 这两行必须写在init之前,否则这些配置在init过程中将无效 ARouter.openLog(); // 打印日志 ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) } ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化

在开发调试过程中注意需要调用openDebug()方法,否则可能会出现找不到Activity的情况。

添加注解

在目标Activity/Fragment中用Router添加注解,path为路由路径

@Router(path = "/app/MainActivity") public class MainActivity extends Activity{ protected void onCreate(){ ... //注入 ARouter.getInstance().inject(this) } }

路径必须用/且至少有两级,同时需要在onCreate中进行注入。

发起路由

Activity跳转

ARouter.getInstance().build("/app/MainActivity").navigation();

Fragment

aFragment = (AFragment) ARouter.getInstance().build("/fragment/AFragment").navigation(); 传参

基本数据类型

传递参数:

ARouter.getInstance().build(路径) .withChar("CharKey", 'a') .withShort("ShortKey", 1) .withInt("IntKey", 11) .withLong("LongKey", 12l) .withFloat("FloatKey", 1.1f) .withDouble("DoubleKey", 1.1d) .withBoolean("BooleanKey", true) .withString("StringKey", "value") .navigation();

通过注解解析参数:

注意不能使用private关键字修饰。

//伪代码如下 public class TestActivity extends Activity{ //用Autowired注解 @Autowired(name = "IntKey") int i; @Autowired(name = "StringKey") String str; //...省略其他 onCreate(){ ... Log.d("IntKey = " + i + " StringKey = " + str); } }

传参过程中,可以省略nameARouter会自动根据类型匹配参数,但是建议都指定name,避免一些异常。

序列化对象

传递:

ARouter.getInstance().build("路径") .withSerializable("SerializableKey", new TestObj()) .withParcelable("ParcelableKey", new TestObj()) .navigation();

传递对接需要分别实现Serializable和Parcelable接口,调用的方法与基本数据类型大同小异,很好理解。

解析:

同样通过注解进行自动参数解析。

// 支持解析自定义对象,URL中使用json传递 @Autowired(name = "ParcelableKey") TestObj obj;

同样建议指定name,当然也支持省略name。

自定义对象

传递自定义对象的前提是对象不能实现Serializable或Parcelable接口。

传递:

ARouter.getInstance().build("路径") .withObject("ObjectKey", new TestObjectBean()) .navigation();

解析:

@Autowired(name = "ObjectKey") TestObjectBean object;

除了上述两步外,传递自定义对象必须新建一个类,实现 SerializationService,并使用@Route注解标注。

ARouter这么设计是为了方便用户自己选择Json解析方式。(路径随意指定一个不重复的就可以)

@Route(path = "/app/custom/json") public class JsonSerializationService implements SerializationService { Gson gson; @Override public T json2Object(String input, Class clazz) { return gson.fromJson(input,clazz); } @Override public String object2Json(Object instance) { return gson.toJson(instance); } @Override public T parseObject(String input, Type clazz) { return gson.fromJson(input,clazz); } @Override public void init(Context context) { gson = new Gson(); } }

除了初始化init方法,其他几个方法都是用来处理json和object对象转换的。

因为整个自定义对象的传递过程经历了一下几步:

withObhect("", obj) 调用SerializationService的2json方法将obj转换成string的json对象。 将string的json对象传递到目标页面。 目标页面调用调用SerializationService的2Object方法将json对象转换成obj对象。

也可以从withObject的源码中看到转换步骤:

/** * Set object value, the value will be convert to string by 'Fastjson' * * @param key a String, or null * @param value a Object, or null * @return current */ public Postcard withObject(@Nullable String key, @Nullable Object value) { serializationService = ARouter.getInstance().navigation(SerializationService.class); mBundle.putString(key, serializationService.object2Json(value)); return this; }

需要注意的是,因为参数的解析过程是通过类型匹配自动处理的,所以使用withObject()传递List和Map对象时,接收该对象时不能指定List和Map的实现了Serializable的实现类(ArrayList和HashMap等),可能有点拗口,简单来说就是接收withObject传参的对象,不能是Serializable或Parcelable的实现类。(如果指定了Serializable或Parcelable的实现类会影响序列化类型的判断。)

路由管理

上面的跳转处理过程中不可避免会需要指定很多的路由路径(如Activity路径等),为了方便管理和处理,通常会定义一个常量类去维护和管理所有的路由路径,并且为了各模块可以正常引用需要将常量类下沉到基础模块中(如BaseModule中),虽然这在一定程度上破坏了各模块的独立性(必须依赖常量类模块才能实现路由跳转),增加了业务模块与基础模块的耦合性,但是处于代码维护的角度考虑,这么做还是有必要的。

/** * 路由管理类 */ public class ARouterPath { /** push module */ public static final String SERVICE_PUSH_RECEIVER_URL = "/push/PushRegisterActivity"; /** Display Module */ public static final String ACTIVITY_DISPLAY_DISPLAY_URL = "/display/DisplayActivity"; public static final String SERVICE_DISPLAY_UPLOAD_URL = "/display/PushReceiver"; } 拦截器

之前在介绍OkHttp中提到过,OkHttp的拦截器设计是整个框架实现中特别经典,特别有亮点的部分。在ARouter中同样添加了拦截器的实现,拦截器可以对路由的过程进行拦截和处理。

@Interceptor(priority = 8, name = "测试拦截器") public class TestInterceptor implements IInterceptor { private static final String TAG = "Interceptor"; @Override public void process(Postcard postcard, InterceptorCallback callback) { String threadName = Thread.currentThread().getName(); Log.i(TAG, "拦截器开始执行,线程名称: " + threadName + "\n postcard = " + postcard.toString()); //拦截路由操作 callback.onInterrupt(new RuntimeException("有异常,禁止路由")); //继续路由操作 callback.onContinue(postcard); } @Override public void init(Context context) { Log.i(TAG, "TestInterceptor拦截器初始化"); } }

拦截器支持添加多个,通过注解@Interceptor()进行注解来实现拦截器的注册,priority用于定义拦截器的优先级,数字越小优先级越高,多个拦截器按优先级顺序执行。

多个拦截器不能拥有相同的优先级。 用name属性为拦截器指定名称(可省略)。 init()方法会在拦截器被初始化时自动调用。 process()当有路由操作被发起时会触发,可以根据需要通过onInterrupt()拦截路由,或者通过onContinue()继续路由操作,注意两个方法必须调用一个,否则路由会丢失不会继续执行。

拦截器比较经典的应用时用来判断登录事件,app中某些页面必须用户登录之后才能跳转,这样的话就可以通过拦截器做登录检查,避免在目标页面重复检查。

跳转结果监听处理:

ARouter.getInstance().build("路径") .navigation(this, new NavigationCallback() { @Override public void onFound(Postcard postcard) { //路由发现 } @Override public void onLost(Postcard postcard) { //路由丢失 } @Override public void onArrival(Postcard postcard) { //达到 } @Override public void onInterrupt(Postcard postcard) { //拦截 } });

在跳转时通过指定NavigationCallback进行跳转监听。如果只想监听到达事件也可以通过指定抽象类NavCallback来进行简化。

需要注意的是只有Activity才会触发拦截器,Fragment和IProvider并不支持拦截。

通过查看源码,可以发现拦截处理是在_ARouter的navigation()方法中处理的。

if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR. //处理拦截器 interceptorService.doInterceptions(postcard, new InterceptorCallback() { /** * Continue process * * @param postcard route meta */ @Override public void onContinue(Postcard postcard) { _navigation(postcard, requestCode, callback); } //省略 }

是否被拦截取决于postcard.isGreenChannel()值,而赋值是在LogisticsCenter的completion()方法中:

switch (routeMeta.getType()) { case PROVIDER: // if the route is provider, should find its instance // Its provider, so it must implement IProvider Class


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有