Android 的保活的两种解决方案
做Android 保活的背景
由于之前做一个项目的时候需要让进程一直在后台活下去保持不被杀死的状态,因此也是各种百度各种苦苦寻找,本来是想着靠Service来做保活的,因为我的手机是6.0系统的,之前试过的各种依靠Service的方式均以失败告终,因此决定站在另一个角度上来解决问题,–>Android的进程。
方案一:双进程守护
其实诸如类似360杀毒软件之类的产品本身原理是通过一个一个的遍历进程,如果存活就杀死从而达到清理软件的作用的,所以我们是可以拿到自己进程和创建新的进程的。而通过AIDL的接口则可以实现跨进程通信,因此,使用双进程并通过进程间的通信是一种可行的解决方案。因此方案一是通过双进程守护来解决这个Android应用保活的。
首先是一个AIDL接口,两边的Service都要通过继承Service_1.Stub来实现AIDL接口中的方法,这里做一个空实现,目的是为了实现进程通信。接口声明如下:
package com.ph.myservice; interface Service_1 { String getName(); }
然后是两个Service,为了保持连接,内部写一个内部类实现ServiceConnection的接口,当外部杀了其中一个进程的时候,会进入onDisConnection中,那么此时要做的就是start和bind另一个进程,因为Service的启动是可以多次的,所以这样是没问题的,代码如下:
package com.ph.myservice; import android.app.ActivityManager; import android.app.ActivityManager.RunningServiceInfo; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; import android.widget.Toast; import java.util.List; public class LocalService extends Service { private ServiceConnection conn; private MyService myService; @Override public IBinder onBind(Intent intent) { return myService; } @Override public void onCreate() { super.onCreate(); init(); } private void init() { if (conn == null) { conn = new MyServiceConnection(); } myService = new MyService(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(getApplicationContext(), "本地进程启动", Toast.LENGTH_LONG).show(); Intent intents = new Intent(); intents.setClass(this, RemoteService.class); bindService(intents, conn, Context.BIND_IMPORTANT); return START_STICKY; } class MyService extends Service_1.Stub { @Override public String getName() throws RemoteException { return null; } } class MyServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { System.out.println("获取连接"); } @Override public void onServiceDisconnected(ComponentName name) { Toast.makeText(LocalService.this, "远程连接被干掉了", Toast.LENGTH_SHORT).show(); LocalService.this.startService(new Intent(LocalService.this, RemoteService.class)); LocalService.this.bindService(new Intent(LocalService.this, RemoteService.class), conn, Context.BIND_IMPORTANT); } } }
远程服务类如下:
package com.ph.myservice; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.RemoteException; import android.widget.Toast; public class RemoteService extends Service { private MyBinder binder; private ServiceConnection conn; @Override public void onCreate() { super.onCreate(); // System.out.println("远程进程开启"); init(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(getApplicationContext(), "远程进程启动", Toast.LENGTH_LONG).show(); Intent intents = new Intent(); intents.setClass(this, LocalService.class); bindService(intents, conn, Context.BIND_IMPORTANT); return START_STICKY; } private void init() { if (conn == null) { conn = new MyConnection(); } binder = new MyBinder(); } @Override public IBinder onBind(Intent intent) { return binder; } static class MyBinder extends Service_1.Stub { @Override public String getName() throws RemoteException { return "远程连接"; } } class MyConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { System.out.println("获取远程连接"); } @Override public void onServiceDisconnected(ComponentName nme) { Toast.makeText(RemoteService.this, "本地连接被干掉了", Toast.LENGTH_SHORT).show(); RemoteService.this.startService(new Intent(RemoteService.this, LocalService.class)); RemoteService.this.bindService(new Intent(RemoteService.this, LocalService.class), conn, Context.BIND_IMPORTANT); } } }
布局文件里要加上声明
<service android:name=".LocalService" /> <service android:name=".RemoteService" android:process=":remote" />
实际情况我个人测试,在5.0以下的模拟器上是没问题的,不管多次从系统的进程里kill掉,也还是会重新启动tos,但是5.0以上这种方法是无效的,5.0以上Android应该是意识到了这种双进程守护的方式,因此修改了一下源码,让这种双进程保活应用的方式无效。因此,针对5.0以上,我们采用另一种方案。
方案二:JobScheduler执行任务调度保活
JobScheduler这个类是21版本google新出来的api,我们看他的文档可以知道大致这个类的作用如下:
框架将智能当你收到你的回调,并尝试批并尽可能推迟。通常如果你不指定期限在你的工作,它可以运行在任何时候根据作业调度器的当前状态的内部队列,然而它可能是延迟只要直到下一次设备被连接到一个电源。
这个任务其实是在设备空闲期执行的,而且系统设计的这个api不会很耗电,本意是用来执行一些任务调度的,但是我们设想一下,如果用这个类来执行我们的开启双进程,那么也是一定会在设备空闲期执行的,因此我们写一个类继承JobService,在onstart里声明创建JobScheduler对象,并设置多就执行一次和开机自启动,这样就能确保及时在息屏状态,也能够执行重启进程,所以我们在JobService的onStopJob方法里判断我们的进程是否被回收了,如果被回收了就重启进程,这样子就可以实现5.0以上的进程保活了。具体代码如下:
package com.ph.myservice; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.os.Build; import android.widget.Toast; import java.util.List; /** * Created by 86119 on 2017/1/6. */ @SuppressLint("NewApi") public class JobHandlerService extends JobService { private JobScheduler mJobScheduler; @Override public int onStartCommand(Intent intent, int flags, int startId) { System.out.println("服务被创建"); // startService(new Intent(this, LocalService.class)); // startService(new Intent(this, RemoteService.class)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); JobInfo.Builder builder = new JobInfo.Builder(startId++, new ComponentName(getPackageName(), JobHandlerService.class.getName())); builder.setPeriodic(5000); //每隔5秒运行一次 builder.setRequiresCharging(true); builder.setPersisted(true); //设置设备重启后,是否重新执行任务 builder.setRequiresDeviceIdle(true); if (mJobScheduler.schedule(builder.build()) <= 0) { //If something goes wrong System.out.println("工作失败"); } else { System.out.println("工作成功"); } } return START_STICKY; } @Override public boolean onStartJob(JobParameters params) { Toast.makeText(this, "服务启动", Toast.LENGTH_SHORT).show(); // || isServiceRunning(this, "com.ph.myservice.RemoteService") == false System.out.println("开始工作"); // if (!isServiceRunning(getApplicationContext(), "com.ph.myservice") || !isServiceRunning(getApplicationContext(), "com.ph.myservice:remote")) { // startService(new Intent(this, LocalService.class)); // startService(new Intent(this, RemoteService.class)); // } /* boolean serviceRunning = isServiceRunning(getApplicationContext(), "com.ph.myservice"); System.out.println("进程一" + serviceRunning); boolean serviceRunning2 = isServiceRunning(getApplicationContext(), "com.ph.myservice:remote"); System.out.println("进程二" + serviceRunning2);*/ return false; } @Override public boolean onStopJob(JobParameters params) { if (!isServiceRunning(this, "com.ph.myservice.LocalService") || !isServiceRunning(this, "com.ph.myservice.RemoteService")) { startService(new Intent(this, LocalService.class)); startService(new Intent(this, RemoteService.class)); } return false; } // 服务是否运行 public boolean isServiceRunning(Context context, String serviceName) { boolean isRunning = false; ActivityManager am = (ActivityManager) this .getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> lists = am.getRunningAppProcesses(); for (ActivityManager.RunningAppProcessInfo info : lists) {// 获取运行服务再启动 System.out.println(info.processName); if (info.processName.equals(serviceName)) { isRunning = true; } } return isRunning; } } package com.ph.myservice; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { openJobService(); } else { openTwoService(); } } private void openTwoService() { startService(new Intent(this, LocalService.class)); startService(new Intent(this, RemoteService.class)); } private void openJobService() { Intent intent = new Intent(); intent.setClass(MainActivity.this, JobHandlerService.class); startService(intent); } }
经过我6.0系统的华为真机测试是没有问题的,就算处于息屏状态进程也还是活着的,不管过多久打开屏幕还是会tos,并且关机了开机也会tos。以上就是我的Android保活的两种方案啦,其实还有更深入的几种方案,但是涉及到ndk层和Linux层我目前也没做,就不在这里说啦。