红橙Darren视频笔记 App保活-双进程守护与JobService

App为什么会被杀死

一般情况App被杀有以下几种情况
1 手机内存不足,系统需要花费更多资源去运行优先级较高的应用
2 第三方的管理软件比如360,腾讯管家,清理进程也可能会杀死进程

Android系统会优先杀死进程优先级较低的应用,如果应用的优先级相同,那么系统会优先杀死占用内存较多的应用。
所以要使得我们的应用长时间保活,正常的做法是提高优先级以及优化内存使用,不要出现内存泄漏等现象。

Android进程优先级:
以下内容拷贝自Android 官网
https://developer.android.com/guide/components/activities/process-lifecycle

为了确定在内存不足时应该终止哪些进程,Android
会根据每个进程中运行的组件以及这些组件的状态,将它们放入“重要性层次结构”。这些进程类型包括(按重要性排序):

1.A 前台进程是用户目前执行操作所需的进程。在不同的情况下,进程可能会因为其所包含的各种应用组件而被视为前台进程。如果以下任一条件成立,则进程会被认为位于前台:
它正在用户的互动屏幕上运行一个 Activity(其 onResume() 方法已被调用)。 它有一个 BroadcastReceiver
目前正在运行(其 BroadcastReceiver.onReceive() 方法正在执行)。 它有一个 Service
目前正在执行其某个回调(Service.onCreate()、Service.onStart() 或
Service.onDestroy())中的代码。
系统中只有少数此类进程,而且除非内存过低,导致连这些进程都无法继续运行,才会在最后一步终止这些进程。通常,此时设备已达到内存分页状态,因此必须执行此操作才能使用户界面保持响应。

2.可见进程正在进行用户当前知晓的任务,因此终止该进程会对用户体验造成明显的负面影响。在以下条件下,进程将被视为可见: 它正在运行的 Activity 在屏幕上对用户可见,但不在前台(其 onPause() 方法已被调用)。举例来说,如果前台 Activity
显示为一个对话框,而这个对话框允许在其后面看到上一个 Activity,则可能会出现这种情况。 它有一个 Service 正在通过
Service.startForeground()(要求系统将该服务视为用户知晓或基本上对用户可见的服务)作为前台服务运行。
系统正在使用其托管的服务实现用户知晓的特定功能,例如动态壁纸、输入法服务等。
相比前台进程,系统中运行的这些进程数量较不受限制,但仍相对受控。这些进程被认为非常重要,除非系统为了使所有前台进程保持运行而需要终止它们,否则不会这么做。

3.服务流程包含一个已使用 startService() 方法启动的 Service。虽然用户无法直接看到这些进程,但它们通常正在执行用户关心的任务(例如后台网络数据上传或下载),因此系统会始终使此类进程保持运行,除非没有足够的内存来保留所有前台和可见进程。
已经运行了很长时间(例如 30 分钟或更长时间)的服务的重要性可能会降位,以使其进程降至下文所述的缓存 LRU
列表。这有助于避免超长时间运行的服务因内存泄露或其他问题占用大量内存,进而妨碍系统有效利用缓存进程。

4.缓存进程是目前不需要的进程,因此,如果其他地方需要内存,系统可以根据需要自由地终止该进程。在正常运行的系统中,这些是内存管理中涉及的唯一进程:运行良好的系统将始终有多个缓存进程可用(为了更高效地切换应用),并根据需要定期终止最早的进程。只有在非常危急(且具有不良影响)的情况下,系统中的所有缓存进程才会被终止,此时系统必须开始终止服务进程。
这些进程通常包含用户当前不可见的一个或多个 Activity 实例(onStop() 方法已被调用并返回)。只要它们正确实现其
Activity 生命周期(详情请见 Activity),那么当系统终止此类流程时,就不会影响用户返回该应用时的体验,因为当关联的
Activity 在新的进程中重新创建时,它可以恢复之前保存的状态。

