目录
1)为什么需要进行进程保活呢?需求是什么?
2)进程分类
3)进程的优先级
4)如何提高进程优先级
5)如何进行进程保活
一、为什么需要进行进程保活呢?需求是什么?
比如:
- app在运行过程中,可能会因为各种原因导致闪退,报错或者被杀死等,尤其是一些自动售卖机,安装了android系统,如果这个时候app闪退了,那么就无法售卖了,我们需要能让程序运行起来。
- 如果用中有后台服务需要一直运行,例如音乐播放器、即时通讯等。
- 应用可能需要接收推送消息并及时展示给用户。为了实这个功能,需要保证应用进程直处于活跃状态,以便能够及时处理和显示推送消息。
- 有些应用需要执行定时任务,例如定时更新数据、定时发送统计信息等。通过保持进程活跃,可以确保这些任务按时执行。
- 某些应用需要在屏幕上显示悬浮窗口,例如悬浮球、悬浮菜单等
就需要通过进程保活来防止系统将其杀死,今天我们就来讲讲,进程保活。
二、进程分类
进程会被杀死,那么什么进程会被杀死呢?
1)前台进程(Foreground Process):前台进程是指当前正在与用户进行交互的app进程。例如,用户正在使用某个app或者app正在展示一个前台界面时;在前台运行的 Service 服务 , Service 调用了 startForeground,该app所在的进程就被认为是前台进程。前台进程拥有最高的优先级,不容易被系统杀死。
2)可见进程(Visible Process)可见进程是指虽然没有处于前台,但是对用户可见的app进程。例如,当一个Activity被另一个Activity或Dialog部分遮挡时,被遮挡的Activity所在的进程就被认为是可见进程。可见进程的优先级较高,相比后台进程更不容易被系统杀死。Activity 组件调用 onPause 生命周期函数。
3)服务进程(Service Process)调用 startService 方法启动的 Service 进程组件 , 就是服务进程 , 其没有与 Activity 组件绑定 , 因此该 Service 组件的优先级要降低一个等级 , 称为服务进程 ;
4)后台进程(Background Process):后台进程是指已经失去用户焦点且不可见的app进程。例如,当用户按Home键回到桌面时,app进入后台状态。后台进程的优先级较低,当系统内存不足时可能会被系统杀死以释放资源。
5)空进程(Empty Process):空进程是指没有任何活动组件(如Activity、Service等)的应用进程。这进程通常只是为了缓存应用的数据而保留,当需要重新启动应用时可以快速恢复。空进程的优先级最低,是系统内存不足时首先被杀死的对象。
三、那么,如何才能清楚的确认,究竟是那种进程呢?
通过oom_adj值.
oom_adj值是Linux内核为每个进程分配的,用于反映进程的优先级。在Android系统中,当内存不足需要杀死进程以回收内存时,系统会根据oom_adj值来决定是否回收该进程。oom_adj值越大,对应的进程优先级越低,越有可能被系统回收。
以下是一些oom_adj值对应的进程优先级(注意:这些值可能会因Android版本的不同而有所变化,以下信息基于参考文章提供的信息):
oom_adj值 | 进程优先级描述 |
---|---|
-16 | SYSTEM_ADJ:系统进程,具有最高优先级 |
0 | FOREGROUND_APP_ADJ:前台进程,用户当前正在交互的进程 |
1 | VISIBLE_APP_ADJ:可见进程,虽然不在前台但用户仍然可以看到(如锁屏界面上的应用) |
2 | PERCEPTIBLE_APP_ADJ:可感知进程,如后台播放音乐、铃声、震动等 |
3 | BACKUP_APP_ADJ:进入后台的进程,按下Menu键可查看 |
5 | SERVICE_ADJ:服务进程,持有应用服务的进程 |
如何查看oom_adj值
在 Android Studio 中 , 可以看到该运行的程序的进程号 PID 为 30575
进入 adb shell 命令行 , su 获取 root 权限 , 使用如下命令cat /proc/30575/oom_adj
, 查询指定 PID 的 oom_adj 值就可以看到,为0就是前台进程。
那么,内存不足,多少才算不足呀?
这些数字,其单位是 4 K B , 80640 , 乘以 4 K B \rm 4KB 4KB , 除以1024 , 最后得到 315MB
内存不足时杀进程的优先顺序 :
18432 : 内存小于 18432 × 4 K B \rm 18432 \times 4KB 18432×4KB 时 , 杀 " 前台进程 " ;
23040 : 内存小于 23040 × 4 K B \rm 23040 \times 4KB 23040×4KB 时 , 杀 " 可见进程 " ;
27648 : 内存小于 27648 × 4 K B \rm 27648 \times 4KB 27648×4KB 时 , 杀 " 服务进程进程 " ;
32256 : 内存小于 32256 × 4 K B \rm 32256 \times 4KB 32256×4KB 时 , 杀 " 后台进程 " ;
55296 : 内存小于 55296 × 4 K B \rm 55296 \times 4KB 55296×4KB 时 , 杀 " ContentProvider 进程 " ;
80640 : 内存小于 80640 × 4 K B \rm 80640 \times 4KB 80640×4KB 时 , 杀 " 空进程 " ;
四、如何提高进程优先级
了解了这么多,我们知道前台进程不容易被杀死,假如我们变成了其他进程,那么如何才能提高他的优先级呢?
4.1 前台 Service
(1)service
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder class MyForegroundService : Service() { private val NOTIFICATION_ID = 1 private val CHANNEL_ID = "ForegroundServiceChannel" override fun onCreate() { super.onCreate() val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // 创建通知渠道(Android 8.0+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( CHANNEL_ID, "Foreground Service Channel", NotificationManager.IMPORTANCE_LOW ) notificationManager.createNotificationChannel(channel) } // 启动前台服务 startForeground(NOTIFICATION_ID, getNotification("服务正在运行...")) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // 处理从Activity传递过来的数据(如果有的话) return START_STICKY } override fun onBind(intent: Intent?): IBinder? { return null } private fun getNotification(contentText: String): Notification { val builder = Notification.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) // 设置图标,确保你有这个资源 .setContentTitle("前台服务示例") .setContentText(contentText) .setPriority(Notification.PRIORITY_LOW) // 可以在这里添加其他通知设置,如声音、振动等 return builder.build() }
}
(2)注册服务
<application ... > ... <service android:name=".MyForegroundService" android:enabled="true" android:exported="false" /> ...
</application>
(3)activity
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 启动前台服务 val serviceIntent = Intent(this, MyForegroundService::class.java) startService(serviceIntent) } // 其他方法...
}
好了,我们可以运行程序看看,比如按home键,然后再读取oom_adj值,看看有没有生效。
五、如何进行进程保活
通过上述信息,那么我们就可以知道,我们现在的进程究竟是什么?为我们接下来要进行进程保活做铺垫。进程保活是指在Android系统,通过一些手段和技术手段来确保应用进程持续运行的一种机制。
5.1 双进程保活
进程之间相互监听销毁的状态,从而重新启动对方。通过ServiceConnection 来进行监听。
(1)activity
startService(Intent(this, AService.java))
startService(Intent(this, BService.java))
(2)service
public class AService extends Service {private Connection connection;@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();// 启动前台进程startService();}private void startService() {// 创建通知通道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();startForeground(10, notification);}/*** 绑定 另外一个 服务*/private void bindService() {// 创建连接对象connection = new Connection();// 创建本地前台进程组件意图Intent bindIntent = new Intent(this, BService.class);// 绑定进程操作bindService(bindIntent, connection, BIND_AUTO_CREATE);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 绑定另外一个服务bindService();return START_STICKY; // 尝试在系统杀死服务后重新创建它}class Connection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 服务绑定成功时回调}@Overridepublic void onServiceDisconnected(ComponentName name) {// 再次启动前台进程startService();// 绑定另外一个远程进程bindService();}}}
(3)service
public class BService extends Service {private Connection connection;@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();// 启动前台进程startService();}private void startService(){// 创建通知通道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();startForeground(10, notification);}/*** 绑定 另外一个 服务*/private void bindService(){// 创建连接对象connection = new Connection();// 创建本地前台进程组件意图Intent bindIntent = new Intent(this, AService.class);// 绑定进程操作bindService(bindIntent, connection, BIND_AUTO_CREATE);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 绑定另外一个服务bindService();return START_STICKY; // 尝试在系统杀死服务后重新创建它}class Connection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 服务绑定成功时回调}@Overridepublic void onServiceDisconnected(ComponentName name) {// 再次启动前台进程startService();// 绑定另外一个远程进程bindService();}}}