Android消息推送总结

现状

由于国内防火墙以及各手机厂商系统定制的原因,Android系统内置的GCM(已升级到FCM)推送服务在国内基本不能用,大家只能各自发挥,有的像QQ、微信一样自己搭建推送服务,有的集成像极光、个推等专门做推送服务的第三方库,有的则使用手机厂商提供的推送服务如华为推送、小米推送。但即便有这么多选择,目前也仍然没有一家能做到百分百可靠,究其根源还是各大厂商各自为战缺乏统一规范所致。而由于推送服务都需要在后台长期驻留,多种多样的消息推送也成了影响Android手机性能和耗电的元凶之一。

原理

推送的实现原理主要有三种:

轮询:客户端定时从服务端拉取最新消息,这种方式需要考虑轮询的频率,如果太慢可能导致消息的延迟,如果太快,则会大量消耗流量和电池。对于即时通讯类的产品,这种方案完全不可用;

SMS: 服务端有新消息时,通过短信的方式发送到客户端,客户端监听短消息并获取消息内容;这种方式由于发送短信需要向运营商缴纳费用,故而成本较高;

长连接:客户端与服务端保持长连接,服务端有新消息时通过连接主动发送到客户端;这种方式既能保证消息的实时性,又不会过于消耗手机资源,依托互联网成本不高,所以我们说的推送太多都是采用这种方式。

注:这里说的长连接是应用层的概念,它的传输层实现既可以是TCP也可以是UDP。

关键技术

要实现一个高性能、低消耗的消息推送服务,对于Android客户端来说,涉及的关键技术主要有三点:心跳策略,后台保活 和 休眠唤醒。

心跳策略

前面说到了通过长连接的方式实现消息推送,而长连接往往会因为NAT表超时、DHCP租期、网络状态变化等问题而断开,因此需要不时地传输心跳数据来检测和维持连接的可用。而心跳策略做的就是在合适的时间以合适的间隔去发送心跳包,一个好的心跳策略既可以保证连接的稳定以避免消息延迟,又可以降低因为心跳而引起的信道资源消耗以及手机流量和电量损耗。

微信智能心跳策略

详细可以看这篇文章https://mp.weixin.qq.com/s/ghnmC8709DvnhieQhkLJpA?:

前后台区分处理

为了保证微信收消息及时性的体验,当微信处于前台活跃状态时,使用固定心跳。

微信进入后台(或者前台关屏)时,先用几次最小心跳维持长链接。然后进入后台自适应心跳计算。目的是尽量选择用户不活跃的时间段,来减少心跳计算可能产生的消息不及时收取影响。

自适应心跳算法

自适应心跳算法的做法是通过从小到大逐步增加心跳间隔,来逼近出最大可用心跳,在逼近过程中使用延迟心跳测试法,即每次测试新的心跳间隔前,需要使用短心跳连续成功三次,才认为网络相对稳定,可以进行当前心跳间隔的测试。

在最大心跳(即NAT超时值)算出来之后,使用稍微小一点的值作为后台稳定的心跳间隔,以避免计算结果是临界值的情况。在维持稳定心跳的过程中使用动态调整策略,即在发生心跳失败后,使用延迟心跳测试法测试五次,如果有一次成功,则保持当前心跳值不变,如果五次全部失败,则重新计算心跳值,并且以周为周期,每周三重新计算心跳值。

使用冗余Sync和心跳,即在用户的一些主动操作以及联网状态改变时,增加冗余Sync和心跳,确保及时收到消息。如当用户点亮屏幕时,做一次心跳;当微信切换到前台时,做一词Sync; 联网重建信令TCP时,做一次Sync;

后台保活

Android系统在内存不足会将进程杀死,诸如360等清理软件也会将进程杀死,各厂商定制的后台管理机制也可能会将进程杀死,推送服务面临的威胁如此之多,如何维持推送服务不死也是一大难题。

Android内存回收机制

Android系统将进程按照优先级,从高到低划分为5类:

1、前台进程

(1)Activity正在与用户进程交互(Activity的onResume已经被调用)
(2)与正在和用户交互的Activity绑定的Service
(3)Service运行在前台——Service中调用了startForeground函数
(4)Service正在执行生命周期回调函数(onCreate,onStart,onDestory)
(5)BroadcastReceiver正在执行onReceive方法

2、可视进程

(1)Activity没有运行在前台,但是用户仍然可见(它的onPause方法被调用),例如:当前台Activity启动了一个Dialog,这样Dialog运行在前台,Activity仍然可见,属于可视进程。
(2)与一个可视的Activity绑定的服务所在的进程

3、服务进程

