默认
打赏 发表评论 5
想开发IM:买成品怕坑?租第3方怕贵?找开源自已撸?尽量别走弯路了... 找站长给点建议
深入的聊聊Android消息推送这件小事
阅读(103842) | 评论(5 收藏5 淘帖1 3
微信扫一扫关注!

引言


今天来讲讲推送这件小事,事虽小,要做好却不容易。

推送难,难于上青天。


我们在讨论 Android 手机上的推送时,大多数情况是在说集成第三方推送,因为即使是像微信这样的大厂,也需要厂商加到启动白名单里才能保持在线。

iOS 手机使用 APNs(Apple Push Notification service)进行推送(可参见《iOS的推送服务APNs详解:设计思路、技术原理及缺陷等),而 Android 手机,也是有GCM(Google Cloud Messaging)作为 Google 官方的推送支持的,但是在国内需要翻墙才能使用,并且需要手机安装了 Google Service ,条件比较苛刻(参见《移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)》、《求教android消息推送:GCM、XMPP、MQTT三种方案的优劣)。

这样一来,国产手机的推送就成了个问题,也带了机会。

微信由于有国际版,将 GCM 作为辅助公共通道,但仅用于激活微信自己的 Push 通道,并没有通过 GCM 来传递数据,这点也是为了复用心跳的优化策略和数据处理逻辑。(GCM 最新版本叫 FCM(Firebase Cloud Messaging))。

相关资料


[1] 有关推送保活的文章:
Android进程保活详解:一篇文章解决你的所有疑问
Android端消息推送总结:实现原理、心跳保活、遇到的问题等
为何基于TCP协议的移动端IM仍然需要心跳保活机制?
微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)
微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)
移动端IM实践:实现Android版微信的智能心跳机制
移动端IM实践:WhatsApp、Line、微信的心跳策略分析
Android进程永生技术终极揭密:进程被杀底层原理、APP对抗被杀技巧》(* 推荐
>> 更多同类文章 ……

[2] 更多推送技术文章:
iOS的推送服务APNs详解:设计思路、技术原理及缺陷等
Android端消息推送总结:实现原理、心跳保活、遇到的问题等
扫盲贴:认识MQTT通信协议
一个基于MQTT通信协议的完整Android推送Demo
IBM技术经理访谈:MQTT协议的制定历程、发展现状等
求教android消息推送:GCM、XMPP、MQTT三种方案的优劣
移动端实时消息推送技术浅析
扫盲贴:浅谈iOS和Android后台实时消息推送的原理和区别
绝对干货:基于Netty实现海量接入的推送服务技术要点
移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)
为何微信、QQ这样的IM工具不使用GCM服务推送消息?
极光推送系统大规模高并发架构的技术实践分享
从HTTP到MQTT:一个基于位置服务的APP数据通信实践概述
魅族2500万长连接的实时消息推送架构的技术实践分享
专访魅族架构师:海量长连接的实时消息推送系统的心得体会
>> 更多同类文章 ……

推送的实现方式


总结一下几种推送实现方式,其中有些我们只要了解即可,因为属于历史解决方案,现在已经被废弃掉了。

1轮询


客户端定期询问服务器有没有新的消息,这种方式最大的缺点就是性能和实时性的矛盾,轮询时间过长和过短都不好。

2短信


这种方式还没出生就不被允许了,首先运营商不会配合,其次拦截手机短信本身就是一个高危权限,大多数用户不会买单。

3长连


目前最通用的方案,客户端与服务端建立 TCP 长连,定时发送心跳包保活,有新消息时服务端通过该长连通道进行推送。

这里再简单说明一下长连和心跳包。长连接就是建立连接之后,双方互相发送数据,,发完了也不主动断开连接。

但是在某些情况下长连会断开,问题就在断开这件事上,而且这件事必须是由客户端知道,因为客户端是可以重连服务器的,服务器却没法再联系上客户端。这样才确定了心跳包必须由客户端发给服务器。所以心跳包的作用就是告诉服务端客户端还活着,如果服务端挂了,客户端能知道,所以保活说的是保持两边都活着。

