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

news2024/9/22 13:34:48

目录

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值进程优先级描述
-16SYSTEM_ADJ:系统进程,具有最高优先级
0FOREGROUND_APP_ADJ:前台进程,用户当前正在交互的进程
1VISIBLE_APP_ADJ:可见进程,虽然不在前台但用户仍然可以看到(如锁屏界面上的应用)
2PERCEPTIBLE_APP_ADJ:可感知进程,如后台播放音乐、铃声、震动等
3BACKUP_APP_ADJ:进入后台的进程,按下Menu键可查看
5SERVICE_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;


    @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


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

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1974014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国标GB28181视频平台LntonCVS视频融合共享平台视频汇聚应用方案

近年来&#xff0c;国内视频监控应用迅猛发展&#xff0c;系统接入规模不断扩大&#xff0c;导致了大量平台提供商的涌现。然而&#xff0c;不同平台的接入协议千差万别&#xff0c;使得终端制造商不得不为每款设备维护多个不同平台的软件版本&#xff0c;造成了资源的严重浪费…

工业大数据通过哪些方式实现价值?详解实施工业大数据的难点!

在数字化转型的浪潮中&#xff0c;工业大数据正成为推动制造业革新的核心动力。它不仅重塑了生产流程&#xff0c;还为企业带来了前所未有的洞察力和竞争优势。本文将深入探讨工业大数据的类别、价值实现方式&#xff0c;以及在实施过程中存在的挑战和解决方案。 更多详细内容&…

JavaScript和vue实现左右两栏,中间拖动按钮可以拖动左右两边的宽度

JavaScript实现&#xff1a; <!DOCTYPE html> <html lang"en"> <head><title>拖动效果</title><style> body, html {margin: 0;padding: 0;height: 100%;font-family: Arial, sans-serif; }.container {display: flex;height: …

pytest测试框架之http协议接口测试

1 接口测试 日常测试中接口测试是一项重要的工作&#xff0c;尤其是http协议的接口测试更加普遍,比如一些常用的测试框架或者工具&#xff08;robotframework框架&#xff0c;testng框架&#xff0c;postman等&#xff09;都支持http接口的测试&#xff0c;而这节内容主要介绍…

函数:全局,局部和静态变量

文章目录 &#x1f34a;自我介绍&#x1f34a;全局变量&#x1f34a;局部变量&#x1f34a;静态局部变量 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34a;自我介绍 Hello,大家好&#x…

力扣SQL50 餐馆营业额变化增长 子连接

Problem: 1321. 餐馆营业额变化增长 &#x1f468;‍&#x1f3eb; 参考题解 Code select a.visited_on,sum(b.amount) as amount, round(sum(b.amount) / 7,2) as average_amount from (select distinct visited_on from customer) a join customer bon datediff(a.visited…

window安装elasticsearch和可视化界面kibana

ElasticSearch 官网下载zip安装包并解压 Elasticsearch&#xff1a;官方分布式搜索和分析引擎 | Elastic 修改配置文件 改选项是指定ssl访问还是普通http访问 不改的话使用http访问不了&#xff0c;得使用https 浏览器访问 localhost:9200 Kibana Download Kibana Free |…

MySQL 将文件导入数据库(load data Statement)

前面我们介绍过如何用select…into outfile语句将SQL查询结果导出到文件&#xff1a; MySQL 将查询结果导出到文件&#xff08;select … into Statement&#xff09; MySQL同时也提供互补的功能&#xff0c;可以使用load data infile语句将文件中的数据加载到数据库中&#x…

Robot Operating System——Action通信机制的服务端

大纲 回调接受或者拒绝请求执行任务的回调终止任务回调 创建服务完整代码总结 在《Robot Operating System——Action通信机制概念及Client端》一文中&#xff0c;我们介绍了Action客户端的主要流程。本文我们将介绍Action服务端的编写。 回顾下Action的构成: 目标&#xff0…

cesium canvas广告牌

在有些业务中&#xff0c;对场景中的广告牌样式要求比较高&#xff0c;需要动态显示一些数据&#xff0c;这个时候&#xff0c;我们可以通过将复杂背景样式制作成图片&#xff0c;通过canvas绘制图片和动态数据&#xff0c;从而达到比较好的显示效果。 1 CanvasMarker 类封装 …

