Android 进程保活(一)

news2024/10/5 20:50:28

在这里插入图片描述

最近公司项目需求,需要给应用加入进程保活。
这里简述一下需求,由于App应用对接了蓝牙接收实时数据,并且数据量很大;
用户在操作App获取实时数据的时候,不可能一直看着手机屏幕,
这时候手机一般会有黑屏或者锁屏的情况,有时候就会导致数据丢失;
在我做的这款应用中,数据是非常重要的,所以数据丢失也是很致命的问题!

在如何处理这件问题上,自己也翻阅了很多资料。
毕竟这个问题已经很早就有人解决或者说处理过,但是网上资料过于多,并且杂,实用性也是有待考虑的,今天总结一下我自己应用过后,效果并且结果很nice的方案!

Android 进程保活

    • 进程保活
    • 进程初步了解
      • 如何查看进程基本信息
      • 进程划分
        • 前台进程(Foreground process)
        • 可见进程(Visible process)
        • 服务进程(Service process)
        • 后台进程(Background process)
        • 空进程(Empty process)
      • 内存阈值
    • 进程保活方案
      • 1像素原理:
      • 实战操作:
        • 创建一个1像素的Activity
        • 设置当前主题(异常)
        • 监听系统锁屏广播
        • 活动页调用
      • 实测结果

进程保活

对于解决这个问题,总共有如下几个技术:

1像素Activity前台服务账号同步Jobscheduler相互唤醒系统服务捆绑但是我在这里应用的是1像素Activity

进程初步了解

每一个Android应用启动后至少对应一个进程,有的是多个进程,而且主流应用中多个进程的应用比例较大。

如何查看进程基本信息

adb shell ps|grep <package_name>

在这里插入图片描述

23924  进程ID
23902  进程的父进程ID
com.harry.glucose.application 进程名

进程划分

Android 中的进程跟封建社会一样,分了三流九等,Android 系统把进程的划为了如下几种(重要性从高到低),网上多位大神都详细总结过(备注:严格来说是划分了 6 种)。

前台进程(Foreground process)

可见场景:

  1. 某个进程持有一个正在与用户交互的Activity并且该Activity正处于resume的状态。
  2. 某个进程持有一个Service,并且该Service与用户正在交互的Activity绑定。
  3. 某个进程持有一个Service,并且该Service调用startForeground()方法使之位于前台运行。
  4. 某个进程持有一个Service,并且该Service正在执行它的某个生命周期回调方法,比如onCreate()、
    onStart()或onDestroy()。
  5. 某个进程持有一个BroadcastReceiver,并且该BroadcastReceiver正在执行其onReceive()方法。

用户正在使用的程序,一般系统是不会杀死前台进程的,除非用户强制停止应用或者系统内存不足等极端情况会杀死。

可见进程(Visible process)

使用场景:

  1. 拥有不在前台、但仍对用户可见的 Activity(已调用 onPause())。
  2. 拥有绑定到可见(或前台)Activity 的 Service。

用户正在使用,看得到,但是摸不着,没有覆盖到整个屏幕,只有屏幕的一部分可见进程不包含任何前台组件,一般系统也是不会杀死可见进程的,除非要在资源吃紧的情况下,要保持某个或多个前台进程存活。

服务进程(Service process)

场景:

  • 某个进程中运行着一个Service且该Service是通过startService()启动的,与用户看见的界面没有直接关联。

在内存不足以维持所有前台进程和可见进程同时运行的情况下,服务进程会被杀死。

后台进程(Background process)

场景:

  • 在用户按了"back"或者"home"后,程序本身看不到了,但是其实还在运行的程序,比如Activity调用了onPause方法系统可能随时终止它们,回收内存。

空进程(Empty process)

  • 某个进程不包含任何活跃的组件时该进程就会被置为空进程,完全没用,杀了它只有好处没坏处,第一个干它!

内存阈值

上面是进程的分类,进程是怎么被杀的呢?

系统出于体验和性能上的考虑,app 在退到后台时系统并不会真正的 kill 掉这个进程,而是将其缓存起来。

打开的应用越多,后台缓存的进程也越多。

在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要 kill 掉哪些进程,以腾出内存来供给需要的 app, 这套杀进程回收内存的机制就叫 Low Memory Killer。

那这个不足怎么来规定呢,那就是内存阈值,我们可以使用cat /sys/module/lowmemorykiller/parameters/minfree 来查看某个手机的内存阈值。

在这里插入图片描述

其实系统在进程回收跟内存回收类似也是有一套严格的策略,可以自己去了解,大概是这个样子的,oom_adj 越大,占用物理内存越多会被最先 kill 掉,OK,那么现在对于进程如何保活这个问题就转化成,如何降低 oom_adj 的值,以及如何使得我们应用占的内存最少

