Android 应用进程保活APP常驻内存研究方案
前言
遥想当年,博主铭记在心的一件事,就是曾做过让我加快秃顶的功能——健步活动。我们知道,在Android4.4之后的版本中,Android在硬件中支持内置计步传感器,例如微信运动等软件都是调用了Android中的Sensor.TYPE_STEP_COUNTER传感器服务,从而获取到每日的步数。
由于当时用户手机版本普遍偏低,因此需要手写记步。当时好在机智如我,参考了微信摇一摇的原理,通过加速度传感器SENSOR_TYPE_LINEAR_ACCELERATION获取到在某个时间段的加速度值,最后通过一顿计算公式得出步数。但是不同机型的加速度值并不相同,而且超级耗电。而最难的问题,就是应用特别容易被杀死,因为内存和电量耗损大,当应用在后台运行会优先被系统回收,而且用户手动一键清理时杀死进程,导致无法记步。
一、常见的应用保活方法
1、 监听广播方式
通过监听全局的静态广播,如开机广播、解锁屏广播、网络状态广播等,来启动应用的后台服务。目前,在高版本的Android系统中已经失效,因为Android系统规定应用必须在系统开机后运行一次才能监听这些系统广播,一般应用进程被杀死,广播也接收不到。
2、 提高Service的优先级
提高Service优先级方法很多,比如onStartCommand返回START_STICKY使系统内存足够的时候Service能够自动启动、弹出通知、配置service的优先级等,这些方式只能在一定程度上缓解service被立马回收,但只要用户一键清理或者系统回收照样无效。
3、 双service拉起
经过测试,只要当前应用被杀,任何后台service都无法运行,也无法拉起。
3、双进程拉起
这种方式使用NDK在底层fork出一个子进程,来实现与父进程之间的互拉。在Android4.x还是非常有效的,但是高版本的Android系统的系统回收策略已经改成进程组的形式了,如果系统要回收一个应用,必然会杀死同属于一个进程组的所有进程导致双进程无法拉起。
二、多进程音频保活方案
综上所述,上面的方法只是提高了APP后台运行存留能力,在用户不主动清理或强杀的情况下,测试APP的保活效果还是非常不错的。但是,"咕咚"在点击一键清理时奇妙的活了下来,原因是在后台循环播放一段无声音乐。如下图:
代码如下:
public class PlayerMusicService extends Service { private final static String TAG = "PlayerMusicService"; private MediaPlayer mMediaPlayer; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.silent); mMediaPlayer.setLooping(true); } @Override public int onStartCommand(Intent intent, int flags, int startId) { new Thread(new Runnable() { @Override public void run() { startPlayMusic(); } }).start(); return START_STICKY;//注释1 } private void startPlayMusic(){ if(mMediaPlayer != null){ mMediaPlayer.start(); } } private void stopPlayMusic(){ if(mMediaPlayer != null){ mMediaPlayer.stop(); } } @Override public void onDestroy() { super.onDestroy(); stopPlayMusic(); // 注释2 Intent intent = new Intent(getApplicationContext(),PlayerMusicService.class); startService(intent); }}
AndroidManifest.xml
//多进程
注释1:在onStartCommand方法中返回START_STICKY,其作用是当Service进程被kill后,系统会尝试重新创建这个Service。
注释2:在onDestory方法中重新启动自己,即Service被销毁时调用onDestory,就执行重新启动代码。
开启后台播放音频服务,此方案在各机型测试情况如下:
1、 华为Mate8(7.0) ,一键清理依然存活,在置于后台的黑屏模式下存活12小时以上;但是如果用户只选择清理此应用也会被杀死,这与"咕咚"保活效果一致。
2、三星C9(6.0),一键清理最近应用,成功保活;
3、华为4X(6.0):一键清理最近应用,成功保活;
4、三星Note4(5.0):一键清理最近应用,成功保活;
三、通过JobScheduler方案
概述
JobScheduler是谷歌在Android 5.0引入的一个能够执行特定任务的API,当条件满足时执行指定的任务。通常情况下,即使APP被强制停止,预定的任务仍然会被执行。
原理
在重写父类JobService的onStartJob的方法中执行任务,使用JobInfo的Builder方法来设定条件并和实现了JobService的子类的组件名绑定,然后调用系统服务JobScheduler的schedule方法。这样,即便在执行任务之前应用程序进程被杀,也不会导致任务不会执行,因为系统服务JobScheduler会使用bindServiceAsUser的方法把实现了JobService的子类服务启动起来,并执行它的onStartJob方法。
新建类:AliveJobService.java
@TargetApi(21)public class AliveJobService extends JobService { private static final int MESSAGE_ID_TASK = 0x01; // 告知编译器,这个变量不能被优化 private volatile static Service mKeepAliveService = null; public static boolean isJobServiceAlive(){ return mKeepAliveService != null; } private Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { if(SystemUtils.isAPPALive(getApplicationContext(), Contants.PACKAGE_NAME)){ //APP活着的 }else{ Intent intent = new Intent(getApplicationContext(), SportsActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); //APP被杀死后重启 } jobFinished( (JobParameters) msg.obj, false ); // 通知系统任务执行结束 return true; } }); @Override public boolean onStartJob(JobParameters params) { mKeepAliveService = this; Message msg = Message.obtain(mHandler, MESSAGE_ID_TASK, params); mHandler.sendMessage(msg); // 返回false,系统假设这个方法返回时任务已经执行完毕; // 返回true,系统假定这个任务正要被执行 return true; } @Override public boolean onStopJob(JobParameters params) { mHandler.removeMessages(MESSAGE_ID_TASK); return false; }}
JobScheduler管理类
执行系统任务:
JobSchedulerManager.java
public class JobSchedulerManager { private static final int JOB_ID = 1; private static JobSchedulerManager mJobManager; private JobScheduler mJobScheduler; private static Context mContext; private JobSchedulerManager(Context ctxt){ this.mContext = ctxt; mJobScheduler = (JobScheduler)ctxt.getSystemService(Context.JOB_SCHEDULER_SERVICE); } public final static JobSchedulerManager getJobSchedulerInstance(Context ctxt){ if(mJobManager == null){ mJobManager = new JobSchedulerManager(ctxt); } return mJobManager; } @TargetApi(21) public void startJobScheduler(){ if(AliveJobService.isJobServiceAlive() || isBelowLOLLIPOP()){ return; // 如果JobService已经启动或API<21,返回 } JobInfo.Builder builder = new JobInfo.Builder(JOB_ID,new ComponentName(mContext, AliveJobService.class)); builder.setPeriodic(3000); // 设置每3秒执行一下任务 builder.setPersisted(true); // 设置设备重启时,执行该任务 builder.setRequiresCharging(true); // 当插入充电器,执行该任务 JobInfo info = builder.build(); mJobScheduler.schedule(info); } @TargetApi(21) public void stopJobScheduler(){ if(isBelowLOLLIPOP()) return; mJobScheduler.cancelAll(); } private boolean isBelowLOLLIPOP(){ return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; // API< 21 }}
记得添加权限: android:permission="android.permission.BIND_JOB_SERVICE"
Doze休眠模式
谷歌在Android M(6.0)提出为了延长电池使用寿命的一种节能方式,它的核心思想是在手机处于屏幕熄灭、不插电或静止不动一段时间后,手机会自动进入Doze模式,处于Doze模式的手机将停止所有非系统应用的WalkLocks、网络访问、闹钟、GPS/WIFI扫描,包括JobSheduler活动。
当进入Doze模式的手机屏幕被点亮、移动或充电时,会立即从Doze模式恢复到正常,系统继续执行被Doze模式"冷冻"的各项活着。换而言之,Doze模式不会杀死进程,只是停止了进程相关的耗电活动,使其进入"休眠"状态。至Android N(7.0)后,谷歌进一步对Doze休眠机制进行了优化,休眠机制的应用场景和使用规则进行了加强。Doze在Android 6.0中需要将手机平行放置一段时间才能开启,在7.0中则可随时开启。
因此,对于Android 5.0,JobSheduler的唤醒是非常有效的;对于Android 6.0,虽然谷歌引入了Doze模式,但通常很难真正进入Doze模式,所以JobSheduler的唤醒依然有效;对于Android 7.0,JobSheduler的唤醒会有一定的影响,我们可以在电池管理中给APP开绿色通道,防止手机Doze模式后阻止APP使用JobSheduler功能。
测试结果
三星C9(6.0):一键清理和强制停止(force stop)都能够唤醒APP;
三星Note4(5.0):一键清理和强制停止(force stop)都能够唤醒APP;
华为荣耀4X(6.0):一键清理和强制停止(force stop)都能够唤醒APP;
华为Mate8(7.0):失效(可能被华为屏蔽了);
四、华为推送SDK
Android市场推送,包括个推、小米、极光华为Push、360、魅族等。华为推送服务让一个后台运行的Service在一个独立进程里,主程序不需要常驻内存。当该后台Service接收到push消息后会以广播的方式通知主进程,触发相关回调接口。通常,被强制停止的APP无法接收到广播,但是华为push的后台Service,可以强行将APP拉起来,这是因为其在发广播时利用了Intent.FLAG_INCLUDE_STOPPED_PACKAGES标记实现的。
MyHwPushReceiver.java
该类继承了华为接收器(com.huawei.android.pushagent.api.PushEventReceive),用于接收服务器传递过来的token,获取服务器连接状态,接收服务器推送过来的通知、透传等消息。需要注意的是,onToken方法、onPushMsg方法必须要实现,并且尽量不要在MyHwPushReceiver类中开启线程、处理Handler等。
public class MyHwPushReceiver extends PushEventReceiver{ private final static String TAG = "MyHwPushReceiver"; @Override public void onToken(Context context, String token, Bundle bundle) { Log.i(TAG,"连接到华为推送服务器,token="+token); } @Override public boolean onPushMsg(Context context, byte[] msgBytes, Bundle bundle) { Log.i(TAG,"接收透传消息:"+new String(msgBytes,"UTF-8")); // 启动应用 return false; } @Override public void onPushState(Context context, boolean connectState) { Log.i(TAG,"是否连接到华为推送服务器:"+(connectState?"connected":"disconnected")); } @Override public void onEvent(Context context, Event event, Bundle bundle) { //点击打开通知栏 super.onEvent(context, event, bundle); }}
HwPushManager.java
public class HwPushManager { private static HwPushManager mPushManager; private Context mContext; private HwPushManager(Context mContext){ this.mContext = mContext; } public static HwPushManager getInstance(Context mContext){ if(mPushManager == null){ mPushManager = new HwPushManager(mContext); } return mPushManager; } public void startRequestToken(){ //向服务器请求Token PushManager.requestToken(mContext); } //是否接收服务器传递过来的透传消息 public void isEnableReceiveNormalMsg(boolean isEnable){ PushManager.enableReceiveNormalMsg(mContext,isEnable); } //是否接收自呈现消息 public void isEnableReceiverNotifyMsg(boolean isEnable){ PushManager.enableReceiveNotifyMsg(mContext,isEnable); }}
穿透消息,即对于信息传输通道来说不会去关心透传消息的消息体格式及内容,它只是负责消息的传递,不对消息做任何处理,当客户端接收到透传消息后,由客户端自己来决定如何处理消息,比如默默在后台处理消息、以通知的方式向用户展示消息等,因此它弥补了通知栏消息客户端无法捕获到相关动作的不足。
AndroidManifest.xml
除了自定义的MyHwPushReceiver类,其他直接从华为推送官方Demo拷贝即可。通过华为开发者联盟网页中给KeepAppAlive发送透传消息的,测试华为推送保活的可行性。一般来说,我们都是在自己的服务器开一个定时器,定时推送透传消息到客户端。
注:部分华为手机可能还需要开启自启动权限;如何集成华为推送SDK,直接看官方文档即可;非华为手机需要安装"华为手机服务.apk"才能使用华为推送(有点坑)。
测试结果
参考链接:
https://blog.csdn.net/andrexpert/article/details/75174586
https://blog.csdn.net/andrexpert/article/details/75045678
更多相关文章
- 华为面试题:Android 的优势与不足
- android的消息处理机制(图文+源码分析)—Looper/Handler/Message
- 聊一聊Android的消息机制
- Android进程间通信(IPC)机制Binder简要介绍和学习计划
- cocos2d-x集成友盟消息推送SDK(Android版)
- Android消息推送完美方案
- Android之百度推送高级篇之消息【原创】
- 【Android】从主线程向子线程发消息
- Android Gradle Release Version 2.4 增加多进程并行编译,经测试