这些进程保存在伪 LRU
列表中,列表中的最后一个进程是为了回收内存而终止的第一个进程。此列表的确切排序政策是平台的实现细节,但它通常会先尝试保留更多有用的进程(比如托管用户的主屏幕应用、用户最后看到的
Activity
的进程等),再保留其他类型的进程。还可以针对终止进程应用其他政策:比如对允许的进程数量的硬限制,对进程可持续保持缓存状态的时间长短的限制等。
在决定如何对进程进行分类时,系统会参考进程中当前活动的所有组件中最重要的级别。请参阅 Activity、Service 和 BroadcastReceiver 文档,详细了解这些组件各自对进程的整体生命周期有何影响。每个类的文档都详细介绍了它们对应用的整个生命周期有何影响。
进程的优先级也可能因从属于进程的其他依赖项而提升。例如,如果进程 A 已通过 Context.BIND_AUTO_CREATE 标记绑定到 Service,或在使用进程 B 中的 ContentProvider,则进程 B 的分类始终至少和进程 A 一样重要。
上面说了 正规的让我们应用保活的方式有
1.提高进程优先级
2.减少内存的开销
除了正规保活方式 自然也有黑科技 那就是双进程守护

2 双进程守护Demo

原理比较简单 就是利用notification将Service设置为前台服务 提高进程优先级,并相互bind,两个Service互为远程Service,进程不一样。这样当Android设备一个一个杀死进程的时候,就只能杀死一个Service的进程,另外一个Service进程会唤起被杀的进程。

2.1示例代码

因为互为远程Service 因此需要利用AIDL将两个Service都变成远程Service

// IRemoteServiceConn.aidl
package com.example.processgaurd;

// Declare any non-default types here with import statements

interface IRemoteServiceConn {
}

两个Service的定义

public class MainService extends Service {
    private static final String TAG = "MainService";
    private static final int SERVICE_ID = 0x11;


    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, " MainService 创建 onCreate: ");
        new Thread(() -> {
            while (true) {
                Log.e(TAG, "MainService 等待接收消息");
                try {
                    Thread.sleep(2000 * 20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, " MainService启动 onStartCommand: ");
        // 提高进程优先级
        startForeground(SERVICE_ID, Util.getNotification(MainService.this, MainService.class, "111", "1111"));

        // 尝试bind remote GuardService
        Intent guardIntent = new Intent(MainService.this, GuardService.class);
        bindService(guardIntent,
                mServiceConnection, Context.BIND_IMPORTANT);
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, "MainService onBind ");
        return new IRemoteServiceConn.Stub() {
        };
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "MainService onServiceConnected ");
            // 建立连接
            Toast.makeText(MainService.this, "onServiceConnected MainService建立连接到守护Service成功", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // 断开连接
            Log.e(TAG, "MainService onServiceDisconnected ");
            Intent guardIntent = new Intent(MainService.this, GuardService.class);
            startService(guardIntent);
            MainService.this.bindService(guardIntent,
                    mServiceConnection, Context.BIND_IMPORTANT);
        }
    };
}
public class GuardService extends Service {
    private static final String TAG = "GuardService";
    private final int SERVICE_ID = 0x12;

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "GuardService 创建 onCreate: ");
        new Thread(() -> {
            while (true) {
                Log.e(TAG, "GuardService 等待接收消息");
                try {
                    Thread.sleep(2000 * 20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "GuardService启动 onStartCommand: ");
        // 提高进程优先级
        startForeground(SERVICE_ID, Util.getNotification(GuardService.this, GuardService.class, "222", "2222"));

        // 绑定建立连接
        bindService(new Intent(this, MainService.class), mServiceConnection, Context.BIND_IMPORTANT);
        return START_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, "GuardService onBind ");
        return new IRemoteServiceConn.Stub() {
        };
    }


    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "GuardService onServiceConnected ");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "GuardService onServiceDisconnected ");
            // 连接异常断开 ,重新启动,重新绑定
            Intent intent = new Intent(GuardService.this, MainService.class);
            startService(intent);
            bindService(intent,
                    mServiceConnection, Context.BIND_IMPORTANT);
        }
    };
}

