对于移动 App 来讲,IM 功能正变得愈来愈重要,它可以建立起人与人之间的链接。社交类产品中,用户与用户之间的沟通能够产生出更好的用户粘性。
在复杂的 Android 生态环境下,多种因素都会形成消息推送不能及时达到客户端。另外,不稳定的移动网络也给数据传输的速率和可靠性增长了障碍。
本文详解了网易云信 IM SDK 在应对弱网环境、移动端硬件限制以及 Android 复杂的生态现状时的探索与心得。如何实现不影响用户体验的后台保活,改善的长链接加推送组合方案,以及在弱网环境大数据传输的优化实践。linux
1.什么是 IM
2. IM SDK 如何实现不影响用户体验的后台保活
3.如何作长链接加推送组合方案
4.如何在弱网环境下优化大数据传输
IM 由两个字组成:Instant, Messaging
即时性要求有新消息时可以当即收到,若是程序在后台,则要能当即收到推送通知。
通讯则要求稳定可靠,系统不宕机,程序不崩溃,安全,传递消息时不会被拦截监听,消息不丢,顺序不乱,不重复,若是包含音视频聊天,则要求延迟低,流畅不卡顿。
要真正作出一套稳定可靠的商用级 IM 系统,挑战很是大。
第一个问题是消息推送。iOS 有 APNS 作推送,至关稳定。Android 自己也有GCM 能够用,可是在国内有“墙”,直接就把 GCM 等等 Google 的服务所有挡在外面。为了实现即时稳定的消息推送,从易信时代开始,网易就开始研究,随着时间的推移,困难和方法也在不停的变化。
对于 IM,当 App 退到后台,是必须还可以收到新消息提醒的,没有 GCM,怎么办?在最初,惟一能作的,就是后台运行了。这几乎是接收推送的惟一途径,就算是到如今,也是最主要的途径。Android 从设计上,就是支持真后台运行的,后台运行的特性也是 Android 如今能如此成功的缘由之一,但另外一面,Android 长久以来一直摆脱不了的卡顿,耗电等坏名声,后台运行也拖不了干系。所以,系统对于后台运行也不会听任自流。android
第一个障碍是 Android 的 Low Memory Killer 机制。手机的内存有限,当后台运行的进程愈来愈多,内存剩余量也就随之减小。当有一个新的 APP 想要启动,若是内存不够,LMK 机制就会启动,从正在运行的进程中挑选一个清理掉,释放出空间,而后新的 APP 就能够运行了。
LMK 有两个尺度去评判。一个是进程优先级,优先级越低,被清理的可能性越大,另外一个是内存占用,占的内存越多,被清理的权重天然也越大。
由于 LMK 机制的存在,虽然 APP 容许在后台运行,但一样也面临随时被清理的风险。所以,网易须要在被清理后及时的从新启动。常规的,有4种方式可以作到。git
就是在 Service 的 onStartCommand 中返回sticky flag,这样当 service 被 kill 掉后,系统会将它加入重启的 pending 列表,在后面合适的时机再把 service 重启。
闹钟,有循环闹钟和一次性闹钟两种,在闹钟触发后启动对应的组件。
经过监听各类系统事件,好比开机,网络变化,mount/unmounts 等,在这些事件发生时启动组件,由于这种方式会形成在这些事件发生时系统容易卡顿,在 7.0 里面,Android 增长了限制。
这是在 5.0 里面新增的,容许 App 在特定事件发生时作一些动做,好比充电,切换到 wifi 等。
虽然说不管怎么作,App 终究免不了一死,但经过对照 LMK 的评判准则,仍是能够下降APP被清理的几率的。第一个就是下降进程的内存占用。若是采用单进程的模式,因为进程中包含了 UI,Webview,各类图片缓存等内容,内存必然会居高不下,降不下来。IM 软件通常都会采用双进程甚至多进程的策略,将 push 进程独立出来,在 push 进程里只处理网络链接和 push 业务,不参与任何其余业务逻辑,更不包含任何 UI。github
如下是网易云信 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 软件来讲,消息推送是必定要获得保障的,不然不明正确的吃瓜群众们会以为是软件不行,连消息推送都作很差。架构
第一个是经过两次 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 进程起来后,他就去扫描已安装的应用列表,看看有没有本身的兄弟姐妹,好比说同一个长的 App,或者是集成了同一个 SDK 的 APP,若是有,就把这些 App 都拉起来。这也就是如今比较出名的“全家桶”方案。虽然说这种方法确实可以带来较高的后台存活率,特别是那些大厂和应用普遍的 SDK,可是这种方式对于用户的伤害也很是大,若是有后台推送的必要性,且不会对用户体验形成太大伤害时,此方式还可使用,但若是只是为了推广告,则会对用户形成伤害,反过来,也可能会致使用户直接卸载 App。
如今各类手机管理软件都会对这种全家桶唤醒方式作限制,特别是在 root 过的机器上,能够作到彻底切断这些唤醒路径。同时,不少 ROM 也会自带管理软件,限制后台运行和后台唤醒,以便给设备换取更长的续航。在目前国内的 Android 生态环境中,不管采用什么方式,想要一直在后台运行时愈来愈难了,须要从新想另外的办法来保障消息推送。另外一方面,做为开发者,也有义务为用户提供更好体验的软件,而不是无休止的在后台浪费用户的资源。
随着即时通信以及音频处理和压缩技术的不断发展,效果更好、适用范围更广、性能更高的算法和新的技术必将不断涌现,若是你有好的技术或者分享,欢迎关注网易云信官方博客和 GitHub:
关注更多技术干货内容: 网易云信博客
欢迎关注 网易云信 GitHub
欢迎关注 网易云信官网