详细的阐述了 Android 的保活的两种解决方案 —— 由panhaos分享

做Android 保活的背景

由于之前做一个项目的时候需要让进程一直在后台活下去保持不被杀死的状态,因此也是各种百度各种苦苦寻找,本来是想着靠Service来做保活的,因为我的手机是6.0系统的,之前试过的各种依靠Service的方式均以失败告终,因此决定站在另一个角度上来解决问题,–>Android的进程。

方案一:双进程守护

其实诸如类似360杀毒软件之类的产品本身原理是通过一个一个的遍历进程,如果存活就杀死从而达到清理软件的作用的,所以我们是可以拿到自己进程和创建新的进程的。而通过AIDL的接口则可以实现跨进程通信,因此,使用双进程并通过进程间的通信是一种可行的解决方案。因此方案一是通过双进程守护来解决这个Android应用保活的。

首先是一个AIDL接口,两边的Service都要通过继承Service_1.Stub来实现AIDL接口中的方法,这里做一个空实现,目的是为了实现进程通信。接口声明如下:

  1. package com.ph.myservice;
  2.  
  3. interface Service_1 {
  4. String getName();
  5. }

然后是两个Service,为了保持连接,内部写一个内部类实现ServiceConnection的接口,当外部杀了其中一个进程的时候,会进入onDisConnection中,那么此时要做的就是start和bind另一个进程,因为Service的启动是可以多次的,所以这样是没问题的,代码如下:

  1. package com.ph.myservice;
  2.  
  3. import android.app.ActivityManager;
  4. import android.app.ActivityManager.RunningServiceInfo;
  5. import android.app.Service;
  6. import android.content.ComponentName;
  7. import android.content.Context;
  8. import android.content.Intent;
  9. import android.content.ServiceConnection;
  10. import android.os.IBinder;
  11. import android.os.RemoteException;
  12. import android.widget.Toast;
  13.  
  14. import java.util.List;
  15.  
  16. public class LocalService extends Service {
  17. private ServiceConnection conn;
  18. private MyService myService;
  19.  
  20. @Override
  21. public IBinder onBind(Intent intent) {
  22. return myService;
  23. }
  24.  
  25. @Override
  26. public void onCreate() {
  27. super.onCreate();
  28. init();
  29.  
  30. }
  31.  
  32. private void init() {
  33. if (conn == null) {
  34. conn = new MyServiceConnection();
  35. }
  36. myService = new MyService();
  37. }
  38.  
  39. @Override
  40. public int onStartCommand(Intent intent, int flags, int startId) {
  41. Toast.makeText(getApplicationContext(), "本地进程启动", Toast.LENGTH_LONG).show();
  42. Intent intents = new Intent();
  43. intents.setClass(this, RemoteService.class);
  44. bindService(intents, conn, Context.BIND_IMPORTANT);
  45. return START_STICKY;
  46. }
  47.  
  48. class MyService extends Service_1.Stub {
  49.  
  50. @Override
  51. public String getName() throws RemoteException {
  52. return null;
  53. }
  54. }
  55.  
  56. class MyServiceConnection implements ServiceConnection {
  57.  
  58. @Override
  59. public void onServiceConnected(ComponentName name, IBinder service) {
  60. System.out.println("获取连接");
  61.  
  62. }
  63.  
  64. @Override
  65. public void onServiceDisconnected(ComponentName name) {
  66. Toast.makeText(LocalService.this, "远程连接被干掉了", Toast.LENGTH_SHORT).show();
  67. LocalService.this.startService(new Intent(LocalService.this,
  68. RemoteService.class));
  69. LocalService.this.bindService(new Intent(LocalService.this,
  70. RemoteService.class), conn, Context.BIND_IMPORTANT);
  71.  
  72. }
  73.  
  74. }
  75.  
  76. }