进程保活方案

上面几种解决办法,最终我在项目应用中使用的是1像素Activity.

1像素原理:

基本思想,系统一般是不会杀死前台进程的。所以使得进程常驻,可以通过白名单;

但是这里我们在使用1像素的时候,只需要在锁屏或者黑屏的时候,在本进程开启一个activity,为了欺骗用户,让这个Activity的大小是1像素,并且透明无切换动态。在开屏幕的时候,把这个Activity关闭掉。

如果直接启动一个Activity,当我们按下back键返回桌面的时候,oom_adj的值是8,上面已经提到过,这个进程在资源不够的情况下是容易被回收的。

实战操作:

通过监听系统锁屏广播,来实现进程保活的方案。

创建一个1像素的Activity

/**
 * @author 拉莫帅
 * @date 2023/2/2
 * @address
 * @Desc 1像素
 */
public class PixelActivity extends Activity {

    private static final String TAG = PixelActivity.class.getSimpleName();

    public static void actionToLiveActivity(Context pContext){
        Intent intent = new Intent(pContext, PixelActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        pContext.startActivity(intent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pixel);
        Log.e(TAG, "onCreate: " );

        Window window = getWindow();
        //放到左上角
        window.setGravity(Gravity.START | Gravity.TOP);
        WindowManager.LayoutParams attributes = window.getAttributes();
        //宽高设计为1个像素
        attributes.width = 1;
        attributes.height = 1;
        //起始坐标
        attributes.x = 0;
        attributes.y = 0;
        window.setAttributes(attributes);

        ScreenManager.getInstance(this).setActivity(this);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart: " );
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy: " );
    }
}

设置当前主题(异常)

这里还有一步很重要,一定要给当前1像素Activity设置主题,要不然会有黑屏的情况出现,下一篇文章会给大家总结一下。

    <!-- 设置1像素主题-->
    <style name="APPTheme" parent="@android:style/Theme.Holo.NoActionBar">
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowAnimationStyle">@null</item>
        <item name="android:windowNoTitle">true</item>
    </style>

AndroidManifest.xml

<activity android:name=".PixelActivity"
            android:theme="@style/APPTheme"/>

监听系统锁屏广播

在屏幕关闭的时候把 PixelActivity 启动起来,在开屏的时候把 PixelActivity关闭掉,所以要监听系统锁屏广播,以接口的形式通知 MainActivity 启动或者关闭 PixelActivity。

/**
 * @author 拉莫帅
 * @date 2023/2/2
 * @address
 * @Desc 监听系统开/锁屏广播
 */
public class ScreenBroadcastListener {

    private Context mContext;

    private ScreenBroadcastReceiver mScreenReceiver;

    private ScreenStateListener mListener;

    public ScreenBroadcastListener(Context context) {
        mContext = context.getApplicationContext();
        mScreenReceiver = new ScreenBroadcastReceiver();
    }

    interface ScreenStateListener {

        void onScreenOn();

        void onScreenOff();
    }

    /**
     * screen状态广播接收者
     */
    private class ScreenBroadcastReceiver extends BroadcastReceiver {
        private String action = null;

        @Override
        public void onReceive(Context context, Intent intent) {
            action = intent.getAction();
            if (Intent.ACTION_SCREEN_ON.equals(action)) { // 开屏
                mListener.onScreenOn();
            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 锁屏
                mListener.onScreenOff();
            }
        }
    }

    public void registerListener(ScreenStateListener listener) {
        mListener = listener;
        registerListener();
    }

    private void registerListener() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        mContext.registerReceiver(mScreenReceiver, filter);
    }
}
public class ScreenManager {

    private Context mContext;

    private WeakReference<Activity> mActivityWref;

    public static ScreenManager gDefualt;

    public static ScreenManager getInstance(Context pContext) {
        if (gDefualt == null) {
            gDefualt = new ScreenManager(pContext.getApplicationContext());
        }
        return gDefualt;
    }
    private ScreenManager(Context pContext) {
        this.mContext = pContext;
    }

    public void setActivity(Activity pActivity) {
        mActivityWref = new WeakReference<Activity>(pActivity);
    }

    public void startActivity() {
        PixelActivity.actionToLiveActivity(mContext);
    }

    public void finishActivity() {
        //结束掉LiveActivity
        if (mActivityWref != null) {
            Activity activity = mActivityWref.get();
            if (activity != null) {
                activity.finish();
            }
        }
    }
}

