0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【软件干货】Android应用进程如何保活?

武汉万象奥科 2024-10-15 17:05 次阅读

Android应用进程保活

  1. Android应用进程保活方法介绍

在Android应用程序中,为了保证应用的正常运行和稳定性,有时需要对应用进程进行保活。以下是一些实现进程保活的方法:

  1. 使用前台服务(Foreground Service):将服务调用startForeground()方法,并传入一个通知对象,将该服务置于前台运行状态。这样可以使得该服务的优先级更高,从而减少被系统杀死的概率。
  2. 使用JobScheduler:使用setPeriodic()方法可以让应用程序周期性地执行任务,从而避免长时间占用CPU资源,setPersisted(true)方法则表示当设备重启后,该任务仍然需要继续执行。
  3. 使用AlarmManager:使用这个API可以让应用程序在指定的时间间隔内执行任务。例如,可以设置一个闹钟,每隔一段时间唤醒应用程序并执行一些操作。
  4. 使用守护进程:启动一个后台守护进程,监控应用程序的状态并在应用程序被杀死时重新启动它,使用守护进程需要申请额外的权限。
  5. 使用双进程保活:启动两个相互绑定的进程,在其中一个进程被杀死时,另一个进程可以重新启动它。
  6. 使用WorkManger: 这是目前比较新的保活机制,用于取代JobScheduler。

需要注意的是,为了避免滥用和浪费系统资源,Android系统不断升级后,已经严格限制应用程序使用过多的后台资源和保活机制。

  1. 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是适合用于持久性工作的推荐解决方案,它可处理三种类型的持久性工作:

  1. 立即执行:必须立即开始且很快就完成的任务,可以加急。
  2. 长时间运行:运行时间可能较长(有可能超过 10分钟)的任务。
  3. 可延期执行:延期开始并且可以定期运行的预定任务。

通常使用WorkManager需要以下几个步骤:

  1. 将依赖项添加到应用的build.gradle文件中;
  2. 定义工作:工作使用 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);

在定义工作时要考虑要考虑下面常见的需求:

  1. 调度一次性工作还是重复性工作;
  2. 工作约束条件是怎样的,例如要求连接到 Wi-Fi网络或正在充电;
  3. 确保至少延迟一定时间再执行工作;
  4. 设置重试和退避策略;
  5. 输入数据如何传递给工作等等。

4.双进程保活

双进程保活的方式就是在运行了一个主进程之外,还运行了一个 “本地前台进程”,并绑定“远程前台进程”,“远程前台进程”与“本地前台进程”实现了相同的功能,代码基本一致,这两个进程都是前台进程,都进行了提权,并且互相绑定,当监听到绑定的另外一个进程突然断开连接,则本进程再次开启前台进程提权,并且重新绑定对方进程,以达到拉活对方进程的目的。

双进程保活的实现步骤如下:

  1. 定义 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

    文章

    3908

    浏览量

    126898
  • 应用
    +关注

    关注

    2

    文章

    438

    浏览量

    34108
  • 进程
    +关注

    关注

    0

    文章

    198

    浏览量

    13933
收藏 人收藏

    评论

    相关推荐

    进程和线程的概念及其区别

    今天浩道跟大家分享一篇关于进程与线程之间关联的硬核干货,看看大神如何通过通俗易懂的图文,让大家更加深刻理解进程与线程的区别!
    的头像 发表于 11-21 10:50 811次阅读
    <b class='flag-5'>进程</b>和线程的概念及其区别

    cyw43012的低功耗是如何应用的?

    1、目前cyw43012的低功耗是如何应用的如果主控不是psoc,是君正或者rk之类的arm平台可以做远程唤醒吗。 2、cyw43012可以开放43012 rtos的部分开发吗,如下虚拟网卡或者sdio裸数据传输的应用是否支持。 谢谢
    发表于 03-01 07:09

    华清远见Android学习资料

    华清远见Android学习资料《Android进程与生命周期》Android根据应用程序的组件及组件当前运行状态将所有的进程按重要性程度从高
    发表于 04-11 14:34

    华清远见Android学习资料

    华清远见Android学习资料《Android进程与生命周期》Android根据应用程序的组件及组件当前运行状态将所有的进程按重要性程度从高
    发表于 04-11 14:36

    干货Android之蓝牙驱动开发经验

    干货Android之蓝牙驱动开发经验目录一 Bluetooth基本概念1二 Android Bluetooth架构12.1 Bluetooth架构图12.2 Bluetooth代码层次结构3三
    发表于 02-29 15:53

    嵌入式系统死锁和锁含义理解

    计算,然后释放它占用的资源,从而保证了系统中的所有进程都能完成,所以可避免死锁的发生。锁(英文 livelock),指事物1可以使用资源,但它让其他事物先使用资源;事物2可以使用资源,但它也让其他
    发表于 09-14 17:19

    Android双应用进程Demo程序设计

    应用进程为特色的Android工控应用方案,并在ESM6802工控主板上加以实现。具体说来,就是在Linux平台上运行一个直接操作硬件接口的控制通讯管理进程,为保证运行效率,该进程采用
    发表于 08-24 11:21

    Android双应用进程Demo程序设计思路

    Android双应用进程Demo程序设计
    发表于 09-26 08:58

    android--系统启动--init进程启动过程如何

    android--系统启动--init进程启动过程
    发表于 05-29 10:35

    强制结束进程工具软件下载

    强制结束进程工具软件下载 本压缩包包括四个文件:   prockiller.exe 强制结束进程的工具,无功能限制。[已经脱壳]  prockiller_help.htm
    发表于 01-03 22:32 10次下载

    Android平台的校园导览软件设计

    Android平台的校园导览软件设计
    发表于 10-31 10:34 13次下载
    <b class='flag-5'>Android</b>平台的校园导览<b class='flag-5'>软件</b>设计

    Android恶意软件判定方法

    针对当前Android平台资源受限及恶意软件检测能力不足这一问题,以现有Android安装方式、触发方式和恶意负载方面的行为特征为识别基础,构建了基于ROM定制的Android
    发表于 12-20 10:15 0次下载
    <b class='flag-5'>Android</b>恶意<b class='flag-5'>软件</b>判定方法

    基于Bagging-SVM的Android恶意软件检测模型

    恶意软件具有隐蔽性强、窃取用户隐私和恶意扣费等特点,给Android手机用户带来日益严重的安全威胁,Android手机的安全形势日益严峻。Android恶意
    发表于 04-17 15:57 0次下载

    英创信息技术Android双应用进程Demo程序设计

    应用进程为特色的Android工控应用方案,并在ESM6802工控主板上加以实现。具体说来,就是在Linux平台上运行一个直接操作硬件接口的控制通讯管理进程,为保证运行效率,该进程采用
    的头像 发表于 02-06 11:27 779次阅读
    英创信息技术<b class='flag-5'>Android</b>双应用<b class='flag-5'>进程</b>Demo程序设计

    简单的移动电源模块

    电子发烧友网站提供《简单的移动电源模块.zip》资料免费下载
    发表于 11-22 09:47 0次下载
    简单的移动电源<b class='flag-5'>保</b><b class='flag-5'>活</b>模块