Android:进程保活

概述:

进程保活是android的一个特色,承担了很多应用上层的业务。

但随着android的发展,尤其是android 6.0+,系统对这块进行了收拢,常规的保活技术越来越难。

常规的保活技术:

1.进程保活[提升优先级,防杀]

Android系统在APP退出后台时并不会真正杀掉这个进程,而是将其缓存起来方便下次的快速启用。

内存不足的情况下,系统会依据一套Low Memory Killer 机制杀进程。

 科普:

Linux内核会为每个进程分配一个oom_adj值,代表进程的优先级。值越大,代表进程优先级越低,进程就更容易被回收,Lovw Memory Killer 就是根据这个值来据欸的那个哪个进程被回收。

不同oom_adj级别
oom_adj级别 解释
UNKNOWN_ADJ 16 预留的最低级别,一般对于缓存的进程才有可能设置这个级别。
CACHED_APP_MAX_ADJ 15 缓存进程,空进程,在内存不足的情况下就会被优先杀死。
CACHED_APP_MIN_ADJ 9 缓存进程,空进程
SERVICE_B_ADJ 8 不活跃的进程
PREVIOUS_APP_ADJ 7 切换进程
HOME_APP_ADJ 6 与Home交互的进程
SERVICE_ADK 5 有Service的进程
HEAVY_WEIGHT_APP_ADJ 4 高权重进程
BACKUP_APP_ADJ

3

正在备份的进程
PERCEPTIBLE_APP_ADJ 2 可感知的进程, 如播放音乐的进程
VISIBLE_APP_ADJ 1 可见进程,如Acitivty
FOREGROUND_APP_ADJ 0 前台进程
PERSISTENT_SERVICE_ADJ -11 重要进程
PERSISTENT_PROC_ADJ -12 核心进程
SYSTEM_ADJ -16 系统进程
NATIVE_ADJ -17 Native进程

2.利用系统通知管理权限提升保活能力

·当APP拥有通知栏管理权限时,在NotificationListenerSerivce启动后,进程oom_adj可被提升到1.

具有很强的保活能力,需要用户授权。

3.利用系统辅助功能提升保活能力

在APP获取到辅助功能权限后,进程oom_adj可被提升到1.

但是进程被杀后需要用户重新授权。

4.利用系统机制开启前台服务提升保活能力

  • API < 18 : startForeground(ID, new Notification())发送空的Notification,图标不会显示
  • API >= 18 : 需要提高优先级的service A中启动一个InnerService,2个服务同时startForeground且绑定同一个ID,停止掉InnerService,这样通知图标就会被移除。

利用系统机制开启前台服务提升保活能力在api >= 24 时候就失效了。

5.伪装成输入法提升保活能力

有些系统ROM中,可以将自己需要保活的进程伪装为输入法进程,拥有很强的保活能力,但是一般在一些系统软件能力较差的手机上有效。

6. 在后台播放无声音乐

就像前端的flash开发,使用1像素点一样。

主要是将需要保活 的进程伪装成播放音乐进程,播放音乐的进程一般都有较强的保活能力,但是一般在一些系统软件能力较差的手机上有效。

7. 进程拉活[被杀后重启]

7.1 利用系统广播拉活:

常用系统广播:

  • ACTION_BOOT_COMPLETED
  • CONNECTIVITY_ACTION
  • ACITON_USER_PRESENT

很多热门的ROM都增加了自启管理,导致无法接收广播,从而服务无法自启。

7.2 利用系统Service.START_STICKY机制拉活:

将Serive设置START_STICKY,利用系统机制在Service挂掉后将其自动拉活。

  • Service在被第一次异常杀死后会在5s内重启,第2次杀死后会在10s内重启,第3次杀死后会在20s内重启,一旦短时间内Service被杀死的次数达到5次,则系统不再拉活该Service
  • 进程被获取ROOT权限的管理工具或系统工具通过force-stop系统停掉,这时无法重启。

7.3 利用Native进程拉活:

android系统中,所有进程和系统组件的生命周期受到AMS(ActivityManagerService的统一管理。通过Linux的fork机制创建的进程为纯linux进程,其生命周期不受到Android系统的管理)