ICM-20948芯片详解(2)

接前一篇文章&#xff1a;ICM-20948芯片详解&#xff08;1&#xff09; 二、详述 ICM-20948是一款9轴运动跟踪设备&#xff0c;全部采用3x3x1mm QFN封装。ICM-20948是一个多芯片模块&#xff08;MCM&#xff09;&#xff0c;由集成在单个QFN封装中的两个管芯组成。一个芯片内装…

2024年【制冷与空调设备运行操作】考试技巧及制冷与空调设备运行操作考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 制冷与空调设备运行操作考试技巧考前必练&#xff01;安全生产模拟考试一点通每个月更新制冷与空调设备运行操作考试试题题目及答案&#xff01;多做几遍&#xff0c;其实通过制冷与空调设备运行操作作业模拟考试很简…

leetcode173. 二叉搜索树迭代器,注意vector中的size()的无符号整数类型,无符号整数和有符号整数的加减比大小有着种种大坑

leetcode173. 二叉搜索树迭代器 实现一个二叉搜索树迭代器类BSTIterator &#xff0c;表示一个按中序遍历二叉搜索树&#xff08;BST&#xff09;的迭代器&#xff1a; BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分…

【MySQL】全面剖析索引失效、回表查询与索引下推

1.索引失效的情况 以tb_user表举例&#xff0c;id为主键索引、name和phone字段上建立了一个普通索引&#xff0c;name和phone均为varchar类型。 索引列运算 当在 WHERE 子句或 JOIN 子句中对列使用函数或表达式时&#xff0c;索引会失效。 执行以下语句&#xff0c;可以发现执…

分布式事务原理

目录 第一节&#xff1a;分布式事务基础详细总结 1.1 事务的核心特性&#xff08;ACID&#xff09; 1.2 分布式事务的挑战 1.3 分布式事务的实现难点 1.4 分布式事务解决方案概览 图解&#xff1a;分布式事务的ACID特性 第二节&#xff1a;事务消息方案详细总结 2.1 事务…

AUTOSAR实战教程-使用DET来发现开发错误

2年之前因为在调试AUTOSAR存储协议栈的时候使用DET并没发现有用的信息,所以就武断下结论--这玩意没有用。活到老学到老吧,bug经历的多了,发现这玩意还挺有用的。说一下这个bug的背景。 在将时间同步报文改道CanTsync之后,由于这个AUTOSAR工具本身的问题以及配置工程师本身的…

SpringBoot校园社团场地租借平台开发-计算机毕业设计源码00746

摘 要 这个项目旨在开发一个基于SpringBoot 的校园社团场地租借平台&#xff0c;帮助学校社团更方便地租借校园内的场地。用户可以在平台上浏览不同场地的信息、预订场地、查看租借历史记录等。管理员可以管理场地信息、审批租借申请和生成报表统计等功能。通过该平台&#xff…

【大模型实战篇】搭建本地的隐私计算知识问答系统“密答”

1. 背景介绍 在之前分享的文章《基于开源大模型的问答系统本地部署实战教程》中&#xff0c;我们介绍了基于ollama实现本地问答系统的部署和使用。本文将基于ollama进一步实现本地垂直领域的问答系统搭建。ollama作为大模型的运行框架&#xff0c;可以提供大模型的使用接口…

Python 设计模式之建造者模式

文章目录 建造者模式中的简单版本逐渐复杂的问题建造者模式的实现 建造者模式中的经典版本 建造者&#xff08;builder&#xff09;模式属于创建型模式&#xff0c;建造者模式一般有两种类型的应用 建造者模式中的简单版本 逐渐复杂的问题 假设现在需要创建一个用户对象&…

如何提高小红书种草转化率

企业和品牌方想要在小红书上做种草推广一般分为图文种草和视频种草两种形式&#xff0c;而且小红书是一个完全可以依靠内容实现涨粉、变现、种草转化的平台&#xff01; 因为小红书是算法推荐制&#xff0c;将你的作品放在流量池中检测&#xff0c;满足要求就能进入下一个流量池…