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

}
相关推荐
Dnelic-3 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen5 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年12 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿15 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神16 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛16 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法17 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
NotesChapter18 小时前
Android吸顶效果,并有着ViewPager左右切换
android
_祝你今天愉快19 小时前
分析android :The binary version of its metadata is 1.8.0, expected version is 1.5.
android
暮志未晚Webgl19 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5