远程服务类如下:

  1. package com.ph.myservice;
  2.  
  3. import android.app.Service;
  4. import android.content.ComponentName;
  5. import android.content.Context;
  6. import android.content.Intent;
  7. import android.content.ServiceConnection;
  8. import android.os.IBinder;
  9. import android.os.RemoteException;
  10. import android.widget.Toast;
  11.  
  12. public class RemoteService extends Service {
  13. private MyBinder binder;
  14. private ServiceConnection conn;
  15.  
  16. @Override
  17. public void onCreate() {
  18. super.onCreate();
  19. // System.out.println("远程进程开启");
  20. init();
  21.  
  22. }
  23.  
  24. @Override
  25. public int onStartCommand(Intent intent, int flags, int startId) {
  26. Toast.makeText(getApplicationContext(), "远程进程启动", Toast.LENGTH_LONG).show();
  27. Intent intents = new Intent();
  28. intents.setClass(this, LocalService.class);
  29. bindService(intents, conn, Context.BIND_IMPORTANT);
  30. return START_STICKY;
  31. }
  32.  
  33. private void init() {
  34. if (conn == null) {
  35. conn = new MyConnection();
  36. }
  37. binder = new MyBinder();
  38. }
  39.  
  40. @Override
  41. public IBinder onBind(Intent intent) {
  42. return binder;
  43. }
  44.  
  45. static class MyBinder extends Service_1.Stub {
  46.  
  47. @Override
  48. public String getName() throws RemoteException {
  49. return "远程连接";
  50. }
  51. }
  52.  
  53. class MyConnection implements ServiceConnection {
  54.  
  55. @Override
  56. public void onServiceConnected(ComponentName name, IBinder service) {
  57. System.out.println("获取远程连接");
  58. }
  59.  
  60. @Override
  61. public void onServiceDisconnected(ComponentName nme) {
  62. Toast.makeText(RemoteService.this, "本地连接被干掉了", Toast.LENGTH_SHORT).show();
  63. RemoteService.this.startService(new Intent(RemoteService.this,
  64. LocalService.class));
  65. RemoteService.this.bindService(new Intent(RemoteService.this,
  66. LocalService.class), conn, Context.BIND_IMPORTANT);
  67. }
  68. }
  69.  
  70. }

布局文件里要加上声明

  1. <service android:name=".LocalService" />
  2. <service
  3. android:name=".RemoteService"
  4. android:process=":remote" />

实际情况我个人测试,在5.0以下的模拟器上是没问题的,不管多次从系统的进程里kill掉,也还是会重新启动tos,但是5.0以上这种方法是无效的,5.0以上Android应该是意识到了这种双进程守护的方式,因此修改了一下源码,让这种双进程保活应用的方式无效。因此,针对5.0以上,我们采用另一种方案。

方案二:JobScheduler执行任务调度保活

JobScheduler这个类是21版本google新出来的api,我们看他的文档可以知道大致这个类的作用如下:

  1. 框架将智能当你收到你的回调,并尝试批并尽可能推迟。通常如果你不指定期限在你的工作,它可以运行在任何时候根据作业调度器的当前状态的内部队列,然而它可能是延迟只要直到下一次设备被连接到一个电源。

