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)

写在最后

由于本文罗列的知识点是根据我自身总结出来的,并且由于本人水平有限,无法全部提及,欢迎大神们能补充~

将来我会对上面的知识点一个一个深入学习,也希望有童鞋跟我一起学习,一起进阶。

提升架构认知不是一蹴而就的,它离不开刻意学习和思考。

**这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家,**梳理了多年的架构经验,筹备近1个月最新录制的,相信这份视频能给你带来不一样的启发、收获。

最近还在整理并复习一些Android基础知识点,有问题希望大家够指出,谢谢。

希望读到这的您能转发分享和关注一下我,以后还会更新技术干货,谢谢您的支持!

转发+点赞+关注,第一时间获取最新知识点

Android架构师之路很漫长,一起共勉吧!

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

-1712818513141)]

[外链图片转存中…(img-rUG8TEOJ-1712818513141)]

最近还在整理并复习一些Android基础知识点,有问题希望大家够指出,谢谢。

希望读到这的您能转发分享和关注一下我,以后还会更新技术干货,谢谢您的支持!

转发+点赞+关注,第一时间获取最新知识点

Android架构师之路很漫长,一起共勉吧!

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

  • 27
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
司科仿美团O2O程序源码最新版+wap+微信版亲测完整版是一款基于PHP语言设计的团购网站源码。 司科出品,必属精品!司科公司多年网站建设经验,专业提供商城网站制作,团购网站建设,团购APP制作,商城客户端设计,微信营销系统等服务。 PC端功能介绍: 产品发布 发布以天为形式的商品项目(添加、删除、修改、编辑) 秒杀抢团项目 发布以秒为形式的商品项目 商户展示 一、商家有自己的简单的介绍页面,介绍信息和产品 二、商家可以自己的后台对商品券进行验证消费。 商家专卖店展示系统 一、合作商家可以通过后台发布自己的展示商品和商家新闻,帮助树立商家企业形象,加强与商家的合作。 二、商家可以自己的后台对商品券进行验证消费。 商家上传系统 商家可以自行上传产品,管理员审核,审核通过即可显示,审核不通过商家再修改。 商家结算系统 V1.0 超级商家结算系统,网站自动计算商家提成 超强团购商家店铺 团购商家有独立商家店铺展示,实用性强! 会员级别V1.2 根据会员消费情况等,可以自动升级级别 手机版微信支付 包括PC的扫描支付&微信公众号的微信支付 消费评价+评分功能 独家用户消费评价+评分功能,提升用户体验 城市代理商管理V1.0 每个城市产品可以由独立代理商后台管理 可修改商品时间 可对商品进行修改结束时间,让操作更加方便、人性化。 支持商品退款 提供客户申请商品模式,让商品更加自由,让客户在商品中无后顾之忧,可对商品项目设置两种模式:支持 不支持。 支持快递商品 发布项目的时候,可选择优惠券或快递。 好友邀请 用户通过人人网、开心网、MSN/QQ等社交网络邀请好友注册购买。 邮件订阅 用户用来订阅本站每天商品项目信息,方便对自己感兴趣的商品项目及时下单购买。 短信订阅 用户输入手机号码订阅商品信息,可以收到每日最新商品的短信息。 本单答疑 展示在网站右侧,用户可通过答疑,向网站客服咨询,网站管理员在后台回复后显示内容。 常见问题 展示网站内的文字信息,比如:新手指南,入门教程以及常见问题等。 短信发券 用户购买时输入手机号码,购买成功后,可收到由商品网站统一发出的优惠券帐号和密码。 团券认证 用户消费商品券时,可以在此注销或查询团券有效期。团券注销后不可二次使用。 商家地图展示 通过后台设置商家的具体位置,方便消费型商品的客户查找 项目分类首页展示 首页分类不同的产品,不同楼层展示 多城市切换 商品产品可以分配不同的城市显示 在线客服QQ 通过客服qq,方便与网站浏览者及时沟通。 强大的在线分享 用户通过MSN 人人 网易 开心 豆瓣 百度 腾讯 飞信 新浪微博 QQ空间、等,快速分享给好朋友心怡的商品 网项目。 管理员等级及授权 总管理分配下属管理员,各自管理各自的项目 后台控制分类 自可以后台控制推荐产品,分类展示商品。 邮箱发送邮件 用户可以自己设置邮箱,群发邮件给用户 图片延时加载 采用图片延时加载,极大减少服务器带宽 快递单号批量上传 商家可以建立一个TXT文档,输入订单编号,快递单交给网站运营者,网站运营者在后台上传这个TXT文档, 系统批量给这些订单生成快递单号 每个项目设置自己独立的SEO 每个项目可以在后台设置自己独立的SEO,更利于网站被搜索引擎收录,让您的网站排名靠前。 整站生成静态页面 自动生成伪静态,可以增加网页访问速度及搜索引擎排名,提高网站的质量。 首页及内页广告位 通过后台的操作来发布首页及内页通栏相关的广告图片及图片链接从而及时发布重要信息和促销活动。 公告发布 统一发布公告,也可以按地区来发布相关的促销信息以及网站公告。 手机参与商品 通过手机上网参与商品。 购物车功能 方便用户一次性挑选多个商品进行购买 用户购买选择快递 让用户在购买时自己选择城市和快递公司 到货评论功能 用户对已购买的商品发表评论。增加用户粘性,吸引其他浏览者 虚拟购买人数 项目可以设置虚拟购买人数,刺激用户消费 管理员操作日志 根据需求进行后台管理员的权限分配 数据库的备份 根据需求自行在后台进行数据备份 用户调查参与 发布小调查,直观了解用户产品需求 多件免邮功能 根据购买数量设置是否免去邮费 友情链接 通过后台设置文字链接及图片链接 网站统计 客户根据统计报表可以知道每天用户访问量IP、浏览量pv、地区、ip地址及来自那个网站等,可根据时间生成报表,从而为网络营销提供依据。 WAP端功能介绍: 城市切换 触屏版会根据打开的用户地区进行自动城市更新,弹出提示,是否回到当前城市。 登陆注册 用户可以用QQ或者手机号码登陆触屏版进行购物,减少繁琐的输入选项。 在线搜索 触屏版首页有搜索框,用户可以输入自己喜欢的产品关键字进行查找购

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值