辅助类

public class Util {
    @RequiresApi(api = Build.VERSION_CODES.O)
    public static Notification getNotification(Context context, Class<?> cls, String title, String content) {
        Notification.Builder builder = new Notification.Builder(context); //获取一个Notification构造器
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            builder.setChannelId("notification_id");
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationChannel channel = new NotificationChannel("notification_id", "notification_name", NotificationManager.IMPORTANCE_LOW);
            notificationManager.createNotificationChannel(channel);
        }
        Intent nfIntent = new Intent(context, MainActivity.class);
        builder
                .setContentIntent(PendingIntent.getActivity(context, 0, nfIntent, 0)) // 设置PendingIntent
                .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher)) // 设置下拉列表中的图标(大图标)
                .setContentTitle(title)// 设置下拉列表里的标题
                .setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
                .setContentText(content) // 设置上下文内容
                .setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
        Notification notification = builder.build(); // 获取构建好的Notification
        notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
        return notification;
    }
}

Service需要在清单文件声明

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.processgaurd">
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.LearnEassyJoke">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".MainService" />
        <!--GuardService单独运行在一个进程-->
        <service
            android:name=".GuardService"
            android:process=":guardService" />
    </application>

</manifest>

最后定义两个点击事件

    public void startService(View view) {
        startService(new Intent(this, MainService.class));
    }

    public void startRemoteService(View view) {
        startService(new Intent(this, GuardService.class));
    }

2.2执行流程

1.点击startService
2021-04-29 09:42:48.900 4323-4323/com.example.processgaurd E/MainService:  MainService 创建 onCreate: 
2021-04-29 09:42:48.902 4323-4364/com.example.processgaurd E/MainService: MainService 等待接收消息
2021-04-29 09:42:48.902 4323-4323/com.example.processgaurd E/MainService:  MainService启动 onStartCommand: // 这一步虽然已经去bind了GuardService 但是GuardService没有启动 所以没有进一步的log

2.点击startRemoteService 
2021-04-29 09:42:51.187 1669-7737/system_process I/ActivityManager: Start proc 4380:com.example.processgaurd:guardService/u0a89 for service com.example.processgaurd/.GuardService
2021-04-29 09:42:51.234 4380-4380/com.example.processgaurd E/GuardService: GuardService 创建 onCreate: 
2021-04-29 09:42:51.236 4380-4402/com.example.processgaurd E/GuardService: GuardService 等待接收消息
2021-04-29 09:42:51.238 4380-4380/com.example.processgaurd E/GuardService: GuardService onBind //MainService终于bind到了GuardService
2021-04-29 09:42:51.239 4380-4380/com.example.processgaurd E/GuardService: GuardService启动 onStartCommand: //GuardService启动并去bind到MainService
2021-04-29 09:42:51.242 4323-4323/com.example.processgaurd E/MainService: MainService onServiceConnected // MainService接收到成功绑定到GuardService的消息
2021-04-29 09:42:51.261 4323-4323/com.example.processgaurd E/MainService: MainService onBind //GuardService bind到了MainService
2021-04-29 09:42:51.264 4380-4380/com.example.processgaurd E/GuardService: GuardService onServiceConnected //GuardService接收到成功绑定到MainService的消息
这里看起来比较混乱 其实就是两个service相互bind 我们只需要知道一个Service的几个生命周期 以及ServiceConnection的两个回调,一切都很清晰:

onCreate:service创建
onStartCommand:service启动
onBind:其他组件bind到本service

onServiceConnected:本组件调用bindService之后 接收到service端bind成功的消息
onServiceDisconnected:本组件与其他service的bind异常终止

3. 使用JobService保活

关于JobService有一篇比较不错的文章
https://www.jianshu.com/p/8f9090e12015
注意 该方法只适用于低版本Android 且没有被Rom厂商修改framework代码的前提下,虽然网上很多文章说在Android7.0以上可以使用jobService进行保活 但是我没有测试成功(在Setting中点击强制停止 应用不会再次启动,但是使用adb kill -9的命令 进程可以被成功拉起),这里依然列举 是提一个思路以及学习一下JobSchedule JobService的使用

