Android 应用进程保活
在Android应用程序中,为了保证应用的正常运行和稳定性,有时需要对应用进程进行保活。以下是一些实现进程保活的方法:
需要注意的是,为了避免滥用和浪费系统资源,Android系统不断升级后,已经严格限制应用程序使用过多的后台资源和保活机制。
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; } }
WorkManager 是适合用于持久性工作的推荐解决方案,它可处理三种类型的持久性工作:
通常使用WorkManager 需要以下几个步骤:
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);
在定义工作时要考虑要考虑下面常见的需求:
双进程保活的方式就是在运行了一个主进程之外,还运行了一个 “本地前台进程”,并绑定“远程前台进程”,“远程前台进程”与“本地前台进程”实现了相同的功能,代码基本一致,这两个进程都是前台进程,都进行了提权,并且互相绑定,当监听到绑定的另外一个进程突然断开连接,则本进程再次开启前台进程提权,并且重新绑定对方进程,以达到拉活对方进程的目的。
双进程保活的实现步骤如下:
// 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)); } }
这种方案是在 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()); } }
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
全部0条评论
快来发表一下你的评论吧 !