这个任务其实是在设备空闲期执行的,而且系统设计的这个api不会很耗电,本意是用来执行一些任务调度的,但是我们设想一下,如果用这个类来执行我们的开启双进程,那么也是一定会在设备空闲期执行的,因此我们写一个类继承JobService,在onstart里声明创建JobScheduler对象,并设置多就执行一次和开机自启动,这样就能确保及时在息屏状态,也能够执行重启进程,所以我们在JobService的onStopJob方法里判断我们的进程是否被回收了,如果被回收了就重启进程,这样子就可以实现5.0以上的进程保活了。具体代码如下:

  1. package com.ph.myservice;
  2.  
  3. import android.annotation.SuppressLint;
  4. import android.app.ActivityManager;
  5. import android.app.job.JobInfo;
  6. import android.app.job.JobParameters;
  7. import android.app.job.JobScheduler;
  8. import android.app.job.JobService;
  9. import android.content.ComponentName;
  10. import android.content.Context;
  11. import android.content.Intent;
  12. import android.os.Build;
  13. import android.widget.Toast;
  14.  
  15. import java.util.List;
  16.  
  17. /**
  18. * Created by 86119 on 2017/1/6.
  19. */
  20.  
  21. @SuppressLint("NewApi")
  22. public class JobHandlerService extends JobService {
  23. private JobScheduler mJobScheduler;
  24.  
  25. @Override
  26. public int onStartCommand(Intent intent, int flags, int startId) {
  27. System.out.println("服务被创建");
  28.  
  29. // startService(new Intent(this, LocalService.class));
  30. // startService(new Intent(this, RemoteService.class));
  31.  
  32. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  33. mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
  34. JobInfo.Builder builder = new JobInfo.Builder(startId++,
  35. new ComponentName(getPackageName(), JobHandlerService.class.getName()));
  36.  
  37. builder.setPeriodic(5000); //每隔5秒运行一次
  38. builder.setRequiresCharging(true);
  39. builder.setPersisted(true); //设置设备重启后,是否重新执行任务
  40. builder.setRequiresDeviceIdle(true);
  41.  
  42. if (mJobScheduler.schedule(builder.build()) <= 0) {
  43. //If something goes wrong
  44. System.out.println("工作失败");
  45. } else {
  46. System.out.println("工作成功");
  47. }
  48. }
  49. return START_STICKY;
  50. }
  51.  
  52. @Override
  53. public boolean onStartJob(JobParameters params) {
  54.  
  55. Toast.makeText(this, "服务启动", Toast.LENGTH_SHORT).show();
  56. // || isServiceRunning(this, "com.ph.myservice.RemoteService") == false
  57. System.out.println("开始工作");
  58. // if (!isServiceRunning(getApplicationContext(), "com.ph.myservice") || !isServiceRunning(getApplicationContext(), "com.ph.myservice:remote")) {
  59. // startService(new Intent(this, LocalService.class));
  60. // startService(new Intent(this, RemoteService.class));
  61. // }
  62.  
  63. /* boolean serviceRunning = isServiceRunning(getApplicationContext(), "com.ph.myservice");
  64. System.out.println("进程一" + serviceRunning);
  65.  
  66. boolean serviceRunning2 = isServiceRunning(getApplicationContext(), "com.ph.myservice:remote");
  67. System.out.println("进程二" + serviceRunning2);*/
  68. return false;
  69. }
  70.  
  71. @Override
  72. public boolean onStopJob(JobParameters params) {
  73. if (!isServiceRunning(this, "com.ph.myservice.LocalService") || !isServiceRunning(this, "com.ph.myservice.RemoteService")) {
  74. startService(new Intent(this, LocalService.class));
  75. startService(new Intent(this, RemoteService.class));
  76. }
  77. return false;
  78. }
  79.  
  80. // 服务是否运行
  81. public boolean isServiceRunning(Context context, String serviceName) {
  82. boolean isRunning = false;
  83. ActivityManager am = (ActivityManager) this
  84. .getSystemService(Context.ACTIVITY_SERVICE);
  85. List<ActivityManager.RunningAppProcessInfo> lists = am.getRunningAppProcesses();
  86.  
  87. for (ActivityManager.RunningAppProcessInfo info : lists) {// 获取运行服务再启动
  88. System.out.println(info.processName);
  89. if (info.processName.equals(serviceName)) {
  90. isRunning = true;
  91. }
  92. }
  93. return isRunning;
  94.  
  95. }
  96.  
  97. }
  98. package com.ph.myservice;
  99.  
  100. import android.content.Intent;
  101. import android.os.Build;
  102. import android.os.Bundle;
  103. import android.support.v7.app.AppCompatActivity;
  104.  
  105. public class MainActivity extends AppCompatActivity {
  106.  
  107. @Override
  108. protected void onCreate(Bundle savedInstanceState) {
  109. super.onCreate(savedInstanceState);
  110. setContentView(R.layout.activity_main);
  111.  
  112. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  113. openJobService();
  114. } else {
  115. openTwoService();
  116. }
  117.  
  118. }
  119.  
  120. private void openTwoService() {
  121. startService(new Intent(this, LocalService.class));
  122. startService(new Intent(this, RemoteService.class));
  123. }
  124.  
  125. private void openJobService() {
  126.  
  127. Intent intent = new Intent();
  128. intent.setClass(MainActivity.this, JobHandlerService.class);
  129. startService(intent);
  130.  
  131. }
  132. }

经过我6.0系统的华为真机测试是没有问题的,就算处于息屏状态进程也还是活着的,不管过多久打开屏幕还是会tos,并且关机了开机也会tos。以上就是我的Android保活的两种方案啦,其实还有更深入的几种方案,但是涉及到ndk层和Linux层我目前也没做,就不在这里说啦。

