Android推送全家桶SDK设计与实践

达芬奇密码2018-07-23 09:55
背景

随着业务的发展壮大,push的到达率也是对日活不可忽略的因素,另外部门的产品线越来越多,push服务组件化可以提升各个产品线的开发效率。首先push服务组件化的目的是解耦业务和服务代码,便于无缝升级和扩展服务,于是会想到去做一个推送全家桶SDK,把各种小米、友盟、杭研等第三方推送等都放到碗里来。在设计阶段,网上虽然有一些类似push sdk的参考方案,但是真正应用到产品业务当中,却有很多设计上的不足,代码本身都是容易的,写这篇文章的目的是更多探讨如何系统性设计一个推送组件,以及如何真正满足业务发展实践。

方案

个人认为,推送组件对Android应用来说,不仅仅设计一套通用接口让推送服务跑起来那么简单。第三方推送平台的碎片化一定会带来很多问题,比如在调研时发现有以下主要问题
  • 小米支持UserAccount和别名,友盟只支持别名;
  • 小米UserAccount可在多点有效,友盟别名只是单点有效;
  • 小米通知栏方式虽然强大,但是客户端无法对业务消息进行选择性关闭
  • 杭研推送为了安全性,注册和绑定流程相对复杂,如何兼容到组件里来,并优化流程
  • 如何实现推送到达和点击的统计
  • 推送平台数据格式不同,客户端在不用if-else判断具体平台的情况下,如何做到数据兼容
  • 推送平台往往有透传和通知栏模式,服务端切换推送方式对push sdk有何影响
  • 服务端发一条私信和发一条广播,对push sdk来说有何区别
  • 如何支持组播
  • 是否去重
  • 客户端如何协助优化服务端的推送服务
以上这些问题,都是上线后必须会面对的,有的和产品需求相关,有的和运营相关,比如他们最关注的可能是统计。要真正满足业务发展需求,让方案保持弹性,客户端和服务端之间需要共同遵守一套协议,这也是推送全家桶sdk很重要的一部分。否则上线后可能会看到用户反馈如下:“消息重复推送了”、“推送关不掉”、“私信收不到”......
另外,push服务的模块化,不仅仅是集成第三方push种类,还有更细分的组合,对客户端来说,是让服务端在协议的约束下选择透传和通知栏,才能保证到达率的同时不会发生消息重复。

接口协议要点

  1. 全家桶sdk的1.0上线版,先支持了以下4种推送方式:小米通知栏、小米透传、友盟透传、杭研。最高的到达率一定是在每个厂商手机上开厂商推送,故协议约定了App启动时上报用户设备号,如果app已经实现了匿名登录,接口已经有了很容易实现。这是为了协助服务端优化,发送私信时,先查询用户登录设备,如只登录小米手机,只需发送小米通知栏,如果登录非小米设备或者多点登录,则到各平台发送透传方式。上报接口需考虑更多的参数,如设备名称,如厂商系统版本号,应对将来可能某个厂商系统版本通知栏方式失效,也可能小米手机装了华为系统。
  2. 收到消息后的回执。统计是产品最重要的一部分,尤其总结数据时可能会对第三方平台的数据产生疑问,回执可以让服务端预先记录,万一哪天需要做一个数据后台呢。另外,回执的目的是告诉服务端是否收到了“某些重要的信息”,私信必须是,某些重要的域内广播也可以是,给服务端一个重试的选择。这里需考虑上报类型,按行为区分有到达、点击,也可按业务区分。
  3. 上报app推送开关。如小米只提供了一个总开关,若服务端发私信走小米通知栏方式,需先查询该用户的私信通知开关状态,如果app广播也可以开关,那要维护一个组播,如小米可以打tag。另外这个开关本地也要知道,当服务端发透传,需由客户端来判定。
  4. 通用数据格式。如果用在新产品则非常简单,一般把业务数据都塞在一个content json里,但杭研推送除了content json,在外部定义topic、msgId等字段,一般产品都会使用到这几个外部字段。至于要求服务端是否统一数据格式可以协调,sdk解析字段时最好都考虑到各种可能的情况,另外还需要给一个不重复业务数据pushId。
SDK实现

  • 去重不属于协议的部分,sdk的职责要保证服务端多个渠道透传过来的数据不重复通知给接入app。具体去重的方式目前sdk支持3种:根据pushId去重,根据pushId加标题去重,自定义字段去重。sdk保存去重队列在非易失存储,另外也要设置一个合适的最大消息数存储上限,建议根据接入的第三方推送平台数目来设置。
   

  
   
  • 对于消息回执,sdk需考虑可能网络失败,要有缓存和重传机制,支持批量上报,具体触发的时机可以是app启动的时候,也可以是进入某个页面。这块需用数据库来记录,对缓存消息设置一个过期时间。
  • 之前各个产品杭研推送的绑定流程并不统一,sdk在做这块时优化了绑定流程。由于客户端需要从自家服务器上拉取绑定参数、签名、过期时间,消耗网络资源,启动时sdk判断参数未过期则不重复绑定。
  • 另外,为了了解线上用户可能存在的各种问题,需接入日志组件来记录各种错误状态,这就涉及到不仅要监控推送数据接收,还要对每个推送注册和设置命令都进行监控。由于命令数据量不大,sdk将命令与推送数据放在同一个线程的消息队列里接收,并通知上层。
最后,考虑了以上这些,一个有良好通用性和扩展性的推送全家桶sdk就诞生了,Android studio下将sdk的模块化非常容易。在协议的约束下,今后扩展其它厂商推送也相对可控。

本文来自网易实践者社区,经作者范晨灿授权发布。