Android跳转-ARouter详细使用教程

转自:https://blog.csdn.net/niubitianping/article/details/77982033

一、简介
Android平台中对页面、服务提供路由功能(跳转)的一个库

1.1 最新版本

模块    arouter-api    arouter-compiler    arouter-annotation
最新版本            
1.2 实例图片

1.3 功能介绍

支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
支持多模块工程使用
支持添加多个拦截器,自定义拦截顺序
支持依赖注入,可单独作为依赖注入框架使用
支持InstantRun
支持MultiDex(Google方案)
映射关系按组分类、多级管理,按需初始化
支持用户指定全局降级与局部降级策略
页面、拦截器、服务等组件均自动注册到框架
支持多种方式配置转场动画
支持获取Fragment
完全支持Kotlin以及混编(配置见文末 其他#5)
1.4 应用场景

从外部URL映射到内部页面,以及参数传递与解析
跨模块页面跳转,模块间解耦
拦截跳转过程,处理登陆、埋点等逻辑
跨模块API调用,通过控制反转来做组件解耦
二、使用方法
1.1 导包
导包现在分java和kotlin, java的导包方法如下,在根moduel的build.gradle添加如下内容:

android {
    defaultConfig {
    ...
    javaCompileOptions {
        annotationProcessorOptions {
        arguments = [ moduleName : project.getName() ]
        }
    }
    }
}

dependencies {
    // 替换成最新版本, 需要注意的是api,最新版本看文章开头
    // 要与compiler匹配使用,均使用最新版可以保证兼容
    compile 'com.alibaba:arouter-api:x.x.x'
    annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
    ...
}

如果java使用的是apt,导入方法则是如下:

apply plugin: 'com.neenbedankt.android-apt'

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

apt {
    arguments {
        moduleName project.getName();
    }
}

dependencies {
    //这里填写最新的版本看文章开始
    compile 'com.alibaba:arouter-api:x.x.x'
    apt 'com.alibaba:arouter-compiler:x.x.x'
    ...
}

kotlin的导包方法如下,在根moduel的build.gradle添加如下内容:

apply plugin: 'kotlin-kapt'

kapt {
    arguments {
        arg("moduleName", project.getName())
    }
}

dependencies {
    //这里填写最新的版本,看文章开始
    compile 'com.alibaba:arouter-api:x.x.x'
    kapt 'com.alibaba:arouter-compiler:x.x.x'
    ...
}

1.2 初始化
解析在代码中

class BaseApplication :Application(){

    override fun onCreate() {
        super.onCreate()

        if(BuildConfig.DEBUG){ //如果在debug模式下
            // 打印日志,默认关闭
            ARouter.openLog()
            // 开启调试模式,默认关闭(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
            ARouter.openDebug()
            // 打印日志的时候打印线程堆栈
            ARouter.printStackTrace()
        } 

        // 尽可能早,推荐在Application中初始化
        ARouter.init(this) 

    }
}

1.3 普通的Activity跳转
方法又有Uri和path两种

利用ARouter的path方法:

例如我要在 AActivity跳转到BActivity,这时候AActivity的代码是这样子的(记得编写manifest):

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

        textView = findViewById(R.id.textView) as TextView

        textView!!.setOnClickListener {
            ARouter.getInstance()
                    .build("/path/bactivity")
                    .navigation()
        }
    }

然后BActivity的代码是这样子的(记得编写manifest):

@Route(path = "/path/bactivity")
class BActivity :AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }

}

可以看到,跳转是利用build方法里面的一个字符串进行标识,然后调用navigation进行跳转到 注解@Route标记的字符串路径。

利用Uri方法:

例如上面的AActivity里面的跳转方法改为:

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

        textView = findViewById(R.id.textView) as TextView

        textView!!.setOnClickListener {

            val uri = Uri.parse("/path/bactivity")

            ARouter.getInstance()
                    .build(uri)
                    .navigation()
        }
    }

然后BActivity保持不变,效果是一样的。

