引言

最近产品提出了一个非常BT的需求,说我们所做的APP要做到像微信和QQ那种即时用户清理了进程还是能收到消息的效果,当时心中就卧槽了一顿,虽然心里一万只曹尼玛翻滚着,但是功能还是要做啊,因为生活还得继续啊。
我们虽然做不到像微信、QQ这种白名单大佬(白名单指的是一些APP跟某些手机厂商合作,我们通过清理内存是杀不死这些进程的。)的效果,但是我们可以利用黑科技来尽量保证我们自己的APP不被系统杀死。现在市面上的保活和拉活方式有以下几种方案:

保活方式 Activity 1像素保活、前台服务保活
拉活方式 广播拉活、系统Service拉活、账户同步拉活、JobScheduler、双进程拉活、推送拉活、NDK fork拉活

进程被杀死无非就是由于系统内存过低,并且进程的优先级比较低,所以才会被系统kill掉,想要进程保活必须提高进程的优先级。下面是进程优先级高低图:

何时杀死进程?

内存阈值在不同的手机上不一样,一旦低于阈值,Android就会杀死对应优先级的进程,例如:当内存小于315MB(80640),就会杀死空进程。我们可以通过adb shell后输入`cat /sys/module/lowmemorykiller/parameters/minfree`来查看阈值,如下图所示(阈值的单位是4KB):![在这里插入图片描述](https://img-blog.csdnimg.cn/20190517151620712.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODIwMzY5Ng==,size_16,color_FFFFFF,t_70)
第一个值:18432(72MB)当阈值达到这个值时候,前台进程就会被杀死;
第二个值:23040(90MB)当阈值达到这个值的时候,可见进程就会被杀死;
第三个值:27648(108MB)当阈值达到这个值的时候,服务进程会被杀死;
第四个值:32256(126MB)当阈值达到这个值的时候,后台进程会被杀死;
第五个值:55296(216MB)当阈值达到这个值的时候,ContentProvider会被杀死;
第六个值:80640(315MB)当阈值达到这个值的时候,空进程会被杀死。

如何判断进程的优先级

  1. 红色部分是容易被回收的进程,属于android进程
  2. 绿色部分是较难被回收的进程,属于android进程
  3. 其他部分则不是android进程,也不会被系统回收,一般是ROM自带的app和服务才能拥有

通过oom_adj的值,判断进程的优先级,不同的手机的oom_adj的值可能不一样。oom_adj值越小,优先级越高,也就也难被杀死,我们日常开发的APP最高能达到的值是0,即前台进程。oom_adj的值我们可以通过 adb shell 直接输入cat /proc/你的process ID/oom_adj来获取(有一个前提,手机必须是root过的才能获取到值)

如何保活?

1、activity 1像素保活
第一步:创建SinglePixelActivity,如

public class SinglePixelActivity extends AppCompatActivity {private static final String TAG = "SinglePixelActivity";@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.e(TAG, "启动1像素Activity");Window window = getWindow();window.setGravity(Gravity.START | Gravity.TOP);WindowManager.LayoutParams params = window.getAttributes();params.width = 1;params.height = 1;params.x= 0;params.y = 0;window.setAttributes(params);KeepManager.getInstance().setKeepActivity(this);}@Overrideprotected void onDestroy() {super.onDestroy();FixMemLeak.fixLeak(this);Log.e(TAG, "关闭1像素Activity");}
}

第二步:创建KeepManager,和KeepReceiver用来监听屏幕是否息屏
KeepManager.class:

public class KeepManager {private static final KeepManager mInstance = new KeepManager();private KeepReceiver mKeepReceiver;private WeakReference<Activity> mKeepActivity;private KeepManager() {}public static KeepManager getInstance() {return mInstance;}public void registerKeep(Context context) {IntentFilter filter = new IntentFilter();filter.addAction(Intent.ACTION_SCREEN_ON);filter.addAction(Intent.ACTION_SCREEN_OFF);mKeepReceiver = new KeepReceiver();context.registerReceiver(mKeepReceiver, filter);}public void unRegisterKeep(Context context){if (mKeepReceiver != null) {context.unregisterReceiver(mKeepReceiver);}}public void startKeep(Context context) {Intent intent = new Intent(context, SinglePixelActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);context.startActivity(intent);}public void finishKeep() {if (mKeepActivity != null) {Activity activity = mKeepActivity.get();if (activity != null) {activity.finish();}mKeepActivity = null;}}public void setKeepActivity(Activity activity) {this.mKeepActivity = new WeakReference<>(activity);}
}

KeepReceiver.class:

