Android Push实现总结


push方案

poll:客户端定时去服务器端检查

push:客户端和服务器维持一个连接,服务器有新内容推送给客户端

 

实现对比

1、XMPPExtensible Messaging and Presence Protocol,前称Jabber[1])是一种以XML为基础的开放式实时通信协议,是经由互联网工程工作小组(IETF)通过的互联网标准。XMPP因为被Google Talk应用而被广大网民所接触(米聊也是基于XMPP协议)。

http://zh.wikipedia.org/wiki/XMPP

 

优点:基于XML,扩展性强;周边生态成熟,有很多开源方案,如AndroidPn,也可以自己来实现,Smack做客户端,Openfire作为服务端实现客户端和服务端实时通信;

缺点:协议较复杂、冗余(基于XML)、费流量

 

2、GCM服务(Google Cloud Messaging)

简介:Google推出的云消息服务,即第二代的G2DM,基于XMPP协议实现。

优点:Google提供的服务、原生、简单,无需实现和部署服务端。

缺点:Android版本限制(必须大于2.2版本),该服务在国内不够稳定、需要用户绑定Google帐号,受限于Google。

淘宝有用到,客户端根据当前环境选择GCM或定制Push服务。

 

3、MQTT协议(更多信息见:http://mqtt.org/

简介:轻量级的、基于代理的“发布/订阅”模式的消息传输协议。

优点:协议简洁、小巧、可扩展性强、省流量、省电,目前已经应用到企业领域(参考:http://mqtt.org/software),且已有C++版的服务端组件rsmb。

缺点:不够成熟、实现较复杂、服务端组件rsmb不开源

 

4、基于Socket连接,自定义协议

维护一个客户端到服务器长连接,自定义数据交换格式,实现Push

优点:可控性强,按自身业务随意定制,实时性强

缺点:实现复杂

极光推送、信鸽等Push基于TCP协议自己定制

 

5、HTTP轮循方式

简介:定时向HTTP服务端接口(Web Service API)获取最新消息。

优点:实现简单、可控性强

缺点:实时性差。

 

服务器主动Push的方式需要保持链路的连通性,因运营商网关的关系,五分钟左右会回收没有使用的连接,所以需要心跳包保活。

 

Push方案选择

结合自身业务需求,选择了通过HTTP轮询方案。拉取Push没有那么高的实时性要求,长连接保活需要高频率(4-5分钟发送一次心跳包)的发送心跳包,耗电;产品需求是Push展示时间为8-24点,默认轮询周期是4小时,Push一般是前一天配置好,可最大可能的被拉取到。

 

轮询那么必须有个定时机制,定时唤醒客户端去服务器拉取

Android定时机制对比

Android定时机制有Handler、Timer、AlarmManager。

Handler和AlarmManager的区别:AlarmManager可以通过PendingIntent唤醒未启动的进程,而Handler定时信息随着进程被杀消失,Handler定时依赖于epoll的timeout,AlarmManagerService依赖于RTC时钟。如果程序被强制停止后,基于安全机制AlarmManager注册的定时器会被清空(防止恶意应用频繁唤醒)。

 

 The Alarm Manager is intended for cases where youwant to have your application code run at a specific time, even if yourapplication is not currently running. For normal timing operations (ticks,timeouts, etc) it is easier and much more efficient to useHandler

 

定时器注册

定时器需要注册,如何选取注册事件?

为了保证定时器注册成功率,尽可能选高频率的系统事件,如开机广播(Android 系统版本小于2.3),网络切换事件、时间改变事件(DateChanged、TimeChanged,当时注册这两个事件是为了搞定测试,但是被坑的也不浅)

DateTimeChanged和TimeChanged

这两个事件在系统启动后,启动AlarmManagerService服务,同时由AlarmManagerService调度,

DateChanged一天触发一次,12点左右触发,由于程序设计问题,收到DateChanged事件,会直接触发拉取更新或注册定时器,刚发布新版本请求挺均匀的,过了一两天就开始有大量请求聚集在午夜,这也就是为什么每天12点会有高峰(后面会对定时器原理做个分享)

 

请求聚集

上面已经举了个因DateTimeChanged事件聚集,导致客户端拉取push请求聚集的例子。可以用具体算法把拉取周期打散,考虑到用户使用客户端是一个离散事件,首次启动、或网络切换的时候注册定时器,这样就能保证定时器触发的时间在一个周期内是离散的,同时也保证代码的简洁。

 

系统里有大量未触发的Alarm

产商投诉我们的的AlarmManager有3000多个PendingIntent未发送,而且还在频繁注册,起初没留意,以为是产商系统问题,装了一个在测试机上面,dump Alarm信息后发现没有说的那么多,后来根据产商提示,频繁网络切换,频繁灭屏(Wifi自动更新依赖于该事件),确实会有大量的PendingIntent,但是一旦触发一次Alarm,具有相同Action的PendingIntent一起被系统清除(这个问题还没有想清楚,看代码也没看出为啥)。

     AlarmManager在注册定时事件的时候会根据PendingIntent作为判断依据,如果之前注册过一个相同的PendingIntent,那么再注册一次会把前一个给取消,用后面的给替换。

PendingIntent有三个可选参数,FLAG_CANCEL_CURRENT,会把系统缓存的PendingIntentRecord对象先remove,然后再创建一个新的PendingIntent对象。前后两次创建的PendingIntent比较不相等,造成这个问题,将FLAG改成FlAG_UPDATE_CURRENT问题得以解决。

 

注册了事件拉取逻辑?

每隔4小时轮询服务器,联网用户拉取比率只有80-85%左右,没网络就错过一次机会,改策略为间隔大于4小时有网络就拉,联网用户拉取比例99%

 

Push无法展示

安全软件

拦截应用自启动,发push,此动作不一定需要手机root,360能对特定系统漏洞提权,获取root权限,组织应用接收系统事件

系统内置优化工具:已知新蜂Rom,X产商5860S,优化后无法收到Push

现象:dumsys alarm定时器工作正常,但是就是接收不到广播,一番google后发现有人提及4.0后可以禁用Android 四大组件(前提是有root权限),查看/data/system/packages.xml,发现广播接收器被禁用。

安全软件或是优化工具,会对注册系统广播的组件进行分析,禁用;更高级的是获取root后hook,然后替换Android框架通知核心处理逻辑,根据白名单禁止通知,金山早期版本有这么做过。

规避:不要‘高危’广播,或者将这类广播和业务处理逻辑Receiver分开。

      Android限制

     非系统应用最多只能在通知栏展示50个通知,通知被用户cancel、应用进程被杀或者强制停止后,不再有该限制。

       规避:很少有产品会这么做,一般发广播都是会复用REQUEST ID,这里只是澄清有这种限制

系统本身问题

产商OS问题,安装后就会禁用接收了BOOT_RECEIVER广播的组件

Push展示数字不准确

由于后台更新服务是有损的,加之程序逻辑bug,每次进入商店提示应用更新的Push数字差异比较大,程序Bug Fix后出现概率小了些,最终定位到后台返回更新数不准确才算告一段落。

架构

定时事件触发后,加上系统事件这么多事件,如何统一接受、处理、分发?

 

总结:

     Push核心集中在定时任务模块,定时器问题排查一看定时器有没有问题(dumpsys alarm),二看广播接收器有没有问题,看log或看组件是否被禁用(/data/system/packages.xml)。多了解一些Android背后系统实现,解决一些疑难问题比较有帮助。文档写的再好也会有笔误或更新不及时。

你可能感兴趣的:(Android Push实现总结)