双进程保活

【Android 进程保活】应用进程拉活 ( 双进程守护保活 )

韩曙亮

于 2021-04-11 21:50:46 发布

阅读量3.6k
收藏 19

点赞数 5
分类专栏: Android 性能优化 # Android 进程保活 文章标签: 进程保活 双进程守护保活
版权

Android 性能优化
同时被 2 个专栏收录
189 篇文章54 订阅
订阅专栏

Android 进程保活
15 篇文章14 订阅
订阅专栏
文章目录
一、 双进程守护保活原理
二、 双进程守护保活完整源码
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 {
        // 通信内容
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
" 本地前台进程 " LocalForegroundService 在 onCreate 方法中开启前台服务 , 提权 , 参考 【Android 进程保活】提升进程优先级 ( 使用前台 Service 提高应用进程优先级 | 效果展示 | 源码资源 ) , 并且创建用于进程间通信的 Binder 对象 ;

/**
 * 远程调用 Binder 对象
 */
private MyBinder myBinder;

@Override
public void onCreate() {
    super.onCreate();
    // 创建 Binder 对象
    myBinder = new MyBinder();

    // 启动前台进程
    startService();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
" 本地前台进程 " LocalForegroundService , 在 onBind 方法中返回 onCreate 方法中创建的 Binder 对象 ;

@Override
public IBinder onBind(Intent intent) {
    return myBinder;
}

1
2
3
4
" 本地前台进程 " 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();
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

另外特别注意权限问题 , 需要在清单文件中配置 android.permission.FOREGROUND_SERVICE 权限 :

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

二、 双进程守护保活完整源码

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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

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;
      }

    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

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;
      }

    }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162

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>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

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));
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

5、执行效果

执行上述应用后 , 可以看到启动了两个应用 , 干掉应用后 , 可以被远程进程拉起 , 干掉远程进程 , 远程进程可以本主进程服务拉起 ;

三、 源码资源

源码资源 :

GitHub 地址 : https://github.com/han1202012/Two_Progress_Alive
CSDN 源码快照 : https://download.csdn.net/download/han1202012/16623056
————————————————
版权声明:本文为CSDN博主「韩曙亮」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/shulianghan/article/details/115604667

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容