天天看點

【Android 程序保活】應用程序拉活 ( 雙程序守護 + JobScheduler 保活 | 成功率最高 | 推薦使用 )

文章目錄

  • 一、 雙程序守護保活 + JobScheduler 原理
  • 二、 雙程序守護保活 + JobScheduler 源碼
  • 1、JobService 代碼
  • 2、判定服務運作工具類
  • 3、清單檔案
  • 4、MainActivity 代碼
  • 5、運作效果
  • 三、 源碼資源

一、 雙程序守護保活 + JobScheduler 原理

【Android 程序保活】應用程序拉活 ( JobScheduler 拉活 | JobScheduler 使用流程 | JobService 服務 | 不同版本相容 | 源碼資源 ) 部落格中介紹了 JobScheduler 的用法 ;

【Android 程序保活】應用程序拉活 ( 雙程序守護保活 ) 部落格中介紹了雙程序守護保活用法 ;

​使用 " 雙程序守護保活 + JobScheduler " 機制 , 成功率最高 ;​

​" 雙程序守護保活 + JobScheduler " 整合方法 :​

在 JobService 的 onStartJob 方法中 , 判定 " 雙程序守護保活 " 中的雙程序是否挂了 , 如果這兩個程序挂了 , 就重新将挂掉的程序重新開機 ;

​判定 Service 程序是否運作 :​

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<ActivityManager.RunningServiceInfo> infos =
                activityManager.getRunningServices(200);

        // 周遊目前運作的 Service 資訊, 如果找到相同名稱的服務 , 說明某程序正在運作
        for (ActivityManager.RunningServiceInfo info: infos){
            if (TextUtils.equals(info.service.getClassName(), serviceName)){
                return true;
            }
        }

        return false;
    }      

二、 雙程序守護保活 + JobScheduler 源碼

大部分代碼與 【Android 程序保活】應用程序拉活 ( 雙程序守護保活 ) 部落格中重複 , 這裡隻貼出 JobScheduler 相關源碼 ;

1、JobService 代碼

package kim.hsl.two_progress_alive;

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.util.Log;

import androidx.annotation.RequiresApi;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
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());

    }
}      

2、判定服務運作工具類

package kim.hsl.two_progress_alive;

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<ActivityManager.RunningServiceInfo> infos =
                activityManager.getRunningServices(200);

        // 周遊目前運作的 Service 資訊, 如果找到相同名稱的服務 , 說明某程序正在運作
        for (ActivityManager.RunningServiceInfo info: infos){
            if (TextUtils.equals(info.service.getClassName(), serviceName)){
                return true;
            }
        }

        return false;
    }
}      

3、清單檔案

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kim.hsl.two_progress_alive">

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <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.Two_Progress_Alive">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <!-- 本地提權前台服務 Service -->
        <service
            android:name=".LocalForegroundService"
            android:enabled="true"
            android:exported="true"></service>

        <!-- 本地服務 , API 18 ~ 25 以上的裝置, 關閉通知到專用服務 -->
        <service
            android:name=".LocalForegroundService$CancelNotificationService"
            android:enabled="true"
            android:exported="true"></service>

        <!-- 遠端提權前台服務 Service -->
        <service
            android:name=".RemoteForegroundService"
            android:enabled="true"
            android:exported="true"
            android:process=":remote"></service>

        <!-- 遠端服務 , API 18 ~ 25 以上的裝置, 關閉通知到專用服務 -->
        <service
            android:name=".RemoteForegroundService$CancelNotificationService"
            android:enabled="true"
            android:exported="true"
            android:process=":remote"></service>

        <!-- JobScheduler 拉活 -->
        <service
            android:name=".KeepAliveJobService"
            android:enabled="true"
            android:exported="true"
            android:permission="android.permission.BIND_JOB_SERVICE"></service>

    </application>

</manifest>      

4、MainActivity 代碼

package kim.hsl.two_progress_alive;

import android.content.Intent;
import android.os.Build;
import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 通過前台 Service 提升應用權限
        // 啟動普通 Service , 但是在該 Service 的 onCreate 方法中執行了 startForeground
        // 變成了前台 Service 服務
        startService(new Intent(this, LocalForegroundService.class));
        startService(new Intent(this, RemoteForegroundService.class));

        // JobScheduler 拉活
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            KeepAliveJobService.startJob(this);
        }
    }
}      

5、運作效果

運作後 , 兩個程序成功運作 ;

即使将啟動雙程序的代碼注釋掉 , 也可以成功拉起雙程序 ;

【Android 程式保活】應用程式拉活 ( 雙程式守護 + JobScheduler 保活 | 成功率最高 | 推薦使用 )

三、 源碼資源

  • ​GitHub 位址 :​ https://github.com/han1202012/Two_Process_Alive