上面说的某些情况中, NAT 超时算是一个典型的例子。因为 IPv4 的 IP 量有限,运营商分配给手机终端的 IP 是运营商内网的 IP,手机要连接 Internet ,就需要通过运营商的网关做一个网络地址转换(Network Address Translation,NAT)。简单的说运营商的网关需要维护一个外网 IP、端口到内网 IP、端口的对应关系,以确保内网的手机可以跟 Internet 的服务器通讯。大部分移动无线网络运营商都在链路一段时间没有数据通讯时,会淘汰 NAT 表中的对应项,造成链路中断。所以长连接心跳间隔必须要小于 NAT 超时时间(aging-time),如果超过老化时间不做心跳, TCP 长连接链路就会中断,服务器就无法发消息给客户端,只能等到客户端下次心跳失败后,重建连接才能取到消息。

有了长连,即使在休眠模式下,有推送消息过来,也能唤醒 Android 系统,这是由系统机制决定的,我们这里只要知道结论就好。

推送的指标


在接入推送服务时有几个核心指标需要考量。

1在线率


在线率 = 在线用户数 / 总用户数。


推送服务后台保持在线的方法:

  • Push 进程常驻后台,需要用户手动让应用常驻;
  • 共享连接通道的方式,比如极光或者个推,通过共享连接,当应用有推送到达时,唤起该应用。

显然,后者在体验上更加接近 GCM 。

2到达率


到达率 = 实际到达数 / 目标用户数


在线数 -> 目标用户数 -> 成功下发数,如果后端的计算或调用出现问题这两个数据就会不准确;

在线数 -> 实际到达数 -> 展示数,数据收到后,是否展示要看用户有没有打开该应用的允许通知的开关,可以通过如下方法判断:
notificationManagerCompat.areNotificationsEnabled();

3耗电量


耗电量受到很多方面的影响,如果收到推送比较多,打开应用比较频繁,耗电量自然也会上去不少,但这个用户是可以接受的。

以下几个耗电量的因素用户是比较反感的:

  • 应用间互相唤醒产生的耗电,因为这个耗电是别的应用的,用户本来没有意图要去打开;
  • 错误重试造成的耗电,重试策略的优化包括重试时间的累加和重置。

推送选型


上面提到,我们这里聊的推送,是第三方推送,那有开发者要问了,为什么不自己做推送?

自己做不是不行,但需要考虑几个问题:

  • 开发成本问题, ROI 是否可以接受;
  • 如果不加入白名单,那么一旦应用被彻底杀掉,是没人给你吟唱复活魔法(互相唤起)的。

第三方推送主要有厂商推送和非厂商推送:

  • 华为、小米、魅族推送;
  • 个推、极光、友盟;
  • 阿里、腾讯、百度。

其中,选型的几个因素:

  • 厂商推送通知是否系统通道(所有厂商支持);
  • 厂商推送透传是否系统通道(仅魅族);
  • 非厂商推送的市场占有率(影响共享连接互相唤起的概率)。

如何在 Android 手机中查看某个应用使用的是什么推送?
adb shell dumpsys activity services | grep igexin
深入的聊聊Android消息推送这件小事_1.png

可以看到,这几个应用都在使用个推:
大众点评、宝宝树、饿了么、滴滴、简书、领英、 WPS Office 、格瓦拉。

推送接入


1如何解耦


由于各个推送 sdk 接口定义不同,为了减少耦合,我们可以采用多 module 的形式进行接入:

  • app
  • jikepush
    PushServiceImpl。
  • push_厂商1
    PushPlatformImpl。
  • push_厂商2
    PushPlatformImpl。
  • push_非厂商
    PushPlatformImpl。
  • jikecore
    PushService;
    PushPlatform。

在 app 层通过注册的方式添加需要的 PushPlatform ,之后通过 PushService 接口来进行调用,具体的启动和切换的实现放在 PushServiceImpl 。其实大部分推送平台的接口标准都差不多,无非是命名上有差异,所以我们用 PushPlatform 这个接口来屏蔽这种差异。

对于推送启动和切换的操作,放在 PushService 中。当启动某一个推送服务的时候,就关掉其他的推送服务,后台始终只保持一个推送服务。

2registeration id


通常我们启动一个推送服务后,会收到一个 registeration id(以下简称 reg id),用这个 reg id 和我们自己的服务器进行绑定。

