文章目錄
- 一、 雙程序守護保活原理
- 二、 雙程序守護保活完整源碼
- 1、AIDL 接口
- 2、本地前台服務 Service
- 3、遠端前台服務 Service
- 4、清單配置
- 5、啟動兩個服務
- 5、執行效果
- 三、 源碼資源
一、 雙程序守護保活原理
雙程序守護拉活 , 使用 JobScheduler 拉活 和 系統 Service 機制拉活 兩種拉活方式 , 結合起來使用 ;
雙程序機制拉活 , 比之前的 廣播拉活 , 系統 Service 機制拉活 , 賬戶同步拉活 , JobScheduler 機制拉活 , 成功率都要高 , 可靠性比較高 , 但是也存在失敗的情況 ;
JobScheduler 原理 :
在應用中 , 運作了一個主程序 , 除此之外 , 還運作了一個 " 本地前台程序 " , 運作該 " 本地前台程序 " 時 , 開啟前台程序 , 用于提權 , 并綁定 " 遠端前台程序 " ;
" 遠端前台程序 " 與 " 本地前台程序 " 實作了相同的功能 , 代碼基本一緻 , 這兩個程序都是前台程序 , 都進行了提權 , 并且互相綁定 , 當監聽到綁定的另外一個程序突然斷開連接配接 , 則本程序再次開啟前台程序提權 , 并且重新綁定對方程序 , 以達到拉活對方程序的目的 ;
舉例 : " 本地前台程序 " LocalForegroundService , " 遠端前台程序 " RemoteForegroundService ;
這兩個程序之間需要綁定 , 這裡就需要定義 AIDL 接口 IMyAidlInterface , 每個服務中都需要定義繼承 IMyAidlInterface.Stub 的 Binder 類 , 作為程序間通信的橋梁 ; ( 這是個預設的 AIDL 接口 )
/**
* 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 {
// 通信内容
}
}
" 本地前台程序 " LocalForegroundService 在 onCreate 方法中開啟前台服務 , 提權 , 參考 【Android 程序保活】提升程序優先級 ( 使用前台 Service 提高應用程序優先級 | 效果展示 | 源碼資源 ) , 并且建立用于程序間通信的 Binder 對象 ;
/**
* 遠端調用 Binder 對象
*/
private MyBinder myBinder;
@Override
public void onCreate() {
super.onCreate();
// 建立 Binder 對象
myBinder = new MyBinder();
// 啟動前台程序
startService();
}
" 本地前台程序 " LocalForegroundService , 在 onBind 方法中傳回 onCreate 方法中建立的 Binder 對象 ;
@Override
public IBinder onBind(Intent intent) {
return myBinder;
}
" 本地前台程序 " LocalForegroundService 中 , 綁定遠端程序時 , 需要使用到 ServiceConnection 類 , 在服務綁定成功時回調 onServiceConnected , 服務斷開時回調 onServiceDisconnected 方法 ; 這裡就在 onServiceDisconnected 方法中再次對本服務進行提權 , 并且再次綁定 " 遠端前台程序 " RemoteForegroundService ;
class Connection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 服務綁定成功時回調
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 再次啟動前台程序
startService();
// 綁定另外一個遠端程序
bindService();
}
}
另外特别注意權限問題 , 需要在清單檔案中配置 android.permission.FOREGROUND_SERVICE 權限 :
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
二、 雙程序守護保活完整源碼
1、AIDL 接口
這裡的 AIDL 不實作任何操作 , 是系統預設生成的 AIDL 接口 , 隻是用于單純的綁定兩個程序 , 監聽程序的連接配接斷開 ;
// IMyAidlInterface.aidl
package kim.hsl.two_process_alive;
// 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、本地前台服務 Service
package kim.hsl.two_process_alive;
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);
}
class Connection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 服務綁定成功時回調
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 再次啟動前台程序
startService();
// 綁定另外一個遠端程序
bindService();
}
}
/**
* API 18 ~ 25 以上的裝置, 關閉通知到專用服務
*/
public static class CancelNotificationService extends Service {
public CancelNotificationService() {
}
@Override
public void onCreate() {
super.onCreate();
startForeground(10, new Notification());
stopSelf();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
}
3、遠端前台服務 Service
package kim.hsl.two_process_alive;
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);
}
class Connection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 服務綁定成功時回調
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 再次啟動前台程序
startService();
// 綁定另外一個遠端程序
bindService();
}
}
/**
* API 18 ~ 25 以上的裝置, 關閉通知到專用服務
*/
public static class CancelNotificationService extends Service {
public CancelNotificationService() {
}
@Override
public void onCreate() {
super.onCreate();
startForeground(10, new Notification());
stopSelf();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
}
4、清單配置
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="kim.hsl.two_process_alive">
<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.Two_Process_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>
</application>
</manifest>
5、啟動兩個服務
package kim.hsl.two_process_alive;
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);
// 通過前台 Service 提升應用權限
// 啟動普通 Service , 但是在該 Service 的 onCreate 方法中執行了 startForeground
// 變成了前台 Service 服務
startService(new Intent(this, LocalForegroundService.class));
startService(new Intent(this, RemoteForegroundService.class));
}
}
5、執行效果
執行上述應用後 , 可以看到啟動了兩個應用 , 幹掉應用後 , 可以被遠端程序拉起 , 幹掉遠端程序 , 遠端程序可以本主程序服務拉起 ;
三、 源碼資源
- GitHub 位址 : https://github.com/han1202012/Two_Progress_Alive