PS: Uri.parse的时候,改为完整路径也是支持的,例如改为val uri = Uri.parse(“tpnet://m.aliyun.com/path/bactivity”),也可以成功跳转到BActivity

1.4 普通的Activity跳转参数
调用with对应的类型即可,

ARouter.getInstance().build(RouterPath.PAGE_TEST1)
                    //第一个参数为key,第二个参数为值
                    .withLong("longKey", 0x555555L)
                    .withString("stringKey", "66666")
                    .navigation()

在接收的activity解析的时候,获取到extras,get对应的类型即可:

    val extrsa = intent.extras
    println(extrsa.getLong("longKey"))
    println(extrsa.getString("stringKey"))

1.5普通的Activity跳转动画
添加跳转动画有两种方法,一个是兼容,一个是只能大于等于sdk16才能用。

兼容方法:

使用withTransition方法,添加 进入动画,退出动画即可

ARouter.getInstance()
        .build("/path/bactivity")
        //参数1为打开的Activity的进入动画,参数2为当前的Activity的退出动画
        .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
        .navigation(this);

sdk大于等于16的动画方法:

利用withOptionsCompat添加ActivityOptionsCompat对象,

if (Build.VERSION.SDK_INT >= 16) {
    ActivityOptionsCompat compat = ActivityOptionsCompat.
            makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0);

    ARouter.getInstance()
            .build("/path/bactivity")
            .withOptionsCompat(compat)
            .navigation();
} else {
    Toast.makeText(this, "API < 16,不支持新版本动画", Toast.LENGTH_SHORT).show();
}

PS: akeSceneTransitionAnimation 使用共享元素的时候,需要在navigation方法中传入当前Activity

三、 Url跳转
Url跳转就是可以根据url来跳转,可以从网页跳到Activity

3.1 网页url正常跳转Activity
首先定义一个Activity作为中转,网页的链接都跳到这个Activity,然后再从这个Activity打开网页需要打开的Activity

class SchemeFilterActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        ARouter.getInstance().build(intent.data).navigation(this,object: NavCallback() {
            override fun onArrival(postcard: Postcard?) {
                finish()
            }
        })
    }
}

该Activity的manifest为:

<activity android:name=".Url.SchemeFilterActivity">
    <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" />
    </intent-filter>
</activity>

然后定义你要跳转到的Activity,这里定义一个UrlTargetActivity (记得在Manifest说明):

@Route(path = "/test/activity1")
class UrlTargetActivity : AppCompatActivity() {

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

        val tvContent = findViewById(R.id.content) as TextView

        tvContent.text = "当前页面是:" + this@UrlTargetActivity::class.java.name

    }
}

然后在手机打开一个网页,网页内容为:

<a href="arouter://m.aliyun.com/test/activity1">arouter://m.aliyun.com/test/activity1</a></p>
1
点击了这个网页链接之后,就到根据"arouter://m.aliyun.com(scheme://host)跳转到SchemeFilterActivity这个Activity,然后在这个Activity利用ARouter 根据path = test/activity1跳转到UrlTargetActivity

3.2 Url跳转带常用类型参数
上面的网页链接添加name、age、sex三个参数,例如:

<p><a href="arouter://m.aliyun.com/test/activity1?name=tpnet&age=21&sex=true">arouter://m.aliyun.com/test/activity1</a></p>
1
然后,在目标Activity: UrlTargetActivity修改为:

@Route(path = "/test/activity1")
class UrlTargetActivity : AppCompatActivity() {

    @JvmField
    @Autowired
    var name: String = ""

    @JvmField
    @Autowired
    var age: Int = 0

    @JvmField
    @Autowired(name = "sex")
    var gender: Boolean = false


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

        val tvContent = findViewById(R.id.content) as TextView

        tvContent.text = "当前页面是:" + this@UrlTargetActivity::class.java.name


        ARouter.getInstance().inject(this)

        tvContent.text = tvContent.text as String + "\n$name - $age - $gender"


    }
}

可以看到,使用@Autowired 这个注解变量,然后在onCreate中使用ARouter.getInstance().inject(this) 进行注入,即可得到参数。

那参数怎么样对应变量呢?

1是根据变量名
2是Autowired注解里面有个属性name,这个属性指定参数名称

PS: @JvmField这个注解,是因为我用的是Kotlin,ARouter使用的是java,所以为了兼容,需要加这个注解

3.3 Url跳转带自定义类型参数
网页通过传递json参数,然后ARouter帮你自动转换为你的自定义对象。

例如,网页链接为:

<p><a href="arouter://m.aliyun.com/test/activity1?name=tpnet&age=21&sex=true&obj=%7B%22name%22:%22jack%22,%22id%22:666%7D">带json自定义对象</a></p>

然后写一个类,实现SerializationService接口,用来实现json的序列化和反序列化。注意这个类也需要添加path,内容不规定

//这里需要添加path,内容随便
@Route(path = "/service/json")
class JsonObjectImpl : SerializationService {

    override fun init(context: Context?) {

    }


    //json字符串转换为对象
    override fun <T : Any?> json2Object(json: String?, clazz: Class<T>?): T {
        Log.e("@@","json:"+json)
        return JSON.parseObject(json, clazz)
    }


