[Alibaba-ARouter]浅谈简单好用的Android页面路由框架

开发一款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。

一、功能介绍

  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服务的形式,通过接口互相调用)

四、基础功能

添加依赖和配置

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'
  ...
 }

添加注解

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

初始化SDK

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

发起路由操作

 // 1. 应用内简单的跳转(通过URL跳转在'中阶使用'中)
 ARouter.getInstance().build("/test/1").navigation();

 // 2. 跳转并携带参数
 ARouter.getInstance().build("/test/1")
    .withLong("key1", 666L)
    .withString("key3", "888")
    .navigation();

添加混淆规则(如果使用了Proguard)

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

五、进阶用法

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

使用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
  }
 }

开启ARouter参数自动注入(实验性功能,不建议使用,正在开发保护策略)

 // 首先在Application中重写 attachBaseContext方法,并加入ARouter.attachBaseContext();
 @Override
 protected void attachBaseContext(Context base) {
 super.attachBaseContext(base);

 ARouter.attachBaseContext();
 }

 // 设置ARouter的时候,开启自动注入
 ARouter.enableAutoInject();

 // 至此,Activity中的属性,将会由ARouter自动注入,无需 getIntent().getStringExtra("xxx")等等

声明拦截器(拦截跳转过程,面向切面搞事情)

 // 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查

 // 拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行
 @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) {

  }
 }

处理跳转结果

 // 通过两个参数的navigation方法,可以获取单次跳转的结果
 ARouter.getInstance().build("/test/1").navigation(this, new NavigationCallback() {
  @Override
  public void onFound(Postcard postcard) {
   ...
  }

  @Override
  public void onLost(Postcard postcard) {
   ...
  }
 });

自定义全局降级策略

  // 实现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) {

  }
  }

为目标页面声明更多信息

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

使用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) {

  }
 }

使用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不保证能获取到你想要的实现

使用ARouter管理服务(三) 管理依赖

可以通过ARouter service包装您的业务逻辑或者sdk,在service的init方法中初始化您的sdk,不同的sdk使用ARouter的service进行调用,每一个service在第一次使用的时候会被初始化,即调用init方法。

这样就可以告别各种乱七八糟的依赖关系的梳理,只要能调用到这个service,那么这个service中所包含的sdk等就已经被初始化过了,完全不需要关心各个sdk的初始化顺序。

六、更多功能

初始化中的其他设置

 ARouter.openLog(); // 开启日志
 ARouter.printStackTrace(); // 打印日志的时候打印线程堆栈

详细的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链接

最新版本

  1. arouter-annotation : 1.0.0
  2. arouter-compiler : 1.0.1
  3. arouter-api : 1.0.2

