Android ARouter配置和使用

2,635 阅读7分钟

ARouter是一款优秀的组件化路由,项目地址:ARouter

一、配置

1、首先要明确项目是java项目还是kotlin项目,另外需要使用ARouter的每个Module都需要配置

java项目配置:

android {
    ...
    defaultConfig {
        ...
        //配置ARouter
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

//引入依赖
//配置ARouter
implementation 'com.alibaba:arouter-api:1.5.2'
annotationProcessor  'com.alibaba:arouter-compiler:1.5.2'

kotlin项目配置

plugins {
   ...
    id 'kotlin-kapt'   
}

android{
   ...
    //配置ARouter
    kapt {
        arguments {
            arg("AROUTER_MODULE_NAME", project.getName())
        }
    }
}

//引入依赖
//配置ARouter
implementation 'com.alibaba:arouter-api:1.5.2'
kapt 'com.alibaba:arouter-compiler:1.5.2'

2、在Application中初始化

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        var isDebug = true   //伪代码,注意上线前关闭

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

    }
}

3、跳转测试

创建了一个新的SecondActivity,目录结构如下:

image.png

MainActivity只是设置了点击事件:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        findViewById<Button>(R.id.bt).setOnClickListener {
            //跳转到SecondActivity
            ARouter.getInstance().build("/testarouter/SecondActivity").navigation()
        }
    }
}

SecondActivity注意要定义路径Path,必须是以“/” 开头,且必须至少包含二个“/” (至少包含二级)

@Route(path = "/testarouter/SecondActivity")
class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
    }
}

跳转测试成功
配置和跳转写的很详细是因为前面容易踩坑There is no route match the path [/xxx/xxx]找不到路径,如果还是碰到问题,请查看issue上的问题集真正解决"There is no route match the path [/xxx/xxx], in group [xxx][ ]"

4、混淆见Github项目地址ARouter

5、使用 IDE 插件导航到目标类

官方的插件ARouter Helper只支持java,不支持kotlin,但看到目前有其他插件支持kotlin

image.png

安装后如下

image.png

image.png

有多个跳回地址时可选

二、具体使用

1、Path路径封装

一般会将Path路径写一个集中管理类

class ARouterPath {

    companion object {
       const val  SecondActivity = "/testarouter/SecondActivity";
    }

}

调用

//跳转到SecondActivity
ARouter.getInstance().build(ARouterPath.SecondActivity).navigation()

@Route(path = ARouterPath.SecondActivity)
class SecondActivity : AppCompatActivity() {...}

2、Fragment获取并加载

创建一个普通Fragment

@Route(path = ARouterPath.MainFragment)
class MainFragment : Fragment() {...}

Activity获取Fragment并加载

//获取Fragment对象
var fragment: MainFragment =
    ARouter.getInstance().build(ARouterPath.MainFragment).navigation() as MainFragment
//容器加载Fragment
supportFragmentManager.beginTransaction().replace(R.id.fl_container, fragment).commit()

3、传值

带参数跳转,以下是API

image.png

1)传递普通数据类型及Bundle

相对比较简单,注意定义注解@Autowired,注意inject注入。在java中接收值的Activity的对应参数必须是public的。

传值

//跳转到SecondActivity
ARouter.getInstance()
    .build(ARouterPath.SecondActivity)
    .withString("name","zhangsan")
    .withInt("age",22)
    .navigation()

接收值

@Route(path = ARouterPath.SecondActivity)
class SecondActivity : AppCompatActivity() {

    @JvmField
    @Autowired
    var name: String? = null

    @JvmField
    @Autowired(name = "age")  //扩展命名空间
    var userAge: Int = 0   //不能写null会运行崩溃,因为ARouter有转Integer的操作

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //需要注入,这一步不要忘记
        ARouter.getInstance().inject(this@SecondActivity)
        setContentView(R.layout.activity_second)

    }
}

2)传递对象

自定义了一个User类,代码如下:

class User(var name: String, var age: Int = 0){
    override fun toString(): String {
        return "User(name='$name', age=$age)"
    }
}

传值

//跳转到SecondActivity
ARouter.getInstance()
    .build(ARouterPath.SecondActivity)
    .withObject("user", User("zhangsan", 22))
    .navigation()

发现报错空指针异常,看报错的点:

image.png

原来需要引入Fastjson,不然下面红框部位就报空指针异常,因为没有SerializationService的实现类。于是引入Fastjson,发现还是报错,如下:

Snipaste_2022-04-09_16-13-00.png

找不到默认的构造方法,但是Kotlin的Data类是有的,看报错的地点:

image.png

image.png

看网友的博客才知道这是一个创建工程时没有引入的Kotlin的库,如下:

implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

于是终于不蹦了,看接收值的代码:

@Route(path = ARouterPath.SecondActivity)
class SecondActivity : AppCompatActivity() {

    @JvmField
    @Autowired
    var user: User? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //需要注入
        ARouter.getInstance().inject(this@SecondActivity)
        setContentView(R.layout.activity_second)
        
    }
}

运行成功。

将Fastjson改为Gson:

@Route(path = ARouterPath.CommonJson)
class JsonServiceImpl : SerializationService {

    private val gson: Gson by lazy {
        Gson()
    }

    override fun init(context: Context?) {

    }

    override fun <T : Any?> json2Object(input: String?, clazz: Class<T>?): T {
        return gson.fromJson(input, clazz)
    }

    override fun object2Json(instance: Any?): String {
        return gson.toJson(instance)
    }

    override fun <T : Any?> parseObject(input: String?, clazz: Type?): T {
        return gson.fromJson(input, clazz)
    }
}