public class KeepReceiver extends BroadcastReceiver {private static final String TAG = "KeepReceiver";@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();Log.e(TAG, "onReceive:" + action);if (TextUtils.equals(action,Intent.ACTION_SCREEN_ON)) {KeepManager.getInstance().finishKeep();} else if (TextUtils.equals(action,Intent.ACTION_SCREEN_ON)){KeepManager.getInstance().startKeep(context);}}
}

第三步:在MainActivity中注册监听和取消监听


第四步:在清单文件中注册广播和activity

注意:还要再定义一个Style主题,例如:

  <style name="AppTheme.SinglePixelTheme"><item name="android:windowBackground">@null</item><item name="android:windowIsTranslucent">true</item></style>

缺点:存在一个Activity不够干净,同时需要在锁屏才能监听到,如果用户一直处于亮屏状态,oom_adj的值不会变小,如果内存过小,进程会被杀死。

2、前台Service保活
启动一个前台服务,从而拉高进程的优先级:
第一步:创建一个ForegroundService类集成Service:
ForegroundService.class

public class ForegroundService extends Service {private static final String TAG = "ForegroundService";private static final int SERVICE_ID = 1;@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();Log.e(TAG, "ForegroundService 创建了");if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {   //4.3以下startForeground(SERVICE_ID, new Notification());} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {//4.3-7.0startForeground(SERVICE_ID, new Notification());startService(new Intent(this, InnerService.class));//通过启动一个inner服务隐藏通知栏} else {    //8.0以上NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);NotificationChannel channel = new NotificationChannel("wanandroid", "wanAndroid", NotificationManager.IMPORTANCE_LOW);if (manager != null) {manager.createNotificationChannel(channel);Notification notification = new NotificationCompat.Builder(this, "wanandroid").build();startForeground(SERVICE_ID, notification);}}}public static class InnerService extends Service{@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();Log.e(TAG, "InnerService 创建了");startForeground(SERVICE_ID, new Notification());stopSelf();}}}

第二步:在清单文件中注册服务;(注意:这里两个服务都需要注册)
第三步:在Activity中启动服务。
注意: ForegroundService和InnerService 的服务ID必须要一致,否则不能隐藏通知栏。

缺点:这种保活方式有一个缺点,就是在API > 26即8.0以上版本的通知栏隐藏不了。
OK,保活就讲到这里,下面我们将如何拉活进程。

**

如何拉活进程?

1、广播拉活
Android在7.0之后对广播增加了一些限制,在8.0以后就更加严格了,现在接收系统广播的拉活方式基本上已经用不了了。

2、账户同步拉活
账户同步这篇文章讲的比较全面:账户同步拉活在此我就不作多讲了

3、JobSchedule 机制拉活
JobSchedule 允许在特定状态与特定时间间隔周期执行任务。我们可以利用它的这个特点来完成保活功能,效果就像开启一个定时器,与普通定时器不同的是其调度由系统来完成。

第一步:创建MyJobService实现JobService,如:

public class MyJobService extends JobService {private static final String TAG = "MyJobService";@Overridepublic boolean onStartJob(JobParameters jobParameters) {Log.e(TAG, "onStartJob");if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {startJob(this);}return false;}@Overridepublic boolean onStopJob(JobParameters jobParameters) {return false;}public static void startJob(Context context) {JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
//        setPersisted 在设备重启依然执行JobInfo.Builder builder = new JobInfo.Builder(8, new ComponentName(context.getPackageName(), MyJobService.class.getName())).setPersisted(true);//小于7.0if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {builder.setPeriodic(1000);} else {builder.setMinimumLatency(1000);}assert jobScheduler != null;jobScheduler.schedule(builder.build());}}

第二步:在清单中注册MyJobService

  <serviceandroid:name=".jobSchedule.MyJobService"android:permission="android.permission.BIND_JOB_SERVICE" />

第三步:手动开启JobSchedule
在activity的oncreate方法中启动Service