Gradle依赖

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 浅谈android组件化之ARouter简单使用

    ARouter是阿里巴巴开源出来的一款android路由框架,github地址为 : https://github.com/alibaba/ARouter 至于ARouter的诸多好处我就不介绍了,这里主要讲解在项目组件化下,ARouter的一些简单使用 先贴上工程目录: 工程一共分为4个模块,基础组件app.基础服务(包涵路由服务)basecommonlibrary模块.业务模块libraryone.业务模块librarytwo; 在4个模块的gradle文件当中加入如下代码: android

  • [Alibaba-ARouter]浅谈简单好用的Android页面路由框架

    开发一款App,总会遇到各种各样的需求和业务,这时候选择一个简单好用的轮子,就可以事半功倍 前言 Intent intent = new Intent(mContext, XxxActivity.class); intent.putExtra("key","value"); startActivity(intent); Intent intent = new Intent(mContext, XxxActivity.class); intent.putExtra(&

  • 浅谈VUE项目打包后运行页面一片白问题

    目录 1.说明 2.问题说明 3.解决 3.1.index.js 3.2.utils.js 3.3.webpack.prod.conf.js 4.总结 1.说明 我们用VUE搭建一个脚手架后,在IDEA等工具中开发时,启动都没有什么问题,但是项目开发完成之后,可能需要部署上线,所以需要进行打包操作了,一般都是用下面命令进行打包: npm run build 打包过程一般没有什么问题,然后就会在工程目录下生成一个[dist]文件夹,里面就是我们打包好的文件,把这些文件部署到Nginx中或者Tomc

  • Github简单易用的 Android ViewModel Retrofit框架

    目录 RequestViewModel Gradle 使用 1.retrofit接口的声明 2.retrofit配置 3.在Activity或Fragment中创建请求对象 4.继承RequestLiveData,处理返回数据 5.使用RequestViewModel和RequestLiveData请求数据 6.设置请求参数,主动请求数据 7.观察RequestLvieData数据变化 8.日志打印 RequestViewModel 优势: 快捷.方便地使用ViewModel .LiveData

  • 浅谈简单使用CentOS7防火墙及开放端口

    概述(官方有更为详细的说明哦) Firewalld提供动态管理的防火墙,支持网络/防火墙区域,用于定义网络连接或接口的信任级别.它支持IPv4,IPv6防火墙设置,以太网桥和IP集.运行时和永久配置选项分开.它还为服务或应用程序提供了直接添加防火墙规则的接口. 既然是简单使用, 那必须教会你怎么查看防火墙状态, 以及防火墙的关闭和开启之类的, 我们都知道那既然防火墙都开启了, 那么它必定是有一些端口的限制, 不能说你想通过哪些端口就通过哪些端口, 就能访问到我们的主机(也就是服务器了), 换句说

  • 浅谈Vue CLI 3结合Lerna进行UI框架设计

    当前大部分UI框架设计的Webpack配置都相对复杂,例如 Element. Ant Design Vue和Muse-UI等Vue组件库.例如Element,为了实现业务层面的两种引入形式( 完整引入 和 按需引入 ),以及抛出一些可供业务层面通用的 utils . i18n 等,Webpack配置变得非常复杂.为了简化UI框架的设计难度,这里介绍一种简单的UI框架设计,在此之前先简单介绍一下 Element 的构建流程,以便对比新的UI框架设计. 一般组件库的设计者将引入形式设计成 完整引入

  • 浅谈千兆交换路由器的虚拟路由集群技术

    一.引言: 当前,IP已经成为大部分骨干网络产品的路由协议.在部分网络环境,用户对网络的要求是很高的,任何停工和储运损耗都会对用户造成严重影响.例如: 1.Internet服务提供商提供Web主机设备,为了使得用户的Web服务器对公众总是有效的,必须保证用户99.9999%的正常运行时间. 2.过程控制应用必须能够适时访问它的控制的系统,否则可能会发生结果损失严重的控制: 3.有时,运行在IP主机上的应用会超时,如果业务运行对网络应用要求较高,这种超时会带来很坏影响. 越来越多的IP主机使用DH

  • 浅谈如何使用webpack构建多页面应用

    前言 之前使用 vue2.x + webpack3.x 撸了一个 vue 单页脚手架 vue 版 spa 脚手架 有兴趣的同学可以看下,内附详细注释,适合刚学习 webpack 的童鞋. react 版 spa 脚手架 但在一些场景下,单页应用显然无法满足我们的需求,于是便有了 mulXc-cli 好了,废话不多说,进入正题!!!! 文件结构  ├── build 构建服务和webpack配置 ├──── build.js 构建全量压缩包 (打包项目) ├──── setting.js 多页面入

  • 浅谈VUE uni-app 条件编码和页面布局

    目录 条件编译 页面布局 总结 条件编译 条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台. 写法:以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾. #ifdef:if defined 仅在某平台存在 #ifndef:if not defined 除了某平台均存在 %PLATFORM%:平台名称 页面布局 scss 规范,rpx 在iphone6/7/8 中 宽度为750,2rpx = 1px <style

  • 浅谈python字符串方法的简单使用

    学习python字符串方法的使用,对书中列举的每种方法都做一个试用,将结果记录,方便以后查询. (1) s.capitalize() ;功能:返回字符串的的副本,并将首字母大写.使用如下: >>> s = 'wwwwww' >>> scap = s.capitalize() >>> scap 'Wwwwww' (2)s.center(width,char); 功能:返回将s字符串放在中间的一个长度为width的字符串,默认其他部分用空格填充,否则使用c

随机推荐