运行传值成功。
ARouter会根据SerializationService的实现类来序列化和反序列化对象,实现SerializationService后,不需要再添加其他代码。

另外一个需要注意的点是如果自定义对象类型是serializable,那么会被当成serializable处理,如果是parcelable那么会被当成parcelable方式处理,只有在不是 parcelable 也不是 serializable 的时候,才会当成自定义对象处理。 所以只需要将自定义的类,不要去实现 Serializable, Parcelable 接口 这两个接口,那么就可以正常传值。

3)List和Map类型

根据官方文档,接收List或Map类型的值,不能标注具体的实现类类型。如下:

image.png

传值

var arrayList = ArrayList<User>()
var user = User("zhangsan",100)
arrayList.add(user)

//跳转到SecondActivity
ARouter.getInstance()
    .build(ARouterPath.SecondActivity)
    .withObject("arrayList",arrayList)
    .navigation()

接收值

@Route(path = ARouterPath.SecondActivity)
class SecondActivity : AppCompatActivity() {

    @JvmField
    @Autowired
    var arrayList: List<User>? = null  //不能是ArrayList

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        //需要注入
        ARouter.getInstance().inject(this@SecondActivity)
        setContentView(R.layout.activity_second)
    }
}

接收值成功。

4、回传值

1)常规处理

Android的页面跳转有时候是需要回传值的,那么ARouter中应该如何回传? ARouter跳转的navigation方法中有带requestCode的方法,这个方法在requstCode大于等于0时就是调用的startActivityForRestult。这里是一个要注意的点。

image.png

navigation方法

image.png

重写onActivityResult方法,做相应处理即可,跟平时一样。

2)Fragment中回传值

使用ARouter,从Fragment跳转到其他Activity,需要返回值时,回调的值是在Fragment所在Activity的onActivityResult方法中,而不是Fragment的onActivityResult方法中。解决方法:

//获取跳转的Postcard,Postcard中有跳转的所有信息
val postcard: Postcard = ARouter.getInstance()
    .build(ARouterPath.SecondActivity)   //跳转的Activity
    .withInt("num", 123)    //携带参数
//Completion the postcard by route metas(完成明信片)
LogisticsCenter.completion(postcard)
//通过Arouter 的Postcard创建 intent
var intent = Intent(activity, postcard.destination)
// 将Postcard中的参数赋值给intent
intent.putExtras(postcard.extras)
//Fragment自己来跳转
startActivityForResult(intent,REQUES_CODE)


//在Fragment的onActivityResult中处理数据
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUES_CODE && resultCode == AppCompatActivity.RESULT_OK) {
        if (data != null) {
           //处理数据
        }
    }
}

5、降级策略

1)什么是降级策略

跳转过程中如果出现错误,可以进行处理,一种是坚挺路由操作的回调方法NavigationCallback,另一种是全局处理(实现DegradeService接口)。NavigationCallback优先级比全局处理的高,不会再进入DegradeService

2)二种降级策略

坚挺路由

//跳转到SecondActivity
ARouter.getInstance()
    .build(ARouterPath.SecondActivity)
    .navigation(this, object : NavigationCallback {
        override fun onFound(postcard: Postcard?) {
            //找到跳转目标
        }

        override fun onLost(postcard: Postcard?) {
           //丢失跳转目标
        }

        override fun onArrival(postcard: Postcard?) {
            //跳转完成
        }

        override fun onInterrupt(postcard: Postcard?) {
            //路由被拦截
        }

    })

全局处理

@Route(path = "/AA/BB/CC")    //Route注解采用任意的Path都可以
class DegradServiceImpl : DegradeService {
    override fun init(context: Context?) {

    }

    override fun onLost(context: Context?, postcard: Postcard?) {
        //丢失目标时的处理
    }
}

6、拦截器

拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行。

@Interceptor(priority = 1, name = "拦截器名称")  //多个拦截器会按优先级顺序依次执行
class TestInterceptor : IInterceptor {

    override fun init(context: Context?) {
        //拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次
    }

    override fun process(postcard: Postcard?, callback: InterceptorCallback?) {
        //Postcard包含跳转的所有信息
        //获取跳转的路径
        postcard?.path
        //获取Bundle
        var bundle = postcard?.extras
        //通过Bundle获取跳转携带的值
        bundle?.getInt("num")
        //额外添加值
        postcard?.withInt("num", 123)

        //以下两种至少需要调用其中一种,否则不会继续路由
        //1、继续路由
        callback?.onContinue(postcard)
        //2、中断路由
        callback?.onInterrupt(null)
        callback?.onInterrupt(RuntimeException("有异常"))
    }
}

7、转场动画

方式一:

// 转场动画(常规方式)
ARouter.getInstance()
    .build("/test/activity2")
    .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
    .navigation(this);

方式二:

// 转场动画(API16+)
ActivityOptionsCompat compat = ActivityOptionsCompat.
    makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0);

// ps. makeSceneTransitionAnimation 使用共享元素的时候,需要在navigation方法中传入当前Activity

ARouter.getInstance()
    .build("/test/activity2")
    .withOptionsCompat(compat)
    .navigation();

8、跨模块调用服务

1)通过依赖注入解耦:服务管理(一) 暴露服务

ARouter提供了IProvider接口,模块可以继承IProvider接口来暴露服务

// 声明接口,其他组件通过接口来调用服务
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) {

    }
}

2)通过依赖注入解耦:服务管理(二) 发现服务

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");
    }
}

参考了以下博客,表示感谢:

kotlin fastjson:defalut constructor not found

ARouter 传自定义对象获取值为null的解析 及解决方法

startActivityForResult与ARouter在fragment中使用startActivityForResult

组件化基础ARouter(二、跨模块API调用)

--个人学习笔记