Android 的保活的两种解决方案的更多相关文章

  1. PHP中实现MySQL嵌套事务的两种解决方案

    PHP中实现MySQL嵌套事务的两种解决方案 一.问题起源 在MySQL的官方文档中有明确的说明不支持嵌套事务: Transactions cannot be nested. This is a co ...

  2. Android 抗锯齿的两种方法

    Android 抗锯齿的两种方法 (其一:paint.setAntiAlias(ture);paint.setBitmapFilter(true))   在Android中,目前,我知道有两种出现锯齿 ...

  3. javascript文件夹选择框的两种解决方案

    javascript文件夹选择框的两种解决方案 解决方案1:调用windows 的shell,但会有安全问题. * browseFolder.js * 该文件定义了BrowseFolder()函数,它 ...

  4. android emulator启动的两种方法详解

    android emulator启动的两种方法详解    转https://blog.csdn.net/TTS_Kevin/article/details/7452237 对于android学习者,模 ...

  5. Android中BroadcastReceiver的两种注册方式(静态和动态)详解

    今天我们一起来探讨下安卓中BroadcastReceiver组件以及详细分析下它的两种注册方式. BroadcastReceiver也就是"广播接收者"的意思,顾名思义,它就是用来 ...

  6. Android中Fragment的两种创建方式

    fragment是Activity中用户界面的一个行为或者是一部分.你可以在一个单独的Activity上把多个Fragment组合成为一个多区域的UI,并且可以在多个Activity中再使用.你可以认 ...

  7. Android请求服务器的两种方式--post, get的区别

    android中用get和post方式向服务器提交请求_疯狂之桥_新浪博客http://blog.sina.com.cn/s/blog_a46817ff01017yxt.html Android提交数 ...

  8. Android TextView里显示两种颜色

    今天介绍一个小技巧,在Android的TextView里设置两种颜色,直接上代码: TextView TV = (TextView)findViewById(R.id.mytextview01); S ...

  9. Android访问WebService的两种方法

    首先解释一下WebService:WebService是一种基于SOAP协议的远程调用标准.通过WebService可以将不同操作系统平台,不同语言.不同技术整合到一起.详细见:http://baik ...

随机推荐

  1. jQuery-laye插件实现 弹框编辑,异步验证,form验证提交

    代码中用到了 jQuery的ajax异步处理,each()循环,submit()页面验证提交form表单,prepend()追加标签,laye插件的弹框效果(如有其它弹框效果可参考官网:http:// ...

  2. 生产-消费模式的synchronized和lock实现(十)

    lock: package com.net.thread.lock; import java.util.concurrent.locks.Condition; import java.util.con ...

  3. 文件/etc/passwd,/etc/shadow,/etc/group

    文件/etc/passwd /etc/shadow /etc/group 计算资源的使用(并不是所有的人都可以用这台计算机的) 权限:访问资源的的能力. 用户:获取资源或者权限的凭证. 用户的容器:关 ...

  4. linux处理僵尸进程

    由来 在linux下,如果一个进程终止,内核会释放该进程使用的所有存储区,关闭所有文件句柄等,但是,内核会为每个终止子进程保留一定量的信息.这些信息至少包括进程ID,进程的终止状态,以及该进程使用的C ...

  5. 裸机——I2C 2

    前面的随笔完成了I2C时序分析(不涉及仲裁) 现在可以学使用控制器的I2C了. 1.先回顾I2C的基础知识 (1)总线包括SCL + SDA. (2)通信的特点: 同步,串行,电平 所以决定了 I2C ...

  6. 笔记-爬虫-scrapy-srcapy-redis组件

    笔记-爬虫-scrapy-srcapy-redis组件 1.      简介 scrapy是一个爬虫框架,但不支持分布式,scrapy-redis是为了更方便的实现scrapy分布式爬虫的组件. 可以 ...

  7. 初见spark-04(高级算子)

    今天,这个是spark的高级算子的讲解的最后一个章节,今天我们来介绍几个简单的算子, countByKey val rdd1 = sc.parallelize(List(("a", ...

  8. 1,MongoDB简介和安装

    一.初识MongoDB MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介于关系数据库和非关系数 ...

  9. Postman-简单使用(1)

    Postman-简单使用(1) Postman-简单使用 Postman-进阶使用 Postman-CI集成Jenkins Postman功能(https://www.getpostman.com/f ...

  10. AS3项目基础框架搭建分享robotlegs2 + starling1.3 + feathers1.1

    这个框架和我之前使用robotlegs1版本的大体相同,今天要写一个新的聊天软件就把之前的框架升级到了2.0并且把代码整理了一下. 使用适配器模式使得starling的DisplayObject和fl ...