    //自定义对象转换为json字符串
    override fun object2Json(instance: Any?): String = JSON.toJSONString(instance)
}

然后就是自定义的对象:

class TestObj() {


    var name: String = ""     //这里变量名称对应Url的json的key
    var id: Int = 0

    override fun toString(): String = "TestObj(name='$name', id=$id)"

}

然后要打开的目标Activtiy接收自定义对象,如常一样,加一个变量即可,记得变量名字要和url的key一致,或者在Autowired注解添加name属性

    @JvmField
    @Autowired
    var obj: TestObj? = null

PS: 也支持Parcelable

四、 跳转拦截器
拦截器的意思是,例如你想在 AActivity跳到BActivity,如果有拦截器,就可以把这个过程拦截下来,做一些处理(禁止跳转、修改参数)。

添加拦截器的方法是利用Interceptor注解,实现IInterceptor接口。例如我添加一个拦截器:

//priority 为拦截器的优先级,多个拦截器时候有用
@Interceptor(priority = 8,name = "测试用拦截器")
class TestInterceptor : IInterceptor {


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

    /**
     * 拦截器的操作
     * @param postcard 数据
     * @param callback 回调
     */
    override fun process(postcard: Postcard?, callback: InterceptorCallback?) {

        if(postcard?.path == "/path/bactivity"){
            //如果是跳到BActivity,就加个数据
            postcard.withString("extra", "我是在拦截器中附加的参数")
        }

        //继续跳转
        callback!!.onContinue(postcard)

        //终止跳转
        //callback.onInterrupt(null)

        //抛出异常
        // callback.onInterrupt(RuntimeException("我觉得有点异常"))

        // onContinue和onInterrupt至少需要调用其中一种,否则不会继续路由
    }


}

这样子,在BActivity接收的时候,就可以获取到extra,这个key的字符串了

五、 降级策略
什么鬼是降级策略? 因为打开activity是系统级别的操作,我们提交之后了跳转之后,很难插手,所以降级策略其实就是 在跳转过程中,如果出现错误的话,可以进行处理跳转。 方法有两种,一个是处理单词跳转,一个是全局的跳转处理。

方式1: 单个处理-回调方法:

在navigation的时候,添加NavCallback进行回调,回调方法有四个,出现错误的时候,在onLost方法下面处理即可。

ARouter.getInstance().build(intent.data).navigation(this,object: NavCallback() {
            override fun onArrival(postcard: Postcard?) {
                //已经打开了目标activity
                Log.e("@@","目标activity打开完成")
                finish()
            }

            override fun onFound(postcard: Postcard?) {
                //找到了要打开的activity
                Log.e("@@","找到了目标activity")


            }

            override fun onLost(postcard: Postcard?) {
                //找不到要打开的activity
                Log.e("@@","找不到目标activity")

            }

            override fun onInterrupt(postcard: Postcard?) {
                super.onInterrupt(postcard)
                Log.e("@@","被拦截了")
            }

        })

方法2:全局处理-实现接口

如果找不到目标Activity,ARouter默认会提示一个Toast,找不到对象。例如:

    ARouter.getInstance().build("/xxxxx/xxxxx").navigation(this)
1
2
这时候如果你要自己全局处理,只需要实现DegradeService接口,并加上一个Path内容任意的注解即可

//path里面的内容可以任意,注意两个斜杠就行
@Route(path = "/cccc/ddd")
class DegradeServiceImpl : DegradeService {

    //失败的时候处理,注意:如果在navigation时候没有传递context,这个方法的context会是空的
    override fun onLost(context: Context?, postcard: Postcard?) {
        Toast.makeText( context,"找不到路径" +postcard?.path ,Toast.LENGTH_SHORT).show()

    }


    override fun init(context: Context?) {}

}

PS: 不能两种同时使用,单个处理级的方式优先于全局处理,也就是如果同时使用两种方式,只有单独降级能执行。

六、服务管理
这里说的服务不是Android四大组件里面的服务,其实是根据path去获取对象。

例如:首先写一个接口,实现IProvider接口,定义一个方法sayHello

// 声明接口
interface HelloService : IProvider {
    fun sayHello(name: String)
}

然后写一个实现类,添加注解@Route,path内容不固定:

//实现接口
@Route(path = "/service/hello",name = "测试服务")
class HelloServiceImpl : HelloService {

    override fun sayHello(name: String){
        println(name)
    }

    override fun init(context: Context?) {}

}

然后就可以利用ARouter来调用实现类HelloServiceImpl

//方法1 用类的形式,在navigation方法添加参数
ARouter.getInstance().navigation(HelloService::class.java).sayHello("mike")