3.1 JobService的作用

JobSchedule通常和JobService结合起来使用 我们通常创建一个Service继承自JobService 然后实现onStartJob方法 我们通常在该Service的onStartCommand启动定时任务 onStartJob是定时任务的执行体(注意该执行体运行在主线程 因此耗时的操作需要另开线程处理)
JobService最初被设计出来的目的是让App做一些省电 省流量操作 比如,可以设置定时任务 该任务只在插入充电器的情况执行 或者该任务只在wifi连接的情况下进行。由于JobSchedule是将任务的处理抛给framework层处理,因此即时我们的应用被杀死,framework的任务也不会中断

3.2 Demo例子

配置自己的JobService

@RequiresApi(api = Build.VERSION_CODES.O)
public class MyJobService extends JobService {
    private static final int JOB_WAKE_UP_ID = 0x11;
    JobScheduler jobScheduler;
    int jobId;

    // 服务启动
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("hjcai", "onStartCommand: ");
        JobInfo.Builder jobBuilder = new JobInfo.Builder(
                JOB_WAKE_UP_ID, new ComponentName(this, MyJobService.class));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            jobBuilder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS); //执行的最小延迟时间
            jobBuilder.setOverrideDeadline(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);  //执行的最长延时时间
            jobBuilder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);
            jobBuilder.setBackoffCriteria(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS, JobInfo.BACKOFF_POLICY_LINEAR);//线性重试方案
        } else {
            // 开启一个轮寻 每隔30秒查询
            jobBuilder.setPeriodic(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);
        }

        jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        jobBuilder.setPersisted(true);  // 设置设备重启时,执行该任务
        jobId = jobScheduler.schedule(jobBuilder.build());
        return START_STICKY;
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        // 轮询的执行体
        // 看MainService有没有被杀死
        // 如果杀死了就重新启动
        Log.e("hjcai", "onStartJob: ");
        boolean messageServiceAlive = serviceAlive(GuardService.class.getName());
        if (!messageServiceAlive) {
            startForegroundService(new Intent(this, GuardService.class));
        }

        boolean messageServiceAlive2 = serviceAlive(MainService.class.getName());
        if (!messageServiceAlive2) {
            startForegroundService(new Intent(this, MainService.class));
        }

        jobFinished(params, false);
        // jobScheduler.cancel(jobId); //取消指定定时任务
        // jobScheduler.cancelAll(); //取消所有指定定时任务

        // Return true from this method if your job needs to continue running,the job remains active until you call jobFinished(android.app.job.JobParameters, boolean)
        return true;
    }

    /**
     * 判断某个服务是否正在运行的方法
     *
     * @param serviceName 是包名+服务的类名(例如:net.loonggg.testbackstage.TestService)
     * @return true代表正在运行,false代表服务没有在运行
     */
    private boolean serviceAlive(String serviceName) {
        boolean isWork = false;
        ActivityManager myAM = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningServiceInfo> myList = myAM.getRunningServices(100);
        if (myList.size() <= 0) {
            return false;
        }
        for (int i = 0; i < myList.size(); i++) {
            String mName = myList.get(i).service.getClassName();
            if (mName.equals(serviceName)) {
                isWork = true;
                break;
            }
        }
        Log.e("hjcai", "serviceAlive: isWork " + isWork);
        return isWork;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}

在系统清单文件配置service以及权限

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
	
		<service
            android:name=".MyJobService"
            android:enabled="true"
            android:process=":jobService"
            android:permission="android.permission.BIND_JOB_SERVICE" />

在合适的时机启动该Service

    public void startJobService(View view) {
        startService(new Intent(this, MyJobService.class));
    }

该Service启动后 过一段时间就会查询所有Service 查看指定Service是否存活,如果不是存活状态 将其启动以达到保活的目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值