WMRouter:美团外卖Android开源路由框架(1)


WMRouter主要提供URI分发、ServiceLoader两大功能。

URI分发功能可用于多工程之间的页面跳转、动态下发URI链接的跳转等场景,特点如下:

  1. 支持多scheme、host、path
  2. 支持URI正则匹配
  3. 页面配置支持Java代码动态注册,或注解配置自动注册
  4. 支持配置全局和局部拦截器,可在跳转前执行同步/异步操作,例如定位、登录等
  5. 支持单次跳转特殊操作:Intent设置Extra/Flags、设置跳转动画、自定义StartActivity操作等
  6. 支持页面Exported控制,特定页面不允许外部跳转
  7. 支持配置全局和局部降级策略
  8. 支持配置单次和全局跳转监听
  9. 完全组件化设计,核心组件均可扩展、按需组合,实现灵活强大的功能

基于SPI (Service Provider Interfaces) 的设计思想,WMRouter提供了ServiceLoader模块,类似Java中的java.util.ServiceLoader,但功能更加完善。通过ServiceLoader可以在一个App的多个模块之间通过接口调用代码,实现模块解耦,便于实现组件化、模块间通信,以及和依赖注入类似的功能等。其特点如下:

  1. 使用注解自动配置
  2. 支持获取接口的所有实现,或根据Key获取特定实现
  3. 支持获取Class或获取实例
  4. 支持无参构造、Context构造,或自定义Factory、Provider构造
  5. 支持单例管理
  6. 支持方法调用

其他特性:

  1. 优化的Gradle插件,对编译耗时影响很小
  2. 编译期和运行时配置检查,避免配置冲突和错误
  3. 编译期自动添加Proguard混淆规则,免去手动配置的繁琐
  4. 完善的调试功能,帮助及时发现问题

适用场景

WMRouter适用但不限于以下场景:

  1. Native+H5混合开发模式,需要进行页面之间的互相跳转,或进行灵活的运营跳转链接下发。可以利用WMRouter统一页面跳转逻辑,根据不同的协议(HTTP、HTTPS、用于Native页面的自定义协议)跳转对应页面,且在跳转过程中可以使用UriInterceptor对跳转链接进行修改,例如跳转H5页面时在URL中加参数。

  2. 统一管理来自App外部的URI跳转。来自App外部的URI跳转,如果使用Android原生的Manifest配置,会直接启动匹配的Activity,而很多时候希望先正常启动App打开首页,完成常规初始化流程(例如登录、定位等)后再跳转目标页面。此时可以使用统一的Activity接收所有外部URI跳转,到首页时再用WMRouter启动目标页面。

  3. 页面跳转有复杂判断逻辑的场景。例如多个页面都需要先登录、先定位后才允许打开,如果使用常规方案,这些页面都需要处理相同的业务逻辑;而利用WMRouter,只需要开发好UriInterceptor并配置到各个页面即可。

  4. 多工程、组件化、平台化开发。多工程开发要求各个工程之间能互相通信,也可能遇到和外卖App类似的代码复用、依赖注入、编译等问题,这些问题都可以利用WMRouter的URI分发和ServiceLoader模块解决。

  5. 对业务埋点需求较强的场景。页面跳转作为最常见的业务逻辑之一,常常需要埋点。给每个页面配置好URI,使用WMRouter统一进行页面跳转,并在全局的OnCompleteListener中埋点即可。

  6. 对App可用性要求较高的场景。一方面,可以对页面跳转失败进行埋点监控上报,及时发现线上问题;另一方面,页面跳转时可以执行判断逻辑,发现异常(例如服务端异常、客户端崩溃等)则自动打开降级后的页面,保证关键功能的正常工作,或给用户友好的提示。

  7. 页面A/B测试、动态配置等场景。在WMRouter提供的接口基础上进行少量开发配置,就可以实现:根据下发的A/B测试策略跳转不同的页面实现;根据不同的需要动态下发一组路由表,相同的URI跳转到不同的一组页面(实现方面可以自定义UriInterceptor,对匹配的URI返回301的UriResult使跳转重定向)。

基本概念解释

下面开始介绍WMRouter的发展背景和过程。为了方便后文的理解,我们先简单了解和回顾几个基本概念。

路由

