Arouter入门篇
阅读原文时间:2021年04月21日阅读:2

    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

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中初始化

Activity跳转

    简单的Activity跳转:

    使用Router定义路径

@Route(path = "/test/activity1")
public class Test1Activity extends AppCompatActivity {

    跳转

ARouter
.getInstance()
.build("/test/activity1").navigation();

   

    带基础参数的Activity

    跳转

 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来匹配,默认通过类型匹配。

   需要返回的Activity跳转

    

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

    定义协议:

<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>

html中使用标签

<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请求一样。

跳转监听器回调NavCallback

    

 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导致的。

共享元素的转场动画

 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;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的时候我们直接跳转。

指定Flag

如果跳转的时候我们不设置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();

使用Arouter内置的服务

什么情况下我们会使用到服务呢?

目前市面上比较流行,组件化,组件化的要求就是热拔插,能随时分离各个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

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章