美文网首页
Android路由框架-ARouter 使用

Android路由框架-ARouter 使用

作者: 王灵 | 来源:发表于2019-02-27 17:58 被阅读0次

    ARouter是什么

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

    第一次接触可能会比较模糊,我画图讲讲它到底是解决什么问题。
    一般情况下我们的项目结构应该是这样:


    ARouter-1.jpg

    但是我们的需求可能是APP、Module1、Module2、Module3、Module4之间能够交互,至少是Activity能够跳转。互相依赖明显是最差的方案。

    ARouter的主要功能就是解决这个问题,使用ARouter的项目结构会有一些改变,不过不大至少比互相引用强多了

    ARouter-2.jpg
    这个时候APP模块有一个名称:宿主APP,由于ARouter的限制,宿主APP必须对所有的Module依赖(不懂内部实现所有我就不发表评论了)。
    使用ARouter之后不仅APP可以跳转到各个Module,而且APP与Module、Module与Module之间都是可以互相跳转的。

    ARouter能为我们做什么

    虽然上面简单的说了下ARouter可以实现模块间交互,但是只是ARouter的功能之一。下面引用github上的介绍

    1、支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
    2、支持多模块工程使用
    3、支持添加多个拦截器,自定义拦截顺序
    4、支持依赖注入,可单独作为依赖注入框架使用
    5、支持InstantRun
    6、支持MultiDex(Google方案)
    7、映射关系按组分类、多级管理,按需初始化
    8、支持用户指定全局降级与局部降级策略
    9、页面、拦截器、服务等组件均自动注册到框架
    10、支持多种方式配置转场动画
    11、支持获取Fragment
    12、完全支持Kotlin以及混编(配置见文末 其他#5)
    13、支持第三方 App 加固(使用 arouter-register 实现自动注册)
    14、支持生成路由文档
    15、提供 IDE 插件便捷的关联路径和目标类

    ARouter配置

    1、添加依赖和配置

    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'
    

    每个module都需要这么配置,我使用的版本

    implementation 'com.alibaba:arouter-api:1.4.0'
    annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'
    

    2、添加注解
    注意不同模块的一级路径必须不同,建议使用module名。比如/navigation/testActivity

    // 在支持路由的页面上添加注解(必选)
    // 这里的路径需要注意的是至少需要有两级,/xx/xx
    @Route(path = "/test/activity")
    public class YourActivity extend Activity {
        ...
    }
    

    3、初始化SDK

    public class MyApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
    
            if (isDebug()) {           // These two lines must be written before init, otherwise these configurations will be invalid in the init process
                ARouter.openLog();     // Print log
                ARouter.openDebug();   // Turn on debugging mode (If you are running in InstantRun mode, you must turn on debug mode! Online version needs to be closed, otherwise there is a security risk)
            }
            ARouter.init(this); // As early as possible, it is recommended to initialize in the Application
    
        }
    }
    

    5、添加混淆规则(如果使用了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
    

    6、使用 Gradle 插件实现路由表的自动加载 (可选)

    apply plugin: 'com.alibaba.arouter'
    
    buildscript {
        repositories {
            jcenter()
        }
    
        dependencies {
            classpath "com.alibaba:arouter-register:?"
        }
    }
    

    我当前使用classpath "com.alibaba:arouter-register:1.0.2"
    7、使用 IDE 插件导航到目标类 (可选) 非常好用,强烈推荐

    在 Android Studio 插件市场中搜索 ARouter Helper, 或者直接下载文档上方 最新版本 中列出的 arouter-idea-plugin zip 安装包手动安装,安装后 插件无任何设置,可以在跳转代码的行首找到一个图标

    arouter1.png
    点击该图标,即可跳转到标识了代码中路径的目标类

    估计你已经看出来了,上面少了4、 因为我想把它单独说

    ARouter使用

    页面跳转有时候我们需要携带参数,有时候还需要获取页面关闭返回的数据,往下看
    a.不携带数据直接跳转
    ARouter.getInstance().build("/matthew/activity").navigation();
    b.携带数据

    ARouter.getInstance().build("/test/1")
                .withLong("key1", 666L)
                .withString("key3", "888")
                .withObject("key4", new Test("Jack", "Rose"))
                .navigation();
    

    注意: 携带非基础类型数据 需要实现SerializationService并添加@Route注解

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

    c.需要获取返回数据
    navigation函数的最后一个参数就是requestCode

     ARouter.getInstance().build("/matthew/activity")
                    .withObject("json", new TestBean())
                    .navigation(context, 100);
    ...
     @Override
        protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            ...
        }
    

    d、URL跳转
    需要先配置目标Activity

     <activity android:name=".TestArouteActivity">
    
                <!-- Scheme -->
                <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>
    

    然后

            //URL跳转
            Uri url = Uri.parse("arouter://m.aliyun.com/matthew/activity");
            ARouter.getInstance().build(url)
                    .withObject("json", new TestBean())
                    .navigation();
    //                .navigation(this, 100);
    
    处理跳转结果
    // 设置跳转回调,可以获得跳转结果
                    ARouter.getInstance().build("/test/activity4").navigation(this, new NavCallback() {
                        @Override
                        public void onFound(Postcard postcard) {
                            LogUtils.i("ARouter - 找到了");
                        }
     
                        @Override
                        public void onLost(Postcard postcard) {
                            LogUtils.i("ARouter - 找不到了");
                        }
     
                        @Override
                        public void onArrival(Postcard postcard) {
                            LogUtils.i("ARouter - 跳转完成");
                        }
     
                        @Override
                        public void onInterrupt(Postcard postcard) {
                            LogUtils.i("ARouter - 被拦截了");
                        }
                    });
    
    

    如果是需要返回数据,
    navigation(Activity mContext, int requestCode, NavigationCallback callback)

    1597575437690.jpg

    数据解析

    上面讲了携带数据跳转。这里说说如何获取携带过来的数据;为每一个参数声明一个字段,并使用 @Autowired标注。并且(在使用参数之前)添加ARouter.getInstance().inject(this);对参数进行自动注入。
    比如上面我使用.withObject("json", new TestBean())携带了一个TestBean字段名是json
    我们可以这样获取

     @Autowired
        TestBean json;
    

    也可以这样,使用别名

     @Autowired(name = "json")
        TestBean bean;
    

    我的完整类

    @Route(path = "/matthew/activity")
    public class TestArouteActivity extends AppCompatActivity {
        // 支持解析自定义对象,URL中使用json传递
        @Autowired(name = "json")
        TestBean bean;
    
       // @Autowired
      //  TestBean json;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test_aroute);
            KLog.e(getLocalClassName());
            ARouter.getInstance().inject(this);
    
    
            ((TextView) findViewById(R.id.text_view)).setText(bean.toString());
    
        }
    

    拦截器与extras

    其实拦截器和extras是两个分开的功能,我合到一起是因为想偷懒。
    一个场景:我参与的电商项目中有多个界面都是必须登陆才能进入的。是拦截器+extras可以非常完美的解决,不需要在每个界面去判断是否登陆。如何做?
    1、在需要验证的Activity上添加extras

    @Route(path = "/matthew/activity",extras= Const.MUST_LOGIN)
    public class TestArouteActivity extends AppCompatActivity {...}
    

    2、实现IInterceptor接口,定义一个拦截器

    @Interceptor(priority = 8, name = "TestInterceptor")
    public class TestInterceptor implements IInterceptor {
        @Override
        public void process(Postcard postcard, InterceptorCallback callback) {
    // callback.onInterrupt(new RuntimeException("我觉得有点异常"));      // 觉得有问题,中断路由流程
    
        // 以上两种至少需要调用其中一种,否则不会继续路由
        }
            if (postcard.getExtra() == Const.MUST_LOGIN) {
                ARouter.getInstance().build("/base/loginactivity").navigation();
                callback.onInterrupt(new Throwable("需要登陆"));
            } else {
                callback.onContinue(postcard);
            }
        }
    
        @Override
        public void init(Context context) {
    // 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次
            KLog.i("TestInterceptor初始化");
        }
    }
    

    可以定义多个拦截器,设置不同的优先级

    降级策略

    不明所以吧!让我举个🌰
    在A页面跳转到B页面,但是由于个别小伙伴的失误B页面被删除处着路径设置错误了,反正就是无法正确跳转B页面了。ARouter的降级策略可以应对这个场景。
    1、定义页面丢失时展示的默认页面

    @Route(path = "/base/emptyactivity")
    public class EmptyActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_empty);
        }
    }
    

    2、定义全局降级策略

    @Route(path = "/base/DegradeService")
    public class DegradeServiceImpl implements DegradeService {
    
        @Override
        public void onLost(Context context, Postcard postcard) {
            ARouter.getInstance().build("/base/emptyactivity").navigation();
        }
    
        @Override
        public void init(Context context) {
    
        }
    }
    

    以下部分偷懒直接引用官例

    通过依赖注入解耦:服务管理

    暴露服务

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

    发现服务

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

    预处理服务

    // 实现 PretreatmentService 接口,并加上一个Path内容任意的注解即可
    @Route(path = "/xxx/xxx")
    public class PretreatmentServiceImpl implements PretreatmentService {
        @Override
        public boolean onPretreatment(Context context, Postcard postcard) {
            // 跳转前预处理,如果需要自行处理跳转,该方法返回 false 即可
        }
    
        @Override
        public void init(Context context) {
    
        }
    }
    

    相关文章

      网友评论

          本文标题:Android路由框架-ARouter 使用

          本文链接:https://www.haomeiwen.com/subject/dwifuqtx.html