 MyJobService.startJob(this);

4、双进程守护
双进程守护本质上是开启两个服务,一个本地服务和一个远程服务,当其中一个服务被杀死时,另一个服务会自动的把被杀死的那个服务拉活。

第一步:创建一个空的AIDL文件



2、创建本地LocalService和远程RemoteService
LocalService.class:

public class LocalService extends Service {private static final String TAG = "ProcessService";private ServiceConnection serviceConnection;private MyBinder myBinder;@Nullable@Overridepublic IBinder onBind(Intent intent) {return myBinder;}@Overridepublic void onCreate() {super.onCreate();myBinder = new MyBinder();serviceConnection = new MyServiceConnection();//让服务变成前台服务startForeground(10, new Notification());//如果api>18以上,使用相同id再启动一个前台服务,然后结束这个服务if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {startService(new Intent(this, InnerService.class));}}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {bindService(new Intent(this, RemoteService.class), serviceConnection, BIND_AUTO_CREATE);return super.onStartCommand(intent, flags, startId);}class MyBinder extends MyBinderInterface.Stub{}class MyServiceConnection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {}@Overridepublic void onServiceDisconnected(ComponentName componentName) {Log.e(TAG,"RemoteService 进程可能被干掉了,拉活");startService(new Intent(LocalService.this, RemoteService.class));bindService(new Intent(LocalService.this, RemoteService.class), serviceConnection, BIND_AUTO_CREATE);}}public static class InnerService extends Service {@Overridepublic void onCreate() {super.onCreate();startForeground(10, new Notification());stopSelf();}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}}}

RemoteService.class:

public class RemoteService extends Service{private static final String TAG = "ProcessService";private ServiceConnection serviceConnection;private MyBinder myBinder;@Nullable@Overridepublic IBinder onBind(Intent intent) {return myBinder;}@Overridepublic void onCreate() {super.onCreate();myBinder = new MyBinder();serviceConnection = new MyServiceConnection();//让服务变成前台服务startForeground(10, new Notification());//如果api>18以上,使用相同id再启动一个前台服务,然后结束这个服务if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {startService(new Intent(this, LocalService.InnerService.class));}}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {bindService(new Intent(this, LocalService.class), serviceConnection, BIND_AUTO_CREATE);return super.onStartCommand(intent, flags, startId);}class MyBinder extends MyBinderInterface.Stub{}class MyServiceConnection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {}@Overridepublic void onServiceDisconnected(ComponentName componentName) {Log.e(TAG,"LocalService 进程可能被干掉了,拉活");startService(new Intent(RemoteService.this, LocalService.class));bindService(new Intent(RemoteService.this, LocalService.class), serviceConnection, BIND_AUTO_CREATE);}}public static class InnerService extends Service {@Overridepublic void onCreate() {super.onCreate();startForeground(10, new Notification());stopSelf();}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}}
}

第三步:在清单文件中注册服务

<serviceandroid:name=".process.LocalService"android:exported="true" /><serviceandroid:name=".process.LocalService$InnerService"android:exported="true" /><serviceandroid:name=".process.RemoteService"android:exported="true"android:process=":remote" /><serviceandroid:name=".process.RemoteService$InnerService"android:exported="true" />

第四步:在Activity中的onCreate方法开启两个服务

 startService(new Intent(this, LocalService.class));startService(new Intent(this, RemoteService.class));

我们还可以利用上面的JobSchedule 进一步保活我们的进程

public class MyJobService extends JobService {private static final String TAG = "MyJobService";@Overridepublic boolean onStartJob(JobParameters jobParameters) {Log.e(TAG, "onStartJob");if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {startJob(this);}boolean isLocalRun = isServiceRunning(this, LocalService.class.getName());boolean isRemoteRun = isServiceRunning(this, RemoteService.class.getName());if (!isLocalRun || !isRemoteRun) {startService(new Intent(this, LocalService.class));startService(new Intent(this, RemoteService.class));}return false;}@Overridepublic boolean onStopJob(JobParameters jobParameters) {return false;}public static void startJob(Context context) {JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
//        setPersisted 在设备重启依然执行JobInfo.Builder builder = new JobInfo.Builder(8, new ComponentName(context.getPackageName(), MyJobService.class.getName())).setPersisted(true);//小于7.0if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {builder.setPeriodic(1000);} else {builder.setMinimumLatency(1000);}assert jobScheduler != null;jobScheduler.schedule(builder.build());}/*** 判断服务是否开启** @return*/public static boolean isServiceRunning(Context context, String ServiceName) {if (TextUtils.isEmpty(ServiceName)) {return false;}ActivityManager myManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);ArrayList<ActivityManager.RunningServiceInfo> runningService = (ArrayList<ActivityManager.RunningServiceInfo>) myManager.getRunningServices(1000);for (int i = 0; i < runningService.size(); i++) {Log.i("服务运行1:",""+runningService.get(i).service.getClassName().toString());if (runningService.get(i).service.getClassName().toString().equals(ServiceName)) {return true;}}return false;}}

**

总结

进程保活和拉活的方案到这里就结束了,随着谷歌公司对Android的安全机制做的越发的好,想要永久保活是完全不可能的,除非我们的APP可以跟一些手机厂商合作添加到白名单中才能实现永久保活, 另外系统会优先杀死占用内存多的应用,所以想让自己的APP活的更久,还可以从性能的角度上去优化,让其尽可能少的占用内存。如果有说的不对的地方可以评论指出错误的地方,谢谢!

Android进程保活、拉活方案相关推荐

  1. Android进程保活拉活

    参考:腾讯视频相关视频公开课 学习资料: 探讨Android6.0及以上系统APP常驻内存(保活)实现-争宠篇 目录 一.进程的一些基本常识 二.如何保活 三.如何拉活 四.总结 一.进程的一些基本常 ...