监听到主进程死亡后,通过am命令拉活主进程。

适用于android 5.0-手机,android 5.0+的手机中Native进程照样被杀。

7.4 利用AlarmManager定时拉活:

开一个定时任务,在后台隔一段时间拉起进程。【注意新系统对后台的延时影响】

7.5 利用JobScheduler机制拉活:

android 5.0+,系统提供JobScheduler,允许开发者在符合某些条件时创建在后台执行的任务。利用该机制可使得系统拉活进程。

适用5.0+,进程被强制停止后也可以进行拉活。

7.6利用账号同步机制拉活:

所有的android版本都可以,包括被强制停止后也可进行拉活,但一些自启管理的手机机型可能会不支持该方案。

7.7 利用推送SDK拉活:

一般第三方推送SDK都具备矩阵拉活能力,推送SDK的相关接入方通常都可以相互保活。

保活的谬论:

进程保活常伴随着耗电增加,android系统升级伴随的是省电。不能节能的维持进程保活的手段都是不好的。

正确的保活应当对性能、电量、用户体验足够小。

随着andorid官方的管束,保活成了越来越大的难题。

就android P而言,加入了多项目设备电量管理新特性,让app后台消息推送、应用保活变得困难。

例如android P:

1. 应用待机分组:使系统可以根据用户的使用情况而限制应用调用CPU or 网络等设备资源。

2. 后台限制:若应用出现了Android Vitals内所描述的不良行为,系统将提醒用户限制该应用访问设备资源。

3. 省电模式的优化:启用该功能后,系统将对所有应用的后台运行服务加以限制。

4. 低耗电模式:用户一段时间后没有使用设备,设备就会进入低耗电模式,所有app都将受到影响。

进程保活牵涉系统底层,本质需要对Android的文件系统有深入的了解。

关于P 、Q的限制,可查看bilibili上的goolge官方视频.

保活实操:(基于jobService)

保活分为多线程保活和应用层保活:

核心方法是onBind:

代码:

import android.annotation.TargetApi;
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.os.Handler;
import android.os.Message;
import android.util.Log;

/**
 * 一个轻量的后台job service,利用空闲时间执行一些小事情,提高进程不被回收的概率
 */
@TargetApi(value = Build.VERSION_CODES.LOLLIPOP)
public class AliveJobService extends JobService {
    private static final String TAG = AliveJobService.class.getName();

    private JobScheduler mJobScheduler;

    private Handler mJobHandler = new Handler(new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            Log.d(TAG, "pull alive.");
            jobFinished((JobParameters) msg.obj, false);
            return true;
        }
    });

    public static void start(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Intent intent = new Intent(context, AliveJobService.class);
            context.startService(intent);
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        JobInfo job = initJobInfo(startId);
        if (mJobScheduler.schedule(job) <= 0) {
            Log.d(TAG, "AliveJobService failed");
        } else {
            Log.d(TAG, "AliveJobService success");
        }
        return START_STICKY;
    }

    //开始任务
    @Override
    public boolean onStartJob(JobParameters params) {
        mJobHandler.sendMessage(Message.obtain(mJobHandler, 1, params));
        return true;
    }

    //结束任务
    @Override
    public boolean onStopJob(JobParameters params) {
        mJobHandler.sendEmptyMessage(1);
        return false;
    }

    //执行条件
    private JobInfo initJobInfo(int startId) {
        JobInfo.Builder builder = new JobInfo.Builder(startId,
                new ComponentName(getPackageName(), AliveJobService.class.getName()));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS); //执行的最小延迟时间
            builder.setOverrideDeadline(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);  //执行的最长延时时间
            builder.setBackoffCriteria(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS,
                    JobInfo.BACKOFF_POLICY_LINEAR);//线性重试方案
        } else {
            builder.setPeriodic(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);
        }
        builder.setPersisted(false);
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE);
        builder.setRequiresCharging(false);
        return builder.build();
    }
}

清单文件中:

发布了307 篇原创文章 · 获赞 45 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_39969226/article/details/104294005