Android应用进程保活
- Android应用进程保活方法介绍
在Android应用程序中,为了保证应用的正常运行和稳定性,有时需要对应用进程进行保活。以下是一些实现进程保活的方法:
- 使用前台服务(Foreground Service):将服务调用startForeground()方法,并传入一个通知对象,将该服务置于前台运行状态。这样可以使得该服务的优先级更高,从而减少被系统杀死的概率。
- 使用JobScheduler:使用setPeriodic()方法可以让应用程序周期性地执行任务,从而避免长时间占用CPU资源,setPersisted(true)方法则表示当设备重启后,该任务仍然需要继续执行。
- 使用AlarmManager:使用这个API可以让应用程序在指定的时间间隔内执行任务。例如,可以设置一个闹钟,每隔一段时间唤醒应用程序并执行一些操作。
- 使用守护进程:启动一个后台守护进程,监控应用程序的状态并在应用程序被杀死时重新启动它,使用守护进程需要申请额外的权限。
- 使用双进程保活:启动两个相互绑定的进程,在其中一个进程被杀死时,另一个进程可以重新启动它。
- 使用WorkManger: 这是目前比较新的保活机制,用于取代JobScheduler。
需要注意的是,为了避免滥用和浪费系统资源,Android系统不断升级后,已经严格限制应用程序使用过多的后台资源和保活机制。
- JobScheduler用法简介
JobScheduler是系统服务,由系统负责调度第三方应用注册的JobScheduler,定时完成指定任务。
在应用中创建一个 JobService服务,JobService需要 API Level 21以上才可以使用,该服务注册时必须声明 android.permission.BIND_JOB_SERVICE权限:
通常使用JobScheduler需要以下几个步骤:
1、获取 JobScheduler对象:通过Binder机制获取该JobScheduler系统服务;
//创建 JobScheduler JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
2、指定 JobScheduler任务信息 JobInfo:绑定任务 ID,指定任务的运行组件,也就是之前创建并注册的 JobService, 最后要设置该任务在重启后也要执行;
//第一个参数指定任务 ID //第二个参数指定任务在哪个组件中执行 // setPersisted方法需要 android.permission.RECEIVE_BOOT_COMPLETED权限 // setPersisted方法作用是设备重启后 ,依然执行 JobScheduler定时任务 JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(10, new ComponentName(context.getPackageName(), KeepAliveJobService.class.getName())) .setPersisted(true);
3、设置时间信息:7.0以下的系统可以设置间隔, 7.0以上的版本需要设置延迟执行,否则无法启动;
// 7.0 以下的版本,可以每隔 5000毫秒执行一次任务 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N){ jobInfoBuilder.setPeriodic(5_000); }else{ // 7.0 以上的版本 , 设置延迟 5 秒执行 // 该时间不能小于 JobInfo.getMinLatencyMillis 方法获取的最小值 jobInfoBuilder.setMinimumLatency(5_000); }
4、开启定时任务;
//开启定时任务 jobScheduler.schedule(jobInfoBuilder.build());
5、7.0以上的特殊处理,由于在7.0以上的系统中设置了延迟执行,需要在 JobService的 onStartJob方法中再次开启一次 JobScheduler任务执行,也就是重复上述1 ~ 4执行, 这样就实现了周期性执行的目的;
public class KeepAliveJobService extends JobService { @Override public boolean onStartJob(JobParameters params) { Log.i("KeepAliveJobService", "JobService onStartJob开启"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){ //如果当前设备大于 7.0 ,延迟 5秒 ,再次执行一次 startJob(this); } return false; } }
3.WorkManager用法简介
WorkManager是适合用于持久性工作的推荐解决方案,它可处理三种类型的持久性工作:
- 立即执行:必须立即开始且很快就完成的任务,可以加急。
- 长时间运行:运行时间可能较长(有可能超过 10分钟)的任务。
- 可延期执行:延期开始并且可以定期运行的预定任务。
通常使用WorkManager需要以下几个步骤:
- 将依赖项添加到应用的build.gradle文件中;
- 定义工作:工作使用 Worker类定义,doWork()方法在 WorkManager提供的后台线程上异步运行。如需为 WorkManager创建一些要运行的工作,则需扩展 Worker类并替换 doWork()方法;
public class XxxWorker extends Worker { publicXxxWorker( @NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @Override public Result doWork() { // Do the work here xxxxx(); // Indicate whether the work finished successfully with the Result return Result.success(); } }
3.创建 WorkRequest:定义工作后,必须使用 WorkManager服务进行调度该工作才能运行;
WorkRequest xxxWorkRequest = new OneTimeWorkRequest.Builder(XxxWorker.class) .build();
4.将 WorkRequest提交给系统:需要使用enqueue()方法将WorkRequest提交到WorkManager;
WorkManager .getInstance(myContext) .enqueue(uploadWorkRequest);
在定义工作时要考虑要考虑下面常见的需求:
4.双进程保活
双进程保活的方式就是在运行了一个主进程之外,还运行了一个 “本地前台进程”,并绑定“远程前台进程”,“远程前台进程”与“本地前台进程”实现了相同的功能,代码基本一致,这两个进程都是前台进程,都进行了提权,并且互相绑定,当监听到绑定的另外一个进程突然断开连接,则本进程再次开启前台进程提权,并且重新绑定对方进程,以达到拉活对方进程的目的。
双进程保活的实现步骤如下:
- 定义 AIDL接口 IMyAidlInterface,每个服务中都需要定义继承 IMyAidlInterface.Stub的 Binder类,作为进程间通信的桥梁(这是个默认的 AIDL接口 ),监听进程的连接断开;
// Declare any non-default types here with import statements interface IMyAidlInterface { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); }
2.实现一个判定服务运行工具:
import android.app.Activity; import android.app.ActivityManager; import android.content.Context; import android.text.TextUtils; import org.w3c.dom.Text; import java.util.List; public class ServiceUtils { /** *判定 Service是否在运行 * @param context * @return */ public static boolean isServiceRunning(Context context, String serviceName){ if(TextUtils.isEmpty(serviceName)) return false; ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); //最多获取 200个正在运行的 Service List infos = activityManager.getRunningServices(200); //遍历当前运行的 Service信息,如果找到相同名称的服务 ,说明某进程正在运行 for (ActivityManager.RunningServiceInfo info: infos){ if (TextUtils.equals(info.service.getClassName(), serviceName)){ return true; } } return false; } }
3.定义一个用于本地与远程连接的类:
class Connection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { //服务绑定成功时回调 } @Override public void onServiceDisconnected(ComponentName name) { //再次启动前台进程 startService(); //绑定另外一个远程进程 bindService(); } }
4..定义一个本地前台服务类:
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Color; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import androidx.core.app.NotificationCompat; import static androidx.core.app.NotificationCompat.PRIORITY_MIN; /** *本地前台服务 */ public class LocalForegroundService extends Service { /** *远程调用 Binder对象 */ private MyBinder myBinder; /** *连接对象 */ private Connection connection; /** * AIDL远程调用接口 *其它进程调与该 RemoteForegroundService服务进程通信时 ,可以通过 onBind方法获取该 myBinder成员 *通过调用该成员的 basicTypes方法 ,可以与该进程进行数据传递 */ class MyBinder extends IMyAidlInterface.Stub { @Override public void basicTypes( int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { //通信内容 } } @Override public IBinder onBind(Intent intent) { return myBinder; } @Override public void onCreate() { super.onCreate(); //创建 Binder对象 myBinder = new MyBinder(); //启动前台进程 startService(); } private void startService(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ // startForeground(); //创建通知通道 NotificationChannel channel = new NotificationChannel("service", "service", NotificationManager.IMPORTANCE_NONE); channel.setLightColor(Color.BLUE); channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); //正式创建 service.createNotificationChannel(channel); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "service"); Notification notification = builder.setOngoing(true) .setSmallIcon(R.mipmap.ic_launcher) .setPriority(PRIORITY_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build(); //开启前台进程 , API 26以上无法关闭通知栏 startForeground(10, notification); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){ startForeground(10, new Notification()); // API 18 ~ 25以上的设备 ,启动相同 id的前台服务 ,并关闭 ,可以关闭通知 startService(new Intent(this, CancelNotificationService.class)); } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){ // 将该服务转为前台服务 // 需要设置 ID 和 通知 // 设置 ID 为 0 , 就不显示已通知了 , 但是 oom_adj 值会变成后台进程 11 // 设置 ID 为 1 , 会在通知栏显示该前台服务 // 8.0 以上该用法报错 startForeground(10, new Notification()); } } /** * 绑定 另外一个 服务 * LocalForegroundService 与 RemoteForegroundService 两个服务互相绑定 */ private void bindService(){ // 绑定另外一个 服务 // LocalForegroundService 与 RemoteForegroundService 两个服务互相绑定 // 创建连接对象 connection = new Connection(); // 创建本地前台进程组件意图 Intent bindIntent = new Intent(this, RemoteForegroundService.class); // 绑定进程操作 bindService(bindIntent, connection, BIND_AUTO_CREATE); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // 绑定另外一个服务 bindService(); return super.onStartCommand(intent, flags, startId); } }
5.定义一个远程前台服务类:
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Color; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import androidx.core.app.NotificationCompat; import static androidx.core.app.NotificationCompat.PRIORITY_MIN; /** *远程前台服务 */ public class RemoteForegroundService extends Service { /** *远程调用 Binder对象 */ private MyBinder myBinder; /** *连接对象 */ private Connection connection; /** * AIDL远程调用接口 *其它进程调与该 RemoteForegroundService服务进程通信时 ,可以通过 onBind方法获取该 myBinder成员 *通过调用该成员的 basicTypes方法 ,可以与该进程进行数据传递 */ class MyBinder extends IMyAidlInterface.Stub { @Override public void basicTypes( int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException { //通信内容 } } @Override public IBinder onBind(Intent intent) { return myBinder; } @Override public void onCreate() { super.onCreate(); //创建 Binder对象 myBinder = new MyBinder(); //启动前台进程 startService(); } private void startService(){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ // startForeground(); //创建通知通道 NotificationChannel channel = new NotificationChannel("service", "service", NotificationManager.IMPORTANCE_NONE); channel.setLightColor(Color.BLUE); channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); //正式创建 service.createNotificationChannel(channel); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "service"); Notification notification = builder.setOngoing(true) .setSmallIcon(R.mipmap.ic_launcher) .setPriority(PRIORITY_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build(); //开启前台进程 , API 26以上无法关闭通知栏 startForeground(10, notification); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){ startForeground(10, new Notification()); // API 18 ~ 25以上的设备 ,启动相同 id的前台服务 ,并关闭 ,可以关闭通知 startService(new Intent(this, CancelNotificationService.class)); } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){ // 将该服务转为前台服务 // 需要设置 ID 和 通知 // 设置 ID 为 0 , 就不显示已通知了 , 但是 oom_adj 值会变成后台进程 11 // 设置 ID 为 1 , 会在通知栏显示该前台服务 // 8.0 以上该用法报错 startForeground(10, new Notification()); } } /** * 绑定 另外一个 服务 * LocalForegroundService 与 RemoteForegroundService 两个服务互相绑定 */ private void bindService(){ // 绑定 另外一个 服务 // LocalForegroundService 与 RemoteForegroundService 两个服务互相绑定 // 创建连接对象 connection = new Connection(); // 创建本地前台进程组件意图 Intent bindIntent = new Intent(this, LocalForegroundService.class); // 绑定进程操作 bindService(bindIntent, connection, BIND_AUTO_CREATE); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // 绑定另外一个服务 bindService(); return super.onStartCommand(intent, flags, startId); } }
6.启动两个服务:
import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startService(new Intent(this, LocalForegroundService.class)); startService(new Intent(this, RemoteForegroundService.class)); } }
5.双进程保活+JobScheduler整合方案
这种方案是在 JobService的onStartJob方法中判定“双进程保活”中的双进程是否挂了,如果这两个进程挂了,就重新将挂掉的进程重启。
这里给出一个双进程保活+JobScheduler整合方案中JobScheduler部分的示意代码,而双进程保活部分保持不变。
public class KeepAliveJobService extends JobService { @Override public boolean onStartJob(JobParameters params) { Log.i("KeepAliveJobService", "JobService onStartJob开启"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){ //如果当前设备大于 7.0 ,延迟 5秒 ,再次执行一次 startJob(this); } //判定本地前台进程是否正在运行 boolean isLocalServiceRunning = ServiceUtils.isServiceRunning(this, LocalForegroundService.class.getName()); if (!isLocalServiceRunning){ startService(new Intent(this, LocalForegroundService.class)); } //判定远程前台进程是否正在运行 boolean isRemoteServiceRunning = ServiceUtils.isServiceRunning(this, RemoteForegroundService.class.getName()); if (!isRemoteServiceRunning){ startService(new Intent(this, RemoteForegroundService.class)); } return false; } @Override public boolean onStopJob(JobParameters params) { Log.i("KeepAliveJobService", "JobService onStopJob关闭"); return false; } public static void startJob(Context context){ //创建 JobScheduler JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); //第一个参数指定任务 ID //第二个参数指定任务在哪个组件中执行 // setPersisted方法需要 android.permission.RECEIVE_BOOT_COMPLETED权限 // setPersisted方法作用是设备重启后 ,依然执行 JobScheduler定时任务 JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(10, new ComponentName(context.getPackageName(), KeepAliveJobService.class.getName())) .setPersisted(true); // 7.0以下的版本,可以每隔 5000毫秒执行一次任务 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N){ jobInfoBuilder.setPeriodic(5_000); }else{ // 7.0 以上的版本 , 设置延迟 5 秒执行 // 该时间不能小于 JobInfo.getMinLatencyMillis 方法获取的最小值 jobInfoBuilder.setMinimumLatency(5_000); } // 开启定时任务 jobScheduler.schedule(jobInfoBuilder.build()); } }
6.参考文献
1、【Android进程保活】应用进程拉活 (双进程守护 + JobScheduler保活):
https://hanshuliang.blog.csdn.net/article/details/115607584
2、【Android进程保活】应用进程拉活 (双进程守护保活 ):
https://hanshuliang.blog.csdn.net/article/details/115604667
3、【Android进程保活】应用进程拉活 ( JobScheduler拉活):
https://hanshuliang.blog.csdn.net/article/details/115584240
4、Android实现进程保活的思路:
https://blog.csdn.net/gs12software/article/details/130502312
5、WorkManager使用入门:
https://developer.android.google.cn/develop/background-work/background-tasks/persistent/getting-started
-
Android
+关注
关注
12文章
3943浏览量
127814 -
应用
+关注
关注
2文章
439浏览量
34208 -
进程
+关注
关注
0文章
204浏览量
13980
发布评论请先 登录
相关推荐
评论