  2. Android进程保活方案

    自己曾经也在这个问题上伤过脑经,前几日刚好有一个北京的哥们在QQ说在做IM类的项目,问我进程保活如何处理比较恰当,决定去总结一下,网上搜索一下进程常驻的方案好多好多,但是很多的方案都是不靠谱的或者不是 ...

  3. Android进程保活方案的几种方案

    自己曾经也在这个问题上伤过脑经,前几日刚好有一个北京的哥们在QQ说在做IM类的项目,问我进程保活如何处理比较恰当,决定去总结一下,网上搜索一下进程常驻的方案好多好多,但是很多的方案都是不靠谱的或者不是 ...

  4. Android进程保活的一般套路

    自己曾经也在这个问题上伤过脑经,前几日刚好有一个北京的哥们在QQ说在做IM类的项目,问我进程保活如何处理比较恰当,决定去总结一下,网上搜索一下进程常驻的方案好多好多,但是很多的方案都是不靠谱的或者不是 ...

  5. 【Android 进程保活】应用进程拉活 ( 双进程守护 + JobScheduler 保活 | 成功率最高 | 推荐使用 )

    文章目录 一. 双进程守护保活 + JobScheduler 原理 二. 双进程守护保活 + JobScheduler 源码 1.JobService 代码 2.判定服务运行工具类 3.清单文件 4. ...

  6. 【Android 进程保活】应用进程拉活 ( 账户同步拉活 | 账号添加 | 源码资源 )

    文章目录 一. 账号添加 二. 代码示例 1. 账号添加工具类 2. 权限注册 3. 在 Activity 中调用上述工具类 4. 运行效果 三. 源码资源 一. 账号添加 在上一篇博客 [Andro ...

  7. 【Android 进程保活】应用进程拉活 ( 双进程守护保活 )

    文章目录 一. 双进程守护保活原理 二. 双进程守护保活完整源码 1.AIDL 接口 2.本地前台服务 Service 3.远程前台服务 Service 4.清单配置 5.启动两个服务 5.执行效果 ...

  8. 【Android 进程保活】应用进程拉活 ( 系统 Service 机制拉活 | Service 组件 onStartCommand 方法分析 | 源码资源 )

    文章目录 一. Service 组件 onStartCommand 方法分析 1. onStartCommand 函数返回值分析 2. onStartCommand 函数 START_STICKY_C ...

  9. Android 进程保活方案

    前言 Android 系统为了保持系统运行流畅,在内存吃紧的情况下,会将一些进程给杀掉,以释放一部分内存.然而,对于一些(如:QQ.微信等)比较重要的.我们希望能及时收到消息的App,需要保持进程持续 ...

最新文章

  1. docker学习实践之路[第一站]环境安装
  2. linux lz4 lzo,Linux六大压缩算法横评:Ubuntu 19.10最终选择LZ4
  3. [SDN] mininet walkthrough
  4. Ajax提交与传统表单提交的区别说明
  5. Linux资源控制-CPU和内存【转】
  6. XSpreadsheet-在线表格插件
  7. 常用的DIV+CSS网站布局的基本框架结构-完整版
  8. 华为手机老是android自动升级,华为手机系统怎么升级 华为手机升级系统的两种方法...
  9. sonarqube + nexus 分析项目组成员代码状况,并生成报表
  10. 大学追忆录(天空晴朗的梦)
  11. VS修改软件生成的 exe名称
  12. 使用esp32-s2模拟实现usb多点触摸
  13. 论文投稿排版时,Word首页插入连续分节符,后面的内容自动到下一页
  14. EasyWechat微信支付jsapi缺少参数 total_fee
  15. mysql成绩表_mysql--学生课程成绩表
  16. verilog基本语法学习笔记
  17. 数据中台=大数据平台+数据资产管理平台+数据服务平台
  18. 出租车管理系统的设计与实现
  19. NR PUCCH(一) PUCCH format 0/1
  20. iOS专宠WebKit,苹果正在扼杀Web应用创新?

热门文章

  1. android夜景录像模式,【图片】三种夜景拍摄手法优缺点对比,仅供参考【android吧】_百度贴吧...
  2. Java ClassCastException: xxx cannot be cast to xxx 问题4种情况解决(Java、EasyPoi、JFreeChart、EasyExcel)
  3. 顺丰控股2019财报解读
  4. Android游戏开发教程------(绘制屏幕)
  5. Excel表格中如何快速插入多个空白行
  6. 【教程】如何使用ArcGIS绘制荧光图
  7. 数组排序之冒泡法和选择法
  8. Web前端实训两天记录
  9. js函数提升和变量提升_关于在js中提升的真相
  10. 解决The number of method references in a .dex file cannot exceed 64K的问题