活动页调用

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final ScreenManager screenManager = ScreenManager.getInstance(MainActivity.this);
        ScreenBroadcastListener listener = new ScreenBroadcastListener(this);
        listener.registerListener(new ScreenBroadcastListener.ScreenStateListener() {
            @Override
            public void onScreenOn() {
                screenManager.finishActivity();
            }

            @Override
            public void onScreenOff() {
                screenManager.startActivity();
            }
        });
    }
}

在屏幕关闭的时候把PixelActivity启动起来,在开屏的时候把PixelActivity关闭掉,所以要监听系统锁屏广播,以接口的形式通知MainActivity启动或者关闭PixelActivity。

实测结果

最终运行到真机华为、小米、红米Android 6.0、9.0、11.0、12.0结果ojbk,后期鸿蒙真机有待调试一下看看结果。



       最近忙里偷闲,↓↓↓↓【谁家de码农陈先生】↓↓↓↓,里面定时给大家分享技术博文、前方高能资讯内容!欢迎各位老板点赞关注,你们就是我的动力源泉!

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

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

相关文章

freertos学习之路1-裸机和rtos的区别

写在最前 由于工作需要&#xff0c;需要开始学习freertos的相关知识&#xff0c;本专题主要记录freertos的相关内容 参考&#xff1a; https://www.bilibili.com/video/BV19g411p7UT 正点原子视频 1. 裸机和rtos的多任务处理 试想一种场景&#xff0c;我们正在打游戏&#xff0…

java基础巩固-宇宙第一AiYWM:为了维持生计,大数据之ElasticSearch【ElasticSearch的概念、关于文档、索引的命令】~整起

ElasticSearch一、ElasticSearch基本概念1.ElasticSearch是什么&#xff1f;从哪来、来干啥&#xff1f;2.ElasticSearch与Solr的对比与选型&#xff1a;3.ES核心概念及相关操作4.ELK&#xff1a;拆箱即用的技术&#xff0c;解压完成就能用5.IK分词器6.ElasticSearch与SpringBo…

字节跳动青训营--前端day6

文章目录前言一、React的历史和应用二、React的设计思路1. ui编程痛点 & 对React的期望2. 组件化3.状态归属问题4. 生命周期三、React&#xff08;hooks&#xff09;1.Virtual DOM(虚拟 DOM)&#xff1a;2. diff算法四、React状态管理库五、应用级框架科普前言 仅以此文章…

spark 内存管理机制与相关参数调优

spark 内存管理 文章目录spark 内存管理spark 1.6 内存管理机制spark 2.0 内存管理机制spark 3.3.1 官方文档spark 内存相关参数调优spark 1.6 内存管理机制 https://0x0fff.com/spark-memory-management 统一内存管理 Spark 1.6 之后引入的统一内存管理机制&#xff0c;与静…

SpringbootAdmin:轻量级的Springboot监控组件,用过的都说好

简介 Springboot Admin是一个管理和监控Springboot项目的组件&#xff0c;分为服务端和客户端&#xff0c;两端通过http进行通信。由于其轻量级的特性&#xff0c;所以特别适合中小项目使用。 其效果图如下&#xff1a; 服务端配置 1&#xff0c;引入Springboot admin和Spri…

3.5 异常

1.概述 异常是一些用来封装错误信息的对象 它由异常的类型、提示信息、报错的行号提示三部分组成 2.异常的继承结构 3.异常的处理方式 当程序中遇到了异常,通常有两种处理方式:捕获或者向上抛出 当一个方法抛出异常,调用位置可以不做处理继续向上抛出,也可以捕获处理异常 大…

简单使用OpenGauss数据库

1 参考网站 # OpenGauss官网 https://opengauss.org/zh/2 Docker安装OpenGauss 下载OpenGauss docker pull enmotech/opengauss:3.0.0安装OpenGauss 容器参数说明&#xff1a; GS_PASSWORD&#xff1a;必须设置该参数&#xff0c;该参数设置了openGauss数据库的超级用户omm…

LeetCode 刷题系列 -- 1110. 删点成林

给出二叉树的根节点 root&#xff0c;树上每个节点都有一个不同的值。如果节点值在 to_delete 中出现&#xff0c;我们就把该节点从树上删去&#xff0c;最后得到一个森林&#xff08;一些不相交的树构成的集合&#xff09;。返回森林中的每棵树。你可以按任意顺序组织答案。示…

多域(跨域)计算「起势」,智能汽车赛道迎来新拐点

多域&#xff08;跨域&#xff09;计算平台正在成为新一轮市场争夺战的焦点。 就在今年CES展上&#xff0c;采埃孚推出多域功能版本的ProAI高性能计算平台&#xff0c;可以在不同的单板上支持基于域的ADAS、信息娱乐以及车身控制功能&#xff0c;并适配不同供应商的系统芯片以及…