根据维基百科的解释,路由(routing)可以理解成在互联的网络通过特定的协议把信息从源地址传输到目的地址的过程。一个典型的例子就是在互联网中,路由器可以根据IP协议将数据发送到特定的计算机。

URI

URI(Uniform Resource Identifier,统一资源标识符)是一个用于标识某一互联网资源名称的字符串。URI的组成如下图所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一些常见的URI举例如下,包括平时经常用到的网址、IP地址、FTP地址、文件、打电话、发邮件的协议等。

在Android中也提供了android.net.Uri工具类用于处理URI,Android中URI常用的几个部分主要是scheme、host、path和query。

Android中的Intent跳转

在Android中的Intent跳转,分为显式跳转和隐式跳转两种。

显式跳转即指定ComponentName(类名)的Intent跳转,一般通过Bundle传参,示例代码如下:

Intent intent = new Intent(context, TestActivity.class);
intent.putExtra(“param”, “value”)
startActivity(intent);

隐式跳转即不指定ComponentName的Intent跳转,通过IntentFilter找到匹配的组件,IntentFilter支持action、category和data的匹配,其中data就是URI。例如下面的代码,会启动系统默认的浏览器打开网页:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(“http://www.meituan.com”))
startActivity(intent);

Activity通过Manifest配置IntentFilter,例如下面的配置可以匹配所有形如demo_scheme://demo_host/***的URI。

URI跳转

在美团外卖C端早期开发过程中,产品希望通过后台下发URI控制客户端跳转指定页面,从而实现灵活的运营配置。外卖App采用了Native+H5的混合开发模式,Native页面定义了专用的URI,而H5页面则使用HTTP/HTTPS链接在专门的WebView容器中加载,两种链接的跳转逻辑不同,实现起来比较繁琐。

Native页面的URI跳转最开始使用的是Android原生的IntentFilter,通过隐式跳转启动,但是这种方式存在灵活性差、功能缺失、Bug多等问题。例如:

  1. 从外部(浏览器、微信等)跳转外卖的URI时,系统会直接打开相应的Activity,而没有经过欢迎页的正常启动流程,一些代码逻辑可能没有执行,例如定位逻辑。

  2. 有很多页面在打开前需要确保用户先登录或先定位,每个页面都写一遍判断登录、定位的逻辑非常麻烦,提高了开发维护成本。

  3. 运营人员可能会配错URI,页面跳转失败,有些跳转的地方没有做try-catch处理,会产生Crash;有些地方虽然加了try-catch,但跳转失败后没有任何响应,用户体验差;跳转失败没有监控,不能及时发现和解决线上业务异常。

为了解决上述问题,我们希望有一个Android的URI分发组件,可以根据URI中不同的scheme、host、path,进行不同的处理,同时能够在页面跳转过程中进行更灵活的干预。调研发现,现有的一些Android路由组件主要都是在解决多工程之间解耦的问题,而URI往往只支持通过path分发,页面跳转的配置也不够灵活,难以满足实际需要。于是我们决定自行设计实现。

核心设计思路

下图展示了WMRouter中URI分发机制的核心设计思路。借鉴网络请求的机制,WMRouter中的每次URI跳转视为发起一个UriRequest;URI跳转请求被WMRouter逐层分发给一系列的UriHandler进行处理;每个UriHandler处理之前可以被UriInterceptor拦截,并插入一些特殊操作。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

页面跳转来源

常见的页面跳转来源如下:

  1. 来自App内部Native页面的跳转
  2. 来自App内Web容器的跳转,即H5页面发起的跳转
  3. 从App外通过URI唤起App的跳转,例如来自浏览器、微信等
  4. 从通知中心Push唤起App的跳转

对于来自App内部和Web容器的跳转,我们把所有跳转代码统一改成调用WMRouter处理,而来自外部和Push通知的跳转则全部使用一个独立的中转Activity接收,再调用WMRouter处理。

UriRequest

UriRequest中包含Context、URI和Fields,其中Fields为HashMap<String, Object>,可以通过Key存放任意数据。简单起见,UriRequest类同时承担了Response的功能,跳转请求的结果,也会被保存到Fields中。Fields可以根据需要自定义,其中一些常见字段举例如下:

  • Intent的Extra参数,Bundle类型
  • 用于startActivityForResult的RequestCode,int类型
  • 用于overridePendingTransition方法的页面切换动画资源,int[]类型
  • 本次跳转结果的监听器,OnCompleteListener类型

每次URI跳转请求会有一个ResultCode(类似HTTP请求的ResponseCode),表示跳转结果,也存放在Fields中。常见Code如下,用户也可以自定义Code:

  • 200:跳转成功
  • 301:重定向到其他URI,会再次跳转
  • 400:请求错误,通常是Context或URI为空
  • 403:禁止跳转,例如跳转白名单以外的HTTP链接、Activity的exported为false等
  • 404:找不到目标(Activity或UriHandler)
  • 500:发生错误

总结来说,UriRequest用于实现一次URI跳转中所有组件之间的通信功能。

UriHandler

UriHandler用于处理URI跳转请求,可以嵌套从而逐层分发和处理请求。UriHandler是异步结构,接收到UriRequest后处理(例如跳转Activity等),如果处理完成,则调用callback.onComplete()并传入ResultCode;如果没有处理,则调用callback.onNext()继续分发。下面的示例代码展示了一个只处理HTTP链接的UriHandler的实现:

public interface UriCallback {

/**

  • 处理完成,继续后续流程。
    */
    void onNext();

/**

  • 处理完成,终止分发流程。
  • @param resultCode 结果
    */
    void onComplete(int resultCode);
    }

public class DemoUriHandler extends UriHandler {
public void handle(@NonNull final UriRequest request, @NonNull final UriCallback callback) {
Uri uri = request.getUri();
// 处理HTTP链接
if (“http”.equalsIgnoreCase(uri.getScheme())) {
try {
// 调用系统浏览器
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(uri);
request.getContext().startActivity(intent);
// 跳转成功
callback.onComplete(UriResult.CODE_SUCCESS);
} catch (Exception e) {
// 跳转失败
callback.onComplete(UriResult.CODE_ERROR);
}
} else {
// 非HTTP链接不处理,继续分发
callback.onNext();
}
}
// …
}

UriInterceptor

UriInterceptor为拦截器,不做最终的URI跳转操作,但可以在最终的跳转前进行各种同步/异步操作,常见操作举例如下:

  • URI跳转拦截,禁止特定的URI跳转,直接返回403(例如禁止跳转非meituan域名的HTTP链接)
  • URI参数修改(例如在HTTP链接末尾添加query参数)
  • 各种中间处理(例如打开登录页登录、获取定位、发网络请求)
  • ……

每个UriHandler都可以添加若干UriInterceptor。在UriHandler基类中,handle()方法先调用抽象方法shouldHandle()判断是否要处理UriRequest,如果需要处理,则逐个执行Interceptor,最后再调用handleInternal()方法进行跳转操作。

public abstract class UriHandler {

// ChainedInterceptor将多个UriInterceptor合并成一个
protected ChainedInterceptor mInterceptor;

public UriHandler addInterceptor(@NonNull UriInterceptor interceptor) {
if (interceptor != null) {
if (mInterceptor == null) {
mInterceptor = new ChainedInterceptor();
}
mInterceptor.addInterceptor(interceptor);
}
return this;
}

public void handle(@NonNull final UriRequest request, @NonNull final UriCallback callback) {
if (shouldHandle(request)) {
if (mInterceptor != null) {
mInterceptor.intercept(request, new UriCallback() {
@Override
public void onNext() {
handleInternal(request, callback);
}

@Override
public void onComplete(int result) {
callback.onComplete(result);
}
});
} else {
handleInternal(request, callback);
}
} else {
callback.onNext();
}
}

/**

  • 是否要处理给定的uri。在Interceptor之前调用。
    */
    protected abstract boolean shouldHandle(@NonNull UriRequest request);

/**

  • 处理uri。在Interceptor之后调用。
    */
    protected abstract void handleInternal(@NonNull UriRequest request, @NonNull UriCallback callback);
    }
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

结尾

最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。

首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)

泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter

接下来是资料清单:(敲黑板!!!


1.数据结构和算法

2.设计模式

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

480)]

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

[外链图片转存中…(img-eutQR9GM-1712677138480)]

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

[外链图片转存中…(img-1rNN1EZf-1712677138481)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值