这里有个需要注意的地方,绑定的操作我们需要调用2次:

  • after PushService.start() 因为已经接收到 reg id 后有些推送不再触发 receive reg id;
  • after receive reg id 首次启动推送是异步收到 reg id 的。

收到 reg id 之后,推送 sdk 会自己保存下来以便下次使用,但有时候我们使用 sdk 的方法获取的时候无法获取到,究其原因是 reg id 的保存不是我们自己做的,因此, reg id 这么重要的东西我们自己也要存。

3推送需要的系统权限


推送 sdk 需要获取手机的 imei 号作为合成 reg id 的必要元素,需要以下两个权限:

  • Manifest.permission.READ_PHONE_STATE
  • Manifest.permission.WRITE_EXTERNAL_STORAGE

启动推送服务的时候需要判断权限。

4推送的切换策略


建议选择手机 rom 而非手机型号作为切换条件,这样可以解决部分用户刷机的问题,比如 Nexus 手机刷了个 MIUI 的情况。获取到 rom 信息后,有3种推送切换策略。

策略一:
客户端根据 rom 信息自动选择使用哪个推送。
优点:无后端工作量,不需要切换;
缺点:一旦某个推送挂了一天,无法临时切换到其他推送。

策略二:
客户端上报 rom 信息,后端选择使用哪个推送。
优点:灵活切换推送;
缺点:切换推送重新绑定 reg id 有个时间差。

策略三:
使用推送 sdk 自己的集成方案,当 sdk 自己的推送服务离线时,切换到厂商推送。
优点:最大程度保证推送的稳定性;
缺点:集成方案本身的不稳定性影响了推送的稳定性。

5推送类型


推送类型分为通知和透传。

通知:
  • 厂商到达率有优势;
  • 开发成本较高,需要适配不同厂商的接口标准。

透传:
  • 可以自己解析、展示、跳转,灵活性高;
  • 开发成本较低,适配一次就够(通常通过 json );
  • 一些厂商的透传到达率没有厂商优势。

6集成方式


一般我们集成推送 sdk 有两种集成方式:

  • maven 集成;
  • 手动集成。

从维护的角度来说, maven 集成的维护成本要小于手动集成,但是我这里还是推荐手动集成,主要有以下几个原因。

maven 集成的方式通常 sdk 会要求使用 manifest placeholder 的方式进行 appid appkey appsecret 的注入,但是这种方式需要在 app 层的 build.gradle 去注入,比较耦合。

maven 集成还有一个缺点,准确来说这是 sdk 开发商的问题,总会打包一些我们不需要的资源文件,其实我只需要一个 jar 包而已,并不是整个 aar 啊。

手动集成第一次写 manifest 比较麻烦,但是后面只要替换 jar 和 so 文件就好了,因为 manifest 一般不经常变化,除非有重大版本更新。

推送的展示


接下来就要说说通知 Notification 了,这个东西经过厂商的各种定制,适配起来也有不少麻烦。

厂商的推送样式差异(部分厂商):
  • 不支持 NotificationCompat.BigTextStyle (这都能不支持);
  • 不支持 NotificationCompat.MediaStyle (这个还可以理解);
  • 不支持 Action Button;
  • 不支持 Ticker;
  • 自定义样式高度限制。

原生系统 Android 版本的推送样式差异:
  • 4.x large icon 需要是方的;
  • 5.x 6.x 由 setColor 设置底色和 small icon 配合形成 large icon;
  • 7.x setColor 影响标题颜色。

通知的跳转:
  • 透传:采用 Url 方式进行跳转;
  • 通知:采用 Intent 参数的方式进行跳转。

推送这件小事就说到这里了。

(原文链接:点此进入

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

标签:推送技术
上一篇:专访魅族架构师:海量长连接的实时消息推送系统的心得体会下一篇:基于WebSocket实现Hybrid移动应用的消息推送实践(含代码示例)

本帖已收录至以下技术专辑

推荐方案
评论 5

这个很好,非常好
这个很好,非常好
签名: 不错,很好
引用:tarzan2010 发表于 2017-04-06 13:47
adb shell dumpsys activity services | grep igexin   这个怎么用的?

去查查如何使用Android SDK的工具
adb shell dumpsys activity services | grep igexin   这个怎么用的?
好文章,赞一个
签名: 该会员没有填写今日想说内容.
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部