// 方法2 用path方式,在build添加实现类的Router路径
(ARouter.getInstance().build("/service/hello").navigation() as HelloService).sayHello("tpnet")

还可以利用注入的方式:

class ServiceObj {


    @JvmField
    @Autowired
    var helloService: HelloService? = null

    @JvmField
    @Autowired(name = "/service/hello")
    var helloService2: HelloService? = null

    init {

        ARouter.getInstance().inject(this)
    }

    fun testService(){

        // 1. (推荐)使用依赖注入的方式发现服务,通过注解标注字段,即可使用,无需主动获取
        helloService?.sayHello("Vergil")

        // Autowired注解中标注name之后,将会使用byName的方式注入对应的字段,不设置name属性,会默认使用byType的方式发现服务
        // (当同一接口有多个实现的时候,必须使用byName的方式发现服务)
        helloService2?.sayHello("Vergil")

    }

}

七、其他使用
当然一个库的功能是很多的,还有其他的使用方面。

获取Fragment实例
在需要获取的Fragment添加注解@Route

@Route(path = "/test/fragment")
public class BlankFragment extends Fragment {}

获取的时候,强制转换。

Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation();

带RequestCode跳转
在navigation方法的第二个参数添加即可:

ARouter.getInstance().build("/test/activity2").navigation(this, 666);

为目标页面声明更多信息
// 我们经常需要在目标页面中配置一些属性,比方说”是否需要登陆”之类的 
// 可以通过 Route 注解中的 extras 属性进行扩展,这个属性是一个 int值,换句话说,单个int有4字节,也就是32位,可以配置32个开关 
// 剩下的可以自行发挥,通过字节操作可以标识32个开关,通过开关标记目标页面的一些属性,在拦截器中可以拿到这个标记进行业务逻辑判断

@Route(path = "/test/activity", extras = Consts.XXXX)

在拦截器中的process方法,利用getExtra方法即可获取到这个内容。

重写跳转URL
// 实现PathReplaceService接口,并加上一个Path内容任意的注解即可
@Route(path = "/xxx/xxx") // 必须标明注解
public class PathReplaceServiceImpl implements PathReplaceService {
    /**
     * For normal path.
     *
     * @param path raw path
     */
    String forString(String path) {
    return path;    // 按照一定的规则处理之后返回处理后的结果
    }

   /**
    * For uri type.
    *
    * @param uri raw uri
    */
   Uri forUri(Uri uri) {
    return url;    // 按照一定的规则处理之后返回处理后的结果
   }
}

跳转到其他Module
方法1: 和正常跳转一样,设置path,然后navigation过去

方法2: 指定group分组,例如:

@Route(path = "/path/module",group = "m2")
class ModuleActivity : AppCompatActivity() {

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

调用则:

    ARouter.getInstance().build("/path/module", "m2").navigation()

PS: 一旦主动指定分组之后,应用内路由需要使用 ARouter.getInstance().build(path, group) 进行跳转,手动指定分组,否则无法找到

Api总结
// 构建标准的路由请求
ARouter.getInstance().build("/home/main").navigation();

// 构建标准的路由请求,并指定分组
ARouter.getInstance().build("/home/main", "ap").navigation();

// 构建标准的路由请求,通过Uri直接解析
Uri uri;
ARouter.getInstance().build(uri).navigation();

// 构建标准的路由请求,startActivityForResult
// navigation的第一个参数必须是Activity,第二个参数则是RequestCode
ARouter.getInstance().build("/home/main", "ap").navigation(this, 5);

// 直接传递Bundle
Bundle params = new Bundle();
ARouter.getInstance()
    .build("/home/main")
    .with(params)
    .navigation();

// 指定Flag
ARouter.getInstance()
    .build("/home/main")
    .withFlags();
    .navigation();

// 获取Fragment
Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation();

// 对象传递
ARouter.getInstance()
    .withObject("key", new TestObj("Jack", "Rose"))
    .navigation();

// 觉得接口不够多,可以直接拿出Bundle赋值
ARouter.getInstance()
        .build("/home/main")
        .getExtra();

// 转场动画(常规方式)
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();

// 使用绿色通道(跳过所有的拦截器)
ARouter.getInstance().build("/home/main").greenChannel().navigation();

// 使用自己的日志工具打印日志
ARouter.setLogger();

//获取原始的URI
String uriStr = getIntent().getStringExtra(ARouter.RAW_URI);

//关闭ARouter
ARouter.getInstance().destroy();

--------------------- 
作者:SkyHand天天 
来源:CSDN 
原文:https://blog.csdn.net/niubitianping/article/details/77982033 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值