进程中运行着被startService()启动的服务,并且没有进入上面1中(3)、(4)这两种情况。

4、后台进程

没有可见的Activity,并且没有运行中的服务

5、空进程

没有包含活动应用组件的进程为空进程,也就是进程的应用组件已经运行完毕。

每个进程都持有一个adj值,优先级越高的进程持有的adj值就越小。Android内核中的low memory killer会依据这个值来选择杀死哪些进程,比如当内存小于X时,杀死adj值大于Y的进程,前台程序的adj值为0,这意味着它不会被系统杀死。

保活方法

保活的方法主要分为两类,一是尽量减少进程被杀死的概率,一是在进程被杀死后能尽快拉活。

减少杀死的概率:

  • 通过startForeground设为前台服务
  • 在service的onStart方法里返回 STATR_STICK
  • 提高service的优先级
  • 使用系统级service
  • 加入厂商白名单

拉活的方法:

  • 通过监听系统广播拉活

  • 多个app相互拉活

  • 在service的onDestroy方法里重启service

  • 服务互相绑定

  • 在native层fork一个子进程来与主进程互拉。

休眠唤醒

为了节省电量,Android系统在一段时间不操作后,会进入休眠状态,Android6.0之后更是引入了Doze和Standby两种省电模式,达到进一步省电的目的。在这些省电模式下,会挂起一些设备的电源,限制网络访问和一些其它的后台操作(详见Android系统休眠机制),因此休眠自然也会影响到后台的推送服务。

Android休眠机制

由于Android系统是基于Linux内核的,所以Android系统地休眠机制也继承自Linux,并且在此基础上增加了唤醒锁机制。

唤醒锁(WakeLock)

wake_lock 在Android的电源管理系统中扮演一个核心的角色。wake_lock是一种锁的机制,只要持有锁,系统就无法进入休眠,这个锁可以被用户态程序和内核获得。唤醒锁可以是有超时的或者是没有超时的,超时的锁会在超时以后自动解锁。如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠。

Doze和App Standby

从Android 6.0 (API level 23)开始,Android提供了两个节电功能用来增加电池的续航时间。Doze 可以在设备长时间不使用时,通过延迟后台CPU和网络的活动来减少电池的消耗;App Standby将延迟没有交互的app的网络活动。

Doze:

手机在不插电熄屏的状态下,静止不动一段时间(大概1个小时)后,会进入IDLE状态,此机制无视WeakLock,就是说即使持有WeakLock,但在满足上述条件后还是会进入IDLE状态。此状态下将限制应用的网络访问,GPS以及WIFI扫描,推迟包括JobScheduler、Syn、Alarm等操作。在IDLE状态下一段时间后,系统会退出该状态,进入到IDLE_MAINTENANCE状态,此状态下将运行之前推迟的那些操作,并且允许接入网络。在这之后系统又会回到IDLE状态,并且随着休眠时间的增长,IDLE状态下停留的时间也越来越长,进入到IDLE_MAINTENANCE状态的频率也将越来越低,直到维持在一个稳定值。

App Standby:

在App Standby模式下Android系统会使一个用户长时间未使用的应用进入空闲状态。具体来说,当用户长时间未与应用发生交互操作或以下任意场景都未出现时,Android系统就会使应用进入空闲状态:
1.用户主动启动应用
2.应用存在前台进程(前台活动或前台服务,或有组件被另一前台活动及服务使用)
3.应用创建了一个用户可见的锁屏界面上或者是收入Notification tray中的Notification

当用户给Android设备接入充电电源时,Android系统会将所有处于Standby状态的应用释放,允许它们自由的访问网络并执行所有Standby期间暂停的Jobs和Sync。如果应用长时间处于空闲状态,Android系统将会允许处于空闲状态的应用以大约一天一次的频率访问网络。

唤醒方法:

1、在执行发送心跳包、重连、拉取推送内容等操作时使用PARTIAL_WAKE_LOCK,防止在执行重要操作时CPU休眠,并在使用完成后释放,以便休眠;

2、心跳定时器使用AlarmManager,在设置闹钟时使用带WAKEUP后缀的type,在6.0以后使用setExactAndAllowWhileIdle或setAndAllowWhileIdle方法设置闹钟,以便能在Doze模式下唤醒;

3、在Doze模式下,alarm闹钟会被推迟,网络会被限制,可以通过申请加入电池优化白名单的方法修复;申请方法为在mainifest中增加REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限,在代码中使用ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS的Intent来弹出申请对话框。

4、部分手机即便使用了上述方法也会存在网络断开、线程睡死等现象,只能自行适配,看看是否有独特的后台管理机制与省电策略。

你可能感兴趣的:(Android编程)