Android进程保活:如何让app一直运行

目录

1)为什么需要进行进程保活呢?需求是什么?

2)进程分类

3)进程的优先级

4)如何提高进程优先级

5)如何进行进程保活

一、为什么需要进行进程保活呢?需求是什么?

比如:

  1. app在运行过程中,可能会因为各种原因导致闪退,报错或者被杀死等,尤其是一些自动售卖机,安装了android系统,如果这个时候app闪退了,那么就无法售卖了,我们需要能让程序运行起来。
  2. 如果用中有后台服务需要一直运行,例如音乐播放器、即时通讯等。
  3. 应用可能需要接收推送消息并及时展示给用户。为了实这个功能,需要保证应用进程直处于活跃状态,以便能够及时处理和显示推送消息。
  4. 有些应用需要执行定时任务,例如定时更新数据、定时发送统计信息等。通过保持进程活跃,可以确保这些任务按时执行。
  5. 某些应用需要在屏幕上显示悬浮窗口,例如悬浮球、悬浮菜单等

就需要通过进程保活来防止系统将其杀死,今天我们就来讲讲,进程保活。

二、进程分类

进程会被杀死,那么什么进程会被杀死呢?

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

kt 复制代码
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)注册服务

xml 复制代码
<application  
    ...  
    >  
    ...  
    <service  
        android:name=".MyForegroundService"  
        android:enabled="true"  
        android:exported="false" />  
    ...  
</application>

(3)activity

kt 复制代码
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

kt 复制代码
startService(Intent(this, AService.java))
startService(Intent(this, BService.java))

(2)service

java 复制代码
public class AService extends Service {

    private Connection connection;


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

    @Override
    public 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);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 绑定另外一个服务
        bindService();
        return START_STICKY; // 尝试在系统杀死服务后重新创建它
    }

    class Connection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 服务绑定成功时回调
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // 再次启动前台进程
            startService();
            // 绑定另外一个远程进程
            bindService();
        }
    }


}

(3)service

java 复制代码
public class BService extends Service {

    private Connection connection;

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

    @Override
    public 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);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 绑定另外一个服务
        bindService();
        return START_STICKY; // 尝试在系统杀死服务后重新创建它
    }

    class Connection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 服务绑定成功时回调
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // 再次启动前台进程
            startService();
            // 绑定另外一个远程进程
            bindService();
        }
    }

}
相关推荐
太空漫步112 小时前
android社畜模拟器
android
海绵宝宝_5 小时前
【HarmonyOS NEXT】获取正式应用签名证书的签名信息
android·前端·华为·harmonyos·鸿蒙·鸿蒙应用开发
凯文的内存6 小时前
android 定制mtp连接外设的设备名称
android·media·mtp·mtpserver
天若子7 小时前
Android今日头条的屏幕适配方案
android
林的快手8 小时前
伪类选择器
android·前端·css·chrome·ajax·html·json
望佑8 小时前
Tmp detached view should be removed from RecyclerView before it can be recycled
android
xvch11 小时前
Kotlin 2.1.0 入门教程(二十四)泛型、泛型约束、绝对非空类型、下划线运算符
android·kotlin
人民的石头14 小时前
Android系统开发 给system/app传包报错
android
yujunlong391915 小时前
android,flutter 混合开发,通信,传参
android·flutter·混合开发·enginegroup
rkmhr_sef15 小时前
万字详解 MySQL MGR 高可用集群搭建
android·mysql·adb