IM 推送保障及网络优化详解(一):如何实现不影响用户体验的后台保活
对于移动 App 来说,IM 功能正变得越来越重要,它能够创建起人与人之间的连接。社交类产品中,用户与用户之间的沟通可以产生出更好的用户粘性。
在复杂的 Android 生态环境下,多种因素都会造成消息推送不能及时达到客户端。另外,不稳定的移动网络也给数据传输的速率和可靠性增加了障碍。
本文详解了网易云信 IM SDK 在应对弱网环境、移动端硬件限制以及 Android 复杂的生态现状时的探索与心得。如何实现不影响用户体验的后台保活,改善的长连接加推送组合方案,以及在弱网环境大数据传输的优化实践。
带着思考阅读:
1.什么是 IM
2.IM SDK 如何实现不影响用户体验的后台保活
3.如何做长连接加推送组合方案
4.如何在弱网环境下优化大数据传输
IM 的定义
IM 由两个字组成:Instant, Messaging
即时性要求有新消息时能够立即收到,如果程序在后台,则要能立即收到推送通知。
通信则要求稳定可靠,系统不宕机,程序不崩溃,安全,传递消息时不会被拦截监听,消息不丢,顺序不乱,不重复,如果包含音视频聊天,则要求延迟低,流畅不卡顿。
要真正做出一套稳定可靠的商用级 IM 系统,挑战非常大。
第一个问题是消息推送。iOS 有 APNS 做推送,相当稳定。Android 本身也有GCM 可以用,但是在国内有“墙”,直接就把 GCM 等等 Google 的服务全部挡在外面。为了实现即时稳定的消息推送,从易信时代开始,网易就开始研究,随着时间的推移,困难和方法也在不停的变化。
对于 IM,当 App 退到后台,是必须还能够收到新消息提醒的,没有 GCM,怎么办?在最初,唯一能做的,就是后台运行了。这几乎是接收推送的唯一途径,就算是到现在,也是最主要的途径。Android 从设计上,就是支持真后台运行的,后台运行的特性也是 Android 现在能如此成功的原因之一,但另一面,Android 长久以来一直摆脱不了的卡顿,耗电等坏名声,后台运行也拖不了干系。因此,系统对于后台运行也不会放任自流。
App 在后台运行所面对的四大障碍
第一个障碍是 Android 的 Low Memory Killer 机制。手机的内存有限,当后台运行的进程越来越多,内存剩余量也就随之减少。当有一个新的 APP 想要启动,如果内存不够,LMK 机制就会启动,从正在运行的进程中挑选一个清理掉,释放出空间,然后新的 APP 就可以运行了。
LMK 有两个尺度去评判。一个是进程优先级,优先级越低,被清理的可能性越大,另一个是内存占用,占的内存越多,被清理的权重自然也越大。
因为 LMK 机制的存在,虽然 APP 允许在后台运行,但同样也面临随时被清理的风险。因此,网易需要在被清理后及时的重新启动。常规的,有4种方式能够做到。
- 第一个是 sticky service
就是在 Service 的 onStartCommand 中返回sticky flag,这样当 service 被 kill 掉后,系统会将它加入重启的 pending 列表,在后面合适的时机再把 service 重启。
- 第二个障碍是 alarm
闹钟,有循环闹钟和一次性闹钟两种,在闹钟触发后启动对应的组件。
- 第三个障碍是在 Manifest 文件中静态注册的 Receiver
通过监听各种系统事件,比如开机,网络变化,mount/unmounts 等,在这些事件发生时启动组件,因为这种方式会造成在这些事件发生时系统容易卡顿,在 7.0 里面,Android 增加了限制。
- 第四个障碍是 JobScheduler
这是在 5.0 里面新增的,允许 App 在特定事件发生时做一些动作,比如充电,切换到 wifi 等。
虽说无论怎么做,App 终究免不了一死,但通过对照 LMK 的评判准则,还是可以降低APP被清理的概率的。第一个就是降低进程的内存占用。如果采用单进程的模式,由于进程中包含了 UI,Webview,各种图片缓存等内容,内存必然会居高不下,降不下来。IM 软件一般都会采用双进程甚至多进程的策略,将 push 进程独立出来,在 push 进程里只处理网络连接和 push 业务,不参与任何其他业务逻辑,更不包含任何 UI。
以下是网易云信 Android SDK 的架构,按照分层的结构模式设计。最底下青色的一层是 push 层,他就是作为一个独立进程运行的。他只负责处理网络长连接的相关工作,比如安全加密,心跳,鉴权,封包解包等工作,所有业务逻辑都交给UI进程的服务模块去做。来看一下云信 Demo 的进程内存占用情况。上面一个是主进程,看第四列 PSS 的数据,内存占用是 50M 左右,下面一个是 push 进程,内存占用只有 10M 左右。当处于后台时,push 进程被清理概率比 UI 主进程低很多。
降低被清理概率的第二个手段是提升进程优先级。先看这个例子,这是绿色守护的一个截图,最上面是“暂不自动休眠”,因为这里列出的两个 App 的状态都是工作中,对应的进程优先级是“可视进程”。但这两个 App 并没有提供桌面小部门在运行,也没有指示前台服务的常驻通知栏提醒,事实上,他们就只是在后台运而已。通常进程退到后台后,其进程优先级类型就变成了较低的后台进程,而不是这样的“可视进程”,他们是通过什么方法来提升优先级,降低被清理概率呢?
Android 在设计前台服务上有一个漏洞,通过两个服务配合就能创建一个隐形的前台服务。这里有两个已经启动的 Service: A 和 B。先在 A 中调用 startForeground,提供一个 NOTIFY_ID, 然后 A 就变成前台服务了,同时有了一个 ID 为 NOTIFY_ID 的常驻通知栏提醒,然后网易在 B 中也调用startForeground,提供相同的 NOTIFY_ID, B 也变成了前台服务,因为两个通知ID相同,因此这一次就不会创建新的通知栏提醒了。然后再在 A 中调用stopForeground,A 的前台属性被取消,同时,常驻通知栏提醒也会被移除,但是,Service B 并不会受到任何影响,还是前台服务,这是再把 A 停掉,进程就只剩下前台服务 B 了,进程也变成了前台进程,但用户不会有任何感知。
正常来说,做了上面 3 步之后,进程就能够比较稳定的在后台运行了。
但在有些情况下,推送进程却永远起不来。跟踪之后发现,除了系统能够杀掉后台运行的进程外,用户也一样是可以杀死进程的。用户杀掉进程的方式有两种,一种是在最近任务列表中将 App 划掉,这种方式和系统杀掉进程效果相同。另外一种就是通过这里的 force stop,这种方式比系统清理更加彻底。不但 App正在运行的进程会被清理,app 当前在重启列表中的待重启服务,注册的各种闹钟,事件监听组件等都会被移除,除非用户在主动点击或者系统重启等外力,App 没法再自己重新爬起来了。
在有些国内的像 MIUI 一类的 ROM 上,用户从最近任务列表中将 App 移除,效果竟然也是 force stop。正常来说,如果是用户主动操作,App 本身也不应该再重启了。但有些时候这个并不是用户本意,况且,对于 IM 软件来说,消息推送是一定要得到保障的,否则不明正确的吃瓜群众们会觉得是软件不行,连消息推送都做不好。
APP 安卓进程保活的好办法
第一个是通过两次 fork 加上 exec 的方式。两个 fork 后,第一次 fork 的进程退出,第二次 fork 出来的进程就会被 init 进程领养。用户此时再 force stop,因为这个进程复进程是 init,而不是 Zygote,因此不会被清理。由于这个进程还是从 android 进程 fork 出来的,带有 android 运行时环境以及复进程的资源,所以内存会比较大,这里可以再通过 exec 命令,打开一个纯 linux 的可执行文件,开启一个 daemon 进程,其内存占用大概只有 100K+,对用户也就完全无感了。利用这个后台进程,可以定时的将 push 进程拉起来。此种方式只在 5.0 以下的系统中有效,在 4.4 及以上系统中,SELinux 特性是强制开启的,exec 没有权限执行,同时在 5.0 之后,ActivityManager 在做 force stop 以及移除任务时,只要是具有相同的 uid 的进程,就会全部清理掉,不再漏掉没有虚拟机环境的进程。
最后一个后台保活的手段是一个大杀器,也是带有强烈的中国特色。因为前面所列的所有保活手段都不是那么保险,因此想出来这么一个互相保活的方式。当一个 App 进程起来后,他就去扫描已安装的应用列表,看看有没有自己的兄弟姐妹,比如说同一个长的 App,或者是集成了同一个 SDK 的 APP,如果有,就把这些 App 都拉起来。这也就是现在比较出名的“全家桶”方案。虽说这种方法确实能够带来较高的后台存活率,特别是那些大厂和应用广泛的 SDK,但是这种方式对于用户的伤害也非常大,如果有后台推送的必要性,且不会对用户体验造成太大伤害时,此方式还可以使用,但如果只是为了推广告,则会对用户造成伤害,反过来,也可能会导致用户直接卸载 App。
现在各种手机管理软件都会对这种全家桶唤醒方式做限制,特别是在 root 过的机器上,可以做到完全切断这些唤醒路径。同时,很多 ROM 也会自带管理软件,限制后台运行和后台唤醒,以便给设备换取更长的续航。在目前国内的 Android 生态环境中,无论采用什么方式,想要一直在后台运行时越来越难了,需要重新想另外的办法来保障消息推送。另一方面,作为开发者,也有义务为用户提供更好体验的软件,而不是无休止的在后台浪费用户的资源。
随着即时通讯以及音频处理和压缩技术的不断发展,效果更好、适用范围更广、性能更高的算法和新的技术必将不断涌现,如果你有好的技术或者分享,欢迎关注网易云信官方博客和 GitHub:
关注更多技术干货内容:网易云信博客
欢迎关注网易云信 GitHub
欢迎关注网易云信官网