进程保活
进程保活如何做到,你们保活率有多高
前言
进程保活的关键点有两个,一个是进程优先级的理解,优先级越高存活几率越大。二是弄清楚哪些场景会导致进程会 kill,然后采取下面的策略对各种场景进行优化:
1. 提高进程的优先级
2. 在进程被 kill 之后能够唤醒
进程优先级
Android 一般的进程优先级划分: 1.前台进程 (Foreground process) 2.可见进程
(Visible process) 3.服务进程 (Service process) 4.后台进程 (Background
process) 5.空进程 (Empty process) 这是一种粗略的划分,进程其实有一种具体
的数值,称作 oom_adj,注意:数值越大优先级越低:
红色部分是容易被回收的进程,属于 android 进程
绿色部分是较难被回收的进程,属于 android 进程
其他部分则不是 android 进程,也不会被系统回收,一般是 ROM 自带的
app 和服务才能拥有
如何查看某个进程的 oom_adj 数值呢? oom_adj 存储在 proc/PID/oom_adj 文
件中,其中 PID 是进程的 id,直接 adb shell 进入手机根目录查看这个文件即可。
演示一下:以我自己的项目为例,app 中有两个进程,一个是主进程,另一个是
运行 service 的进程取名为:remote。首先用 android studio 查看每个进程的 PID:
然后分别查看 app 在前台,app 退到后台,这 2 中场景主进程的 oom_adj 数值:
可见,当 app 在前台时 oom_adj = 0,对应上面的表格是前台进程。
当 app 退到后台时,oom_adj = 6,对应后台进程。
然后查看运行着 service 的进程:
ok,知道了进程优先级的概念以及如何查看优先级,我们就可以对 app 进程优
化,然后通过查看这个数值判断我们的优化是否有效果。
进程被 kill 的场景
1.点击 home 键使 app 长时间停留在后台,内存不足被 kill处理这种情况前提是你的 app 至少运行了一个 service,然后通过
Service.startForeground() 设置为前台服务,可以将 oom_adj 的数值由 4 降低到
1,大大提高存活率。
要注意的是 android4.3 之后 Service.startForeground() 会强制弹出通知
栏,解决办法是再启动一个 service 和推送共用一个通知栏,然后 stop 这
个 service 使得通知栏消失。
Android 7.1 之后 google 修复这个 bug,目前没有解决办法 下面的代码
放到你的 service 的 onStartCommand 方法中:
//设置 service 为前台服务,提高优先级
if (Build.VERSION.SDK_INT < 18) {
//Android4.3 以下 ,此方法能有效隐藏 Notification 上的图标
service.startForeground(GRAY_SERVICE_ID, new Notification());
} else if(Build.VERSION.SDK_INT>18 && Build.VERSION.SDK_INT<25){
//Android4.3 - Android7.0,此方法能有效隐藏 Notification 上的图标
Intent innerIntent = new Intent(service, GrayInnerService.class);
service.startService(innerIntent);
service.startForeground(GRAY_SERVICE_ID, new Notification());
}else{
//Android7.1 google 修复了此漏洞,暂无解决方法(现状:Android7.1 以上
app 启动后通知栏会出现一条"正在运行"的通知消息)
service.startForeground(GRAY_SERVICE_ID, new Notification());
}经过改进之后,再来看下这个后台 service 进程的 oom_adj,发现被提升为前台
进程。
2.在大多数国产手机下,进入锁屏状态一段时间,省电机制会 kill 后台进程
这种情况和上面不太一样,是很过国产手机 rom 自带的优化,当锁屏一段时间
之后,即使手机内存够用为了省电,也会释放掉一部分内存。
策略:注册广播监听锁屏和解锁事件, 锁屏后启动一个 1 像素的透明 Activity,
这样直接把进程的 oom_adj 数值降低到 0, 0 是 android 进程的最高优先级。 解
锁后销毁这个透明 Activity。这里我把这个 Activity 放到:remote 进程也就是我那
个后台服务进程,当然你也可以放到主进程,看你打算保活哪个进程。我们可以写一个 KeepLiveManager 来负责接收广播,维护这个 Activity 的常见和
销毁,注意锁屏广播和解锁分别是:ACTION_SCREEN_OOF 和
ACTION_USER_PRESENT,并且只能通过动态注册来绑定,并且是绑定到你的后
台 service 里面,onCreate 绑定,onDestroy 里面解绑
配好之后把手机锁屏,看下:remote 进程的 oom_adj:
3. 用户手动释放内存:包括手机自带清理工具,和第三方 app(360,猎豹清理大师等)
清理内存软件会把 优先级低于 前台进程(oom_adj = 0)的所有进程放入清理列
表,而当我们打开了清理软件就意味着其他 app 不可能处于前台。所以说理论
上可以 kill 任何 app。 以 360 安全卫士为例,打开内存清理:因此这类场景唯一的处理办法就是加入 手机 rom 白名单,比如你打开小米,魅
族的权限管理 -> 自启动管理可以看到 QQ,微信,天猫默认被勾选,这就是厂
商合作。那我们普通 app 可以这么做:在 app 的设置界面加一个选项,提示用
户自己去勾选自启动,我封装了一个工具类给出国内各厂商的自启动的 Intent
跳转方法:
/*** Created by carmelo on 2018/3/17.
* 国内手机厂商白名单跳转工具类
*/
public class SettingUtils {
public static void enterWhiteListSetting(Context context){
try {
context.startActivity(getSettingIntent());
}catch (Exception e){
context.startActivity(new Intent(Settings.ACTION_SETTINGS));
}
}
private static Intent getSettingIntent(){
ComponentName componentName = null;
String brand = android.os.Build.BRAND;
switch (brand.toLowerCase()){
case "samsung":
componentName = new ComponentName("com.samsung.android.sm",
"com.samsung.android.sm.app.dashboard.SmartManagerDashBoardActivity");
break;case "huawei":
componentName = new ComponentName("com.huawei.systemmanager",
"com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
break;
case "xiaomi":
componentName = new ComponentName("com.miui.securitycenter",
"com.miui.permcenter.autostart.AutoStartManagementActivity");
break;
case "vivo":
componentName = new ComponentName("com.iqoo.secure",
"com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity");
break;
case "oppo":
componentName = new ComponentName("com.coloros.oppoguardelf",
"com.coloros.powermanager.fuelgaue.PowerUsageModelActivity");
break;
case "360":
componentName = new
ComponentName("com.yulong.android.coolsafe",
"com.yulong.android.coolsafe.ui.activity.autorun.AutoRunListActivity");
break;
case "meizu":
componentName = new ComponentName("com.meizu.safe","com.meizu.safe.permission.SmartBGActivity");
break;
case "oneplus":
componentName = new ComponentName("com.oneplus.security",
"com.oneplus.security.chainlaunch.view.ChainLaunchAppListActivity");
break;
default:
break;
}
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if(componentName!=null){
intent.setComponent(componentName);
}else{
intent.setAction(Settings.ACTION_SETTINGS);
}
return intent;
}
}
补充几点:
额外说下 自启动是什么意思? 网上都是小米开启自启动就是加入白名单,
其实根本不是这样的,经过实测就算 app 勾选自启动也会被内存优化加速清
理掉,只不过进程会在半分钟后复活
除了还有自启动还有一个设置就是电池管理,比如小米的神隐模式,这部分
和自启动不同的是它是管理 app 在锁屏之后被省电机制杀死的场景,当然每
家厂商也有对应的 Intent 跳转路径。
如何查找不同厂商的设置界面跳转 Intent,比如上面的国内手机厂商白
名单,给个方法:(前提是你有 N 多个不同手机,像我这样。。。)
在酷安应用市场下载一个叫 当前 Activity 的 app,打开后可以看到当前界面的
className,例如:
就找到了魅族 MX4 pro 后台权限的 Activity。
进程唤醒
分两种情况,一是主进程(含有 Activity 没有 service),这种进程由于内存不足
被 kill 之后,用户再次打开 app 系统会恢复到上次的 Activity,这个不在本文话题之内。另一种是 service 的后台进程被 kill,可以通过 service 自有 api 来重启
service:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//.....
return START_STICKY;
// service 被异常停止后,系统尝试重启 service,不
能保证 100%重启成功
}
配好 START_STICKY 后,通过 android studio 释放进程的工具测试下,可以发
现:remote 进程被 kill 之后马上重启了:
但它不是 100%保证重启成功,比如下面 2 种情况:(本人经过测试,这里就不
放效果图了)
Service 第一次被异常杀死后会在 5 秒内重启,第二次被杀死会在 10 秒
内重启,第三次会在 20 秒内重启,一旦在短时间内 Service 被杀死达到
5 次,则系统不再拉起。
进程被取得 Root 权限的管理工具或系统工具通过 forestop 停止掉,无
法重启。总结
本文通过两种 提高进程优先级的方法,针对锁屏 和非锁屏模式下进程在后台被
kill 的场景处理,把后台进程优先级提升到可见级别,基本可以保证绝大多数场
景不会被 kill。另外,针对含有 service 的进程被 kill 给出了可唤醒的办法。
最后,我将进程保活的代码上传到了 Github,地
址: https://github.com/08carmelo/android-keeplive
进程保活(不死进程)
此处延伸:进程的优先级是什么
当前业界的 Android 进程保活手段主要分为** 黑、白、灰 **三种,其大致的实现思路如下:
黑色保活:不同的 app 进程,用广播相互唤醒(包括利用系统提供的广播进行唤醒)
白色保活:启动前台 Service灰色保活:利用系统的漏洞启动前台 Service
黑色保活
所谓黑色保活,就是利用不同的 app 进程使用广播来进行相互唤醒。举个 3 个比较常见的场
景:
场景 1:开机,网络切换、拍照、拍视频时候,利用系统产生的广播唤醒 app
场景 2:接入第三方 SDK 也会唤醒相应的 app 进程,如微信 sdk 会唤醒微信,支付宝 sdk 会
唤醒支付宝。由此发散开去,就会直接触发了下面的 场景 3
场景 3:假如你手机里装了支付宝、淘宝、天猫、UC 等阿里系的 app,那么你打开任意一个
阿里系的 app 后,有可能就顺便把其他阿里系的 app 给唤醒了。(只是拿阿里打个比方,其
实 BAT 系都差不多)
白色保活
白色保活手段非常简单,就是调用系统 api 启动一个前台的 Service 进程,这样会在系统的
通知栏生成一个 Notification,用来让用户知道有这样一个 app 在运行着,哪怕当前的 app
退到了后台。如下方的 LBE 和 QQ 音乐这样:
灰色保活
灰色保活,这种保活手段是应用范围最广泛。它是利用系统的漏洞来启动一个前台的 Service
进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个 Notification,看起来就
如同运行着一个后台 Service 进程一样。这样做带来的好处就是,用户无法察觉到你运行着
一个前台进程(因为看不到 Notification),但你的进程优先级又是高于普通后台进程的。那
么如何利用系统的漏洞呢,大致的实现思路和代码如下:
思路一:API < 18,启动前台 Service 时直接传入 new Notification();
思路二:API >= 18,同时启动两个 id 相同的前台 Service,然后再将后启动的 Service 做 stop
处理
熟悉 Android 系统的童鞋都知道,系统出于体验和性能上的考虑,app 在退到后台时系统并
不会真正的 kill 掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。
在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要 kill 掉哪些进程,
以腾出内存来供给需要的 app。这套杀进程回收内存的机制就叫 Low Memory Killer ,它是
基于 Linux 内核的 OOM Killer(Out-Of-Memory killer)机制诞生。
进程的重要性,划分 5 级:前台进程 (Foreground process)
可见进程 (Visible process)
服务进程 (Service process)
后台进程 (Background process)
空进程 (Empty process)
了解完 Low Memory Killer,再科普一下 oom_adj。什么是 oom_adj?它是 linux 内核分配给
每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否
进行回收。对于 oom_adj 的作用,你只需要记住以下几点即可:
进程的 oom_adj 越大,表示此进程优先级越低,越容易被杀回收;越小,表示进程优先级
越高,越不容易被杀回收
普通 app 进程的 oom_adj>=0,系统进程的 oom_adj 才可能<0
有些手机厂商把这些知名的 app 放入了自己的白名单中,保证了进程不死来提高用户体验
(如微信、QQ、陌陌都在小米的白名单中)。如果从白名单中移除,他们终究还是和普通
app 一样躲避不了被杀的命运,为了尽量避免被杀,还是老老实实去做好优化工作吧。
所以,进程保活的根本方案终究还是回到了性能优化上,进程永生不死终究是个彻头彻尾的
伪命题!
目前市面上的应用,貌似除了微信和手Q都会比较担心被用户或者系统(厂商)杀死问题。本文对 Android 进程拉活进行一个总结。
Android 进程拉活包括两个层面:
-
提供进程优先级,降低进程被杀死的概率
-
在进程被杀死后,进行拉活
本文下面就从这两个方面做一下总结。
1. 进程的优先级
Android 系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要清除旧进程来回收内存。 为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 必要时,系统会首先消除重要性最低的进程,然后是清除重要性稍低一级的进程,依此类推,以回收系统资源。
进程的重要性,划分5级:
-
前台进程 (Foreground process)
-
可见进程 (Visible process)
-
服务进程 (Service process)
-
后台进程 (Background process)
-
空进程 (Empty process)
前台进程的重要性最高,依次递减,空进程的重要性最低,下面分别来阐述每种级别的进程
1.1 前台进程 —— Foreground process
用户当前操作所必需的进程。通常在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。
-
拥有用户正在交互的 Activity(已调用
onResume()
) -
拥有某个 Service,后者绑定到用户正在交互的 Activity
-
拥有正在“前台”运行的 Service(服务已调用
startForeground()
) -
拥有正执行一个生命周期回调的 Service(
onCreate()
、onStart()
或onDestroy()
) -
拥有正执行其
onReceive()
方法的 BroadcastReceiver
1.2 可见进程 —— Visible process
没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。
-
拥有不在前台、但仍对用户可见的 Activity(已调用
onPause()
) -
拥有绑定到可见(或前台)Activity 的 Service
1.3 服务进程 —— Service process
尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。
正在运行 startService()
方法启动的服务,且不属于上述两个更高类别进程的进程。
1.4 后台进程 —— Background process
后台进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU 列表中,以确保包含用户最近查看的 Activity
的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。
对用户不可见的 Activity 的进程(已调用 Activity的onStop()
方法)
1.5 空进程 —— Empty process
保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。
不含任何活动应用组件的进程
2. Android 进程回收策略
Android 中对于内存的回收,主要依靠 Lowmemorykiller 来完成,是一种根据 OOM_ADJ 阈值级别触发相应力度的内存回收的机制。
关于 OOM_ADJ 的说明如下:
其中红色部分代表比较容易被杀死的 Android 进程(OOM_ADJ>=4),绿色部分表示不容易被杀死的 Android 进程,其他表示非 Android 进程(纯 Linux 进程)。在 Lowmemorykiller 回收内存时会根据进程的级别优先杀死 OOM_ADJ 比较大的进程,对于优先级相同的进程则进一步受到进程所占内存和进程存活时间的影响。
Android 手机中进程被杀死可能有如下情况:
综上,可以得出减少进程被杀死概率无非就是想办法提高进程优先级,减少进程在内存不足等情况下被杀死的概率。
3. 提升进程优先级的方案
3.1 利用 Activity 提升权限
方案设计思想:监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。注意该 Activity 需设计成用户无感知。
通过该方案,可以使进程的优先级在屏幕锁屏时间由4提升为最高优先级1。
方案适用范围:
-
适用场景:本方案主要解决第三方应用及系统管理工具在检测到锁屏事件后一段时间(一般为5分钟以内)内会杀死后台进程,已达到省电的目的问题。
-
适用版本:适用于所有的 Android 版本。
方案具体实现:首先定义 Activity,并设置 Activity 的大小为1像素:
其次,从 AndroidManifest 中通过如下属性,排除 Activity 在 RecentTask 中的显示:
最后,控制 Activity 为透明:
Activity 启动与销毁时机的控制:
3.2 利用 Notification 提升权限
方案设计思想:Android 中 Service 的优先级为4,通过 setForeground 接口可以将后台 Service 设置为前台 Service,使进程的优先级由4提升为2,从而使进程的优先级仅仅低于用户当前正在交互的进程,与可见进程优先级一致,使进程被杀死的概率大大降低。
方案实现挑战:从 Android2.3 开始调用 setForeground 将后台 Service 设置为前台 Service 时,必须在系统的通知栏发送一条通知,也就是前台 Service 与一条可见的通知时绑定在一起的。
对于不需要常驻通知栏的应用来说,该方案虽好,但却是用户感知的,无法直接使用。
方案挑战应对措施:通过实现一个内部 Service,在 LiveService 和其内部 Service 中同时发送具有相同 ID 的 Notification,然后将内部 Service 结束掉。随着内部 Service 的结束,Notification 将会消失,但系统优先级依然保持为2。
方案适用范围:适用于目前已知所有版本。
方案具体实现:
4. 进程死后拉活的方案
4.1 利用系统广播拉活
方案设计思想:在发生特定系统事件时,系统会发出响应的广播,通过在 AndroidManifest 中“静态”注册对应的广播监听器,即可在发生响应事件时拉活。
常用的用于拉活的广播事件包括:
方案适用范围:适用于全部 Android 平台。但存在如下几个缺点:
-
广播接收器被管理软件、系统软件通过“自启管理”等功能禁用的场景无法接收到广播,从而无法自启。
-
系统广播事件不可控,只能保证发生事件时拉活进程,但无法保证进程挂掉后立即拉活。
因此,该方案主要作为备用手段。
4.2 利用第三方应用广播拉活
方案设计思想:该方案总的设计思想与接收系统广播类似,不同的是该方案为接收第三方 Top 应用广播。
通过反编译第三方 Top 应用,如:手机QQ、微信、支付宝、UC浏览器等,以及友盟、信鸽、个推等 SDK,找出它们外发的广播,在应用中进行监听,这样当这些应用发出广播时,就会将我们的应用拉活。
方案适用范围:该方案的有效程度除与系统广播一样的因素外,主要受如下因素限制:
-
反编译分析过的第三方应用的多少
-
第三方应用的广播属于应用私有,当前版本中有效的广播,在后续版本随时就可能被移除或被改为不外发。
这些因素都影响了拉活的效果。
4.3 利用系统Service机制拉活
方案设计思想:将 Service 设置为 START_STICKY,利用系统机制在 Service 挂掉后自动拉活:
方案适用范围:如下两种情况无法拉活
-
Service 第一次被异常杀死后会在5秒内重启,第二次被杀死会在10秒内重启,第三次会在20秒内重启,一旦在短时间内 Service 被杀死达到5次,则系统不再拉起。
-
进程被取得 Root 权限的管理工具或系统工具通过 forestop 停止掉,无法重启。
4.4 利用Native进程拉活
方案设计思想:
-
主要思想:利用 Linux 中的 fork 机制创建 Native 进程,在 Native 进程中监控主进程的存活,当主进程挂掉后,在 Native 进程中立即对主进程进行拉活。
-
主要原理:在 Android 中所有进程和系统组件的生命周期受 ActivityManagerService 的统一管理。而且,通过 Linux 的 fork 机制创建的进程为纯 Linux 进程,其生命周期不受 Android 的管理。
方案实现挑战:
-
挑战一:在 Native 进程中如何感知主进程死亡。
要在 Native 进程中感知主进程是否存活有两种实现方式:
-
在 Native 进程中通过死循环或定时器,轮训判断主进程是否存活,档主进程不存活时进行拉活。该方案的很大缺点是不停的轮询执行判断逻辑,非常耗电。
-
在主进程中创建一个监控文件,并且在主进程中持有文件锁。在拉活进程启动后申请文件锁将会被堵塞,一旦可以成功获取到锁,说明主进程挂掉,即可进行拉活。由于 Android 中的应用都运行于虚拟机之上,Java 层的文件锁与 Linux 层的文件锁是不同的,要实现该功能需要封装 Linux 层的文件锁供上层调用。
封装 Linux 文件锁的代码如下:
Native 层中堵塞申请文件锁的部分代码:
-
挑战二:在 Native 进程中如何拉活主进程。
通过 Native 进程拉活主进程的部分代码如下,即通过 am 命令进行拉活。通过指定“--include-stopped-packages”参数来拉活主进程处于 forestop 状态的情况。
-
挑战三:如何保证 Native 进程的唯一。
从可扩展性和进程唯一等多方面考虑,将 Native 进程设计层 C/S 结构模式,主进程与 Native 进程通过 Localsocket 进行通信。在Native进程中利用 Localsocket 保证 Native 进程的唯一性,不至于出现创建多个 Native 进程以及 Native 进程变成僵尸进程等问题。
方案适用范围:该方案主要适用于 Android5.0 以下版本手机。
该方案不受 forcestop 影响,被强制停止的应用依然可以被拉活,在 Android5.0 以下版本拉活效果非常好。
对于 Android5.0 以上手机,系统虽然会将native进程内的所有进程都杀死,这里其实就是系统“依次”杀死进程时间与拉活逻辑执行时间赛跑的问题,如果可以跑的比系统逻辑快,依然可以有效拉起。记得网上有人做过实验,该结论是成立的,在某些 Android 5.0 以上机型有效。
4.5 利用 JobScheduler 机制拉活
方案设计思想:Android5.0 以后系统对 Native 进程等加强了管理,Native 拉活方式失效。系统在 Android5.0 以上版本提供了 JobScheduler 接口,系统会定时调用该进程以使应用进行一些逻辑操作。
在本项目中,我对 JobScheduler 进行了进一步封装,兼容 Android5.0 以下版本。封装后 JobScheduler 接口的使用如下:
方案适用范围:该方案主要适用于 Android5.0 以上版本手机。
该方案在 Android5.0 以上版本中不受 forcestop 影响,被强制停止的应用依然可以被拉活,在 Android5.0 以上版本拉活效果非常好。
仅在小米手机可能会出现有时无法拉活的问题。
4.6 利用账号同步机制拉活
方案设计思想:Android 系统的账号同步机制会定期同步账号进行,该方案目的在于利用同步机制进行进程的拉活。添加账号和设置同步周期的代码如下:
该方案需要在 AndroidManifest 中定义账号授权与同步服务。
方案适用范围:该方案适用于所有的 Android 版本,包括被 forestop 掉的进程也可以进行拉活。
最新 Android 版本(Android N)中系统好像对账户同步这里做了变动,该方法不再有效。
5. 其他有效拉活方案
经研究发现还有其他一些系统拉活措施可以使用,但在使用时需要用户授权,用户感知比较强烈。
这些方案包括:
-
利用系统通知管理权限进行拉活
-
利用辅助功能拉活,将应用加入厂商或管理软件白名单。
这些方案需要结合具体产品特性来搞。
上面所有解释这些方案都是考虑的无 Root 的情况。
其他还有一些技术之外的措施,比如说应用内 Push 通道的选择:
-
国外版应用:接入 Google 的 GCM。
-
国内版应用:根据终端不同,在小米手机(包括 MIUI)接入小米推送、华为手机接入华为推送;其他手机可以考虑接入腾讯信鸽或极光推送与小米推送做 A/B Test。