前后端分离的陷阱

不管你设计的系统架构是怎么样&#xff0c;最后都是你的组织内的沟通结构胜出。这个观点一直在组织内不断地被证明&#xff0c;但也不断地被忽略。 前后端分离的利与弊 近几年&#xff0c;随着微服务架构风格的引入、前后端生态的快速发展、多端产品化的出现&#xff0c;前后…

vue前端框架应用案例(三)实现简单的echarts柱状图表

目录前端效果展示项目架构Seller.vueSellerPage.vueindex.jsApp.vuemain.jsindex.html后端源程序接口测试本博客内容参考黑马课程&#xff0c;详细信息请参考以下网址 Bilibili官方黑马课程&#xff1a;【echarts数据可视化项目】 前端 效果展示 项目架构 Seller.vue 该部分…

点云双边滤波

双边滤波&#xff08;Bilateral filter&#xff09;是一种非线性的滤波方法&#xff0c;是结合图像的空间邻近度和像素值相似度的一种折中处理&#xff0c;同时考虑空域信息和灰度相似性&#xff0c;达到保边去噪的目的。具有简单、局部的特点。双边滤波器的好处是可以做边缘保…

rootlogger 和 logger的关系

你是不是经常看到日志框架&#xff08;log4j、log4j2、logback等&#xff09;配置文件中有类似配置&#xff0c;但是始终搞不清楚啥意思&#xff1f;<root level"INFO"><appender-ref ref"CONSOLE" /><appender-ref ref"FILE" /&…

3.9.1Cache的基本概念和原理

文章目录一、引子二、工作原理三、局部性原理&#xff08;1&#xff09;空间局部性&#xff08;2&#xff09;时间局部性&#xff08;3&#xff09;总结四、性能分析&#xff08;1&#xff09;方案一&#xff08;2&#xff09;方案二&#xff08;3&#xff09;考题五、块&#…

LeetCode 212. 单词搜索 II 【字典树+回溯】

题目链接&#xff1a;https://leetcode.cn/problems/word-search-ii/ 给定一个 m x n 二维字符网格 board 和一个单词&#xff08;字符串&#xff09;列表 words&#xff0c; 返回所有二维网格上的单词 。 单词必须按照字母顺序&#xff0c;通过 相邻的单元格 内的字母构成&am…

VC+VB开发CAD重生记:CADEditorX 15.X Crack

CADEditorX是一个 ActiveX 组件&#xff0c;用于在任何支持 ActiveX 和 COM 技术的开发环境&#xff08;例如 C#、Visual C、Delphi、VB、JavaScript 等&#xff09;中向网页或正在开发的应用程序添加 CAD 功能。它可以查看、编辑、转换、打印和测量DWG、DXF、SVG、HPGL、PDF、…

python设计模式-构建器(Builder)设计模式,原型设计模式

构建器(Builder)设计模式构建器(Builder)模式是一种独特的设计模式&#xff0c;它有助于使用简单对象构建复杂对象并使用算法。 这种设计模式属于创建型模式。 在这种设计模式中&#xff0c;构建器类逐步构建最终对象。 该构建器独立于其他对象。构建器(Builder)模式的优点它提…

Anolis Os linux U盘 安装

Anolis OS系统8.4安装|U盘安装Anolis OS(龙蜥)8.4系统_白云一键重装系统 (baiyunxitong.com)https://www.baiyunxitong.com/jiaocheng/7092.html#:~:textAnolis%20OS%E7%B3%BB%E7%BB%9F8.4%E5%AE%89%E8%A3%85%E6%AD%A5%E9%AA%A4%EF%BC%9A%20%28%E5%88%B6%E4%BD%9CU%E7%9B%98%E5…

深度解读 python 实现 dbscan算法

DBScan (密度基于空间聚类) 是一种聚类算法&#xff0c;它通过找到图像中的密度峰值来对数据进行聚类。 文章目录DBScan 算法解释说明DBScan 算法的应用场景Python 实现的 DBScan 算法Python 实现 dbscan 高级算法再演示一种 python 实现 dbscan 算法的代码总结DBScan 算法解释…

共享模型之内存(二)

1.有序性 1>.JVM会在不影响正确性的前提下调整语句的执行顺序,思考下面一段代码: static int i; static int j; // 在某个线程内执行如下赋值操作 i ...; j ...;可以看到,至于是先执行i还是先执行j,对最终的结果不会产生影响.所以,上面代码真正执行时,既可以是: i ..…