美文网首页Android潜修者AndroidAndroid日记
[Alibaba-ARouter] 简单好用的Android页面

[Alibaba-ARouter] 简单好用的Android页面

作者: WangDeFa | 来源:发表于2017-01-06 16:29 被阅读39023次

    开发一款App,总会遇到各种各样的需求和业务,这时候选择一个简单好用的轮子,就可以事半功倍

    前言

            Intent intent = new Intent(mContext, XxxActivity.class);
            intent.putExtra("key","value");
            startActivity(intent);
            
            Intent intent = new Intent(mContext, XxxActivity.class);
            intent.putExtra("key","value");
            startActivityForResult(intent, 666);
    

    上面一段代码,在Android开发中,最常见也是最常用的功能就是页面的跳转,我们经常需要面对从浏览器或者其他App跳转到自己App中页面的需求,不过就算是简简单单的页面跳转,随着时间的推移,也会遇到一些问题:

    1. 集中式的URL管理:谈到集中式的管理,总是比较蛋疼,多人协同开发的时候,大家都去AndroidManifest.xml中定义各种IntentFilter,使用隐式Intent,最终发现AndroidManifest.xml中充斥着各种Schame,各种Path,需要经常解决Path重叠覆盖、过多的Activity被导出,引发安全风险等问题
    2. 可配置性较差:Manifest限制于xml格式,书写麻烦,配置复杂,可以自定义的东西也较少
    3. 跳转过程中无法插手:直接通过Intent的方式跳转,跳转过程开发者无法干预,一些面向切面的事情难以实施,比方说登录、埋点这种非常通用的逻辑,在每个子页面中判断又很不合理,毕竟activity已经实例化了
    4. 跨模块无法显式依赖:在App小有规模的时候,我们会对App做水平拆分,按照业务拆分成多个子模块,之间完全解耦,通过打包流程控制App功能,这样方便应对大团队多人协作,互相逻辑不干扰,这时候只能依赖隐式Intent跳转,书写麻烦,成功与否难以控制。

    另一个轮子

    为了解决以上问题,我们需要一款能够解耦、简单、功能多、定制性较强、支持拦截逻辑的路由组件:我们选择了Alibaba的ARouter,偷个懒,直接贴ARouter的中文介绍文档:

    Demo gifDemo gif

    一、功能介绍

    1. 支持直接解析URL进行跳转、参数按类型解析到Bundle,支持Java基本类型(*)
    2. 支持应用内的标准页面跳转,API接近Android原生接口
    3. 支持多模块工程中使用,允许分别打包,包结构符合Android包规范即可(*)
    4. 支持跳转过程中插入自定义拦截逻辑,自定义拦截顺序(*)
    5. 支持服务托管,通过ByName,ByType两种方式获取服务实例,方便面向接口开发与跨模块调用解耦(*)
    6. 映射关系按组分类、多级管理,按需初始化,减少内存占用提高查询效率(*)
    7. 支持用户指定全局降级策略
    8. 支持获取单次跳转结果
    9. 丰富的API和可定制性
    10. 被ARouter管理的页面、拦截器、服务均无需主动注册到ARouter,被动发现
    11. 支持Android N推出的Jack编译链

    二、不支持的功能

    1. 自定义URL解析规则(考虑支持)
    2. 不能动态加载代码模块和添加路由规则(考虑支持)
    3. 多路径支持(不想支持,貌似是导致各种混乱的起因)
    4. 生成映射关系文档(考虑支持)

    三、典型应用场景

    1. 从外部URL映射到内部页面,以及参数传递与解析
    2. 跨模块页面跳转,模块间解耦
    3. 拦截跳转过程,处理登陆、埋点等逻辑
    4. 跨模块API调用,模块间解耦(注册ARouter服务的形式,通过接口互相调用)

    四、基础功能

    1. 添加依赖和配置

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

       buildscript {
           repositories {
               jcenter()
           }
           dependencies {
               classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
           }
       }
      
       apt {
           arguments {
               moduleName project.getName();
           }
       }
      
       dependencies {
           apt 'com.alibaba:arouter-compiler:x.x.x'
           compile 'com.alibaba:arouter-api:x.x.x'
           ...
       }
      
    2. 添加注解

       // 在支持路由的页面、服务上添加注解(必选)
       // 这是最小化配置,后面有详细配置
       @Route(path = "/test/1")
       public class YourActivity extend Activity {
           ...
       }
      
    3. 初始化SDK

       ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化
      
    4. 发起路由操作
      // 1. 应用内简单的跳转(通过URL跳转在'中阶使用'中)
      ARouter.getInstance().build("/test/1").navigation();

       // 2. 跳转并携带参数
       ARouter.getInstance().build("/test/1")
                   .withLong("key1", 666L)
                   .withString("key3", "888")
                   .navigation();
      
    5. 添加混淆规则(如果使用了Proguard)

       -keep public class com.alibaba.android.arouter.routes.**{*;}
      

    五、进阶用法

    1. 通过URL跳转

       // 新建一个Activity用于监听Schame事件
       // 监听到Schame事件之后直接传递给ARouter即可
       // 也可以做一些自定义玩法,比方说改改URL之类的
       // http://www.example.com/test/1
       public class SchameFilterActivity extends Activity {
           @Override
           protected void onCreate(Bundle savedInstanceState) {
               super.onCreate(savedInstanceState);
      
               // 外面用户点击的URL
               Uri uri = getIntent().getData();
               // 直接传递给ARouter即可
               ARouter.getInstance().build(uri).navigation();
               finish();
           }
       }
      
       // AndroidManifest.xml 中 的参考配置
       <activity android:name=".activity.SchameFilterActivity">
               <!-- 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"/>
               </intent-filter>
      
               <!-- App 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>
       </activity>
      
    2. 使用ARouter协助解析参数类型

       // URL中的参数会默认以String的形式保存在Bundle中
       // 如果希望ARouter协助解析参数(按照不同类型保存进Bundle中)
       // 只需要在需要解析的参数上添加 @Param 注解
       @Route(path = "/test/1")
       public class Test1Activity extends Activity {
           @Param                   // 声明之后,ARouter会从URL中解析对应名字的参数,并按照类型存入Bundle
           public String name;
           @Param
           private int age;
           @Param(name = "girl")   // 可以通过name来映射URL中的不同参数
           private boolean boy;
      
           @Override
           protected void onCreate(Bundle savedInstanceState) {
               super.onCreate(savedInstanceState);
      
               name = getIntent().getStringExtra("name");
               age = getIntent().getIntExtra("age", -1);
               boy = getIntent().getBooleanExtra("girl", false);   // 注意:使用映射之后,要从Girl中获取,而不是boy
           }
       }
      
    3. 开启ARouter参数自动注入(实验性功能,不建议使用,正在开发保护策略)

       // 首先在Application中重写 attachBaseContext方法,并加入ARouter.attachBaseContext();
       @Override
       protected void attachBaseContext(Context base) {
          super.attachBaseContext(base);
      
          ARouter.attachBaseContext();
       }
      
       // 设置ARouter的时候,开启自动注入
       ARouter.enableAutoInject();
      
       // 至此,Activity中的属性,将会由ARouter自动注入,无需 getIntent().getStringExtra("xxx")等等
      
    4. 声明拦截器(拦截跳转过程,面向切面搞事情)

       // 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查
      
       // 拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行
       @Interceptor(priority = 666, name = "测试用拦截器")
       public class TestInterceptor implements IInterceptor {
           /**
            * The operation of this interceptor.
            *
            * @param postcard meta
            * @param callback cb
            */
           @Override
           public void process(Postcard postcard, InterceptorCallback callback) {
               ...
      
               callback.onContinue(postcard);  // 处理完成,交还控制权
               // callback.onInterrupt(new RuntimeException("我觉得有点异常"));      // 觉得有问题,中断路由流程
      
               // 以上两种至少需要调用其中一种,否则会超时跳过
           }
      
           /**
            * Do your init work in this method, it well be call when processor has been load.
            *
            * @param context ctx
            */
           @Override
           public void init(Context context) {
      
           }
       }
      
    5. 处理跳转结果

       // 通过两个参数的navigation方法,可以获取单次跳转的结果
       ARouter.getInstance().build("/test/1").navigation(this, new NavigationCallback() {
           @Override
           public void onFound(Postcard postcard) {
                 ...
           }
      
           @Override
           public void onLost(Postcard postcard) {
               ...
           }
       });
      
    6. 自定义全局降级策略

           // 实现DegradeService接口,并加上一个Path内容任意的注解即可
          @Route(path = "/xxx/xxx") // 必须标明注解
           public class DegradeServiceImpl implements DegradeService {
             /**
              * Router has lost.
              *
              * @param postcard meta
              */
             @Override
             public void onLost(Context context, Postcard postcard) {
                   // do something.
             }
      
             /**
              * Do your init work in this method, it well be call when processor has been load.
              *
              * @param context ctx
              */
             @Override
             public void init(Context context) {
      
             }
           }
      
    7. 为目标页面声明更多信息

       // 我们经常需要在目标页面中配置一些属性,比方说"是否需要登陆"之类的
       // 可以通过 Route 注解中的 extras 属性进行扩展,这个属性是一个 int值,换句话说,单个int有4字节,也就是32位,可以配置32个开关
       // 剩下的可以自行发挥,通过字节操作可以标识32个开关
       @Route(path = "/test/1", extras = Consts.XXXX)
      
    8. 使用ARouter管理服务(一) 暴露服务

       /**
        * 声明接口
        */
       public interface IService extends IProvider {
           String hello(String name);
       }
      
       /**
        * 实现接口
        */
       @Route(path = "/service/1", name = "测试服务")
       public class ServiceImpl implements IService {
      
           @Override
           public String hello(String name) {
               return "hello, " + name;
           }
      
           /**
            * Do your init work in this method, it well be call when processor has been load.
            *
            * @param context ctx
            */
           @Override
           public void init(Context context) {
      
           }
       }
      
    9. 使用ARouter管理服务(二) 发现服务

       1. 可以通过两种API来获取Service,分别是ByName、ByType
       IService service = ARouter.getInstance().navigation(IService.class);    //  ByType
       IService service = (IService) ARouter.getInstance().build("/service/1").navigation(); //  ByName
      
       service.hello("zz");
      
       2. 注意:推荐使用ByName方式获取Service,ByType这种方式写起来比较方便,但如果存在多实现的情况时,SDK不保证能获取到你想要的实现
      
    10. 使用ARouter管理服务(三) 管理依赖

          可以通过ARouter service包装您的业务逻辑或者sdk,在service的init方法中初始化您的sdk,不同的sdk使用ARouter的service进行调用,
      每一个service在第一次使用的时候会被初始化,即调用init方法。
          这样就可以告别各种乱七八糟的依赖关系的梳理,只要能调用到这个service,那么这个service中所包含的sdk等就已经被初始化过了,完全不需要
      关心各个sdk的初始化顺序。
      

    六、更多功能

    1. 初始化中的其他设置

       ARouter.openLog();  // 开启日志
       ARouter.printStackTrace(); // 打印日志的时候打印线程堆栈
      
    2. 详细的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();
      
       // 觉得接口不够多,可以直接拿出Bundle赋值
       ARouter.getInstance()
                   .build("/home/main")
                   .getExtra();
      
       // 使用绿色通道(跳过所有的拦截器)
       ARouter.getInstance().build("/home/main").greenChannal().navigation();
      

    附录

    ARouter Github链接
    Demo apk

    • 最新版本
      arouter-annotation : 1.0.0
      arouter-compiler : 1.0.1
      arouter-api : 1.0.2

    • Gradle依赖

    dependencies {
        apt 'com.alibaba:arouter-compiler:1.0.1'
        compile 'com.alibaba:arouter-api:1.0.2'
    }
    

    相关文章

      网友评论

      • 泡泡之意境:我用的Arouter开发的sdk,,现在接入方接入我的sdk,,混淆的时候,,我的sdk中的有得类找不到了,,,可以用你文章里的混淆规则嘛,对于路由的混淆
      • ffd383475c57:有坑,module里边也要配置正常编译
      • 大爷Martin:请问楼主,我配置了以后,APP这个model内的activity跳转都报There's no router match,但是APP model跳转其他的model内的activity,或者其他model内activity跳转都正常,配置都是一样的,请问是什么问题?@Yaezakura
      • Todo2:实现强大而且完美的组件化路由功能,集成简单稳定.
        https://github.com/wenzhonghu/MyRouter
      • Todo2:实现强大而且完美的组件化路由功能,集成简单稳定.
        https://github.com/wenzhonghu/MyRouter
      • 孟威:多个路由跳同一页面,页面怎么配置
      • 821c2a6c3017:大神,ARouter怎么配置支持多路径
      • 劲草2016:混淆出错怎么办
        找不到arouter,按照文中的方法不行
      • 程序猿tx:请教两个问题:
        1、 按要求混淆,但是打包时报错:Warning:com.alibaba.android.arouter.facade.model.RouteMeta: can't find referenced class javax.lang.model.element.Element

        2、使用ARouter管理服务时, 实现接口的那个类好像不可以是Activity,求教下为啥的,类似这种的
        /**
        * 实现接口
        */
        @Route(path = "/service/1", name = "测试服务")
        public class **Activity implements IService {

        @Override
        public String hello(String name) {
        return "hello, " + name;
        }

        @Override
        public void init(Context context) {

        }
        }
      • 一代天骄只缺小乔:这个东西,学习成本还是有点高的,遇到很多坑
      • 渡边君WM:有ios版本的吗
      • yangyirunning:现在的项目在重构过程中已经要正式要采用这个东西了
      • 饮料管饱:问一下为什么新建了一个library 跳转时就是找不到要跳转的页面啊 该配置的都配置了 别的模块就能跳
      • 6c611656a122:楼主你好,请问一下拦截器判断是否需要登录的具体逻辑怎么编写,有相关的demo吗?请发送s__njay@126.com,万分感激!!!
      • xiaobinZh:原理分析的很透彻。 但是使用有点重,而且 会导致 InstantRun,失败。
        可以参考下 https://github.com/gybin02/RouterKit。 也是用了 APT,但是参考的是 Google
        auto-service 里面的代码,使用APT直接生成 配置文件。
        WangDeFa:简单的看了下你写的 RouterKit,挺好的,说下我的看法,其实用配置文件还是用类来保存路由信息没有什么本质区别,用类的话,可以自动merge,用asset则要写gradle脚本去merge,用户会更困惑,会有更多问题,而且ARouter是支持 InstantRun 的,可以通过 openDebug() 来开启,没有默认开启是有安全的考虑,防止线上加载不安全代码

        如果只用最基本的功能,不算重,ARouter本身是作为 组件化开发框架的,不仅仅是跳转,所以功能会很多

        最重要的是,ARouter本身是开源的,目前为止算是功能比较全面的了, 有好的idea完全可以贡献进来,大家一起维护,我个人是不赞成重复造轮子的
      • 266d90cfc020:简单来说 就是封装了跳转代码, 最主要是通过url跳转 其次是拦截功能
      • 行云流水之灵:大神,ARouter不支持多路径,当一个界面有多个入口,有的使用到拦截器,有的不使用到拦截器,该怎么办?求大神给个思路或方法!
        d727fbbc25f8:ARouter.getInstance().build("").greenChannel().navigation()
        这个方法相当于一个绿色通道不会经过过滤器
        6c7dc214f3b4:每次跳转都会把所有的拦截器校验一遍,你可以在跳转过程中配置一些变量,在通过拦截器时通过变量来判断此次跳转是否需要拦截
      • 72c35595f71b:尝试使用
      • 圈圈猫:诶,这玩意有些手机不兼容,无法自定义scheme。就这点比较坑爹
        WangDeFa:没发现有不兼容的手机,都是标准用法,可以自定义schame
      • openGL小白:想请教一个问题,看文章大部分是跳转路由的讲解和服务的暴露,如果只是想实现解偶后的业务组件之间的同步或异步调用,有则调成功,没引入所调组件就返回失败但能编译(也是为了业务组件间解偶)。这是要按IProvider方式?还是有更好的方法,也就是框架宣传的4跨模块API调用,通过控制反转来做组件解耦
      • ziabo_yu:版本该更新了
        模块 arouter-api arouter-compiler arouter-annotation
        最新版本 1.2.1.1 1.1.2.1 1.0.3
        7d6e7d1e4837::blush: 多谢
      • Lenny_liu:在模块化的时候优势会非常的明显
      • cuieney:请问博主 我这里有三个module 每个module里面的act都有正确@Route(path = "/act/test")这样配置也添加了dependency (api 和 compile 包)但是我在主module里面进行跳转 一直no router match
        WangDeFa:找不到目标是最基础的异常了,可以参考文档中的方法排查,文档中有交流群。
      • c782dd7646d8:我集成的时候说的是找不到路径,然后我直接按demo里面的导他的库,一直包no module name
        WangDeFa:这提示都多明显了,没配置moduleName,文档里面有介绍
      • Poseidon_Wang:我觉得暴露服务必须集成接口就有点污染代码了 既然冲着高内聚 可配置去的 加了这种继承接口以后 我的拆卸还麻烦了 得不偿失啊 关于降级到底是什么意思 不是很懂啊
        WangDeFa:我的理解是这样的:暴露服务,本质上是通过接口把能力暴露出来,接口中只有定义,没有实现,包含了约束,是不会污染代码的,服务包和实现代码要分别打包,就是标准的面向接口编程了,只需要依赖服务包

        降级就是找不到跳转的目标的话,会给你一个回调,让你选择跳转到h5等其他形式,有全局和单次跳转的降级。
      • 吕檀溪:按照这样配置一直无法跳转
        WangDeFa:可以参考github中的issues排查问题,或者是否开启了Instant Run,先尝试关闭试试
      • 611d808d13ea:请问题主,在拦截器中拦截了登录请求后,我跳转到登录页面,登录结果怎么回调给拦截器,并继续路由啊?
        WangDeFa:参考issue中的说明 https://github.com/alibaba/ARouter/issues/9
      • 开发者头条_程序员必装的App:感谢分享!已推荐到《开发者头条》:https://toutiao.io/posts/uqu0v3 欢迎点赞支持!
        欢迎订阅《大圣归来学Android》https://toutiao.io/subjects/12120
      • 碧海鱼龙:大胸弟,demo下下来为啥初始化之后点击没有反应!
        碧海鱼龙:@Vergil03 我看gif里面跳转跳的挺欢的,我这边就吐个司,也不跳!
        WangDeFa:打印的log 。。。
      • 依然范特稀西:这种路由方式适用哪种场景,是 APP内所有的Intent跳转?还是只是用于外部的跳转,还有就是相比直接的Intent跳转,Router 的性能如何呢?
        王浩冉:请问onActivityResult的功能怎么实现啊?
        依然范特稀西:@Vergil03 索噶,有机会试试
        WangDeFa:可以用在所有的场景,包括外部链接映射到内部页面与内部activity之间的跳转,可以通过Router统一起来,方便管理,比较典型是登陆控制
        性能上和原生几乎没有区别,耗时的操作都在编译期,运行期初始化之后没有反射等操作
      • w4lle:赞!你是ARouter作者吗?
        heaven旅行包:看过你之前写的关于tinker的文章
        WangDeFa:用户 :smiley:
      • 灰6太9狼:再有新项目,试一下这个东西
        灰6太9狼:@Vergil03 在看,表示比较喜欢
        WangDeFa:接入成本比较低,现在的app切换上来也不麻烦 :sunny:

      本文标题:[Alibaba-ARouter] 简单好用的Android页面

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