探索App保活黑科技

2023-11-10

我们来聊聊目前可用的App保活技术。这些方法在目前看来都还可以用,并且效果也很好。但无法保证长期可用,因为Android操作系统本身可能会更改其策略,而且各厂商在定制Android时也会引入自家节电策略。这些都有可能破坏我们原本可用的保活逻辑,所以当Android系统本身发生策略改变时,还需要去查看官方文档,关注有关影响保活的策略改变,并适配它们。此外,针对各厂商定制的系统,还应该尽可能地多做测试,尽可能地确保App在大部分设备上是正常运行的。

基础知识

  • Android进程优先级
    一般情况下,Android会尽可能保持应用进程,但在特定场景下会对进程进行kill,例如为了清除旧进程来回收内存等。为了区分哪些进程最先被回收清理,而哪些不会,有一个优先级别,这就是Android的进程优先级,具体包括下面5种
Foreground/Active process 用户当前操作的进程,包括用户正在交互的Activity,绑定用户正在交互Activity的Service,使用 startForeground 的 Service,正在执行 onReceive 的 BroadcastReceiver 等
Vsible process 会影响用户所见内容的进程,如onPause 状态的 Activity 等
Service process 后合服务,如正在运行 startservice 启动的 Service。
Background process 对用户交互无影响,如 onStop 状态的 Activity 等,系统可能随时对其进行终止。
Empty process 一般用作缓存以缩短下次启动时间,系统往往会终止这些空进程。
  • Android进程回收策略
    Android 中主要通过 LMK (LoW Memory Killer)来对进程进行回收管理,LMK是在 Android
    系统内存不足而选择kill 部分进程以释放空间时,生死大权的决定者,其基于 Linux 的 OOM
    机制,阀值定义如下面所示 (lowmemorykiller 文件中),当然也可以通过系统的 init.ro 实现
    自定义。
static uint32_t lowmem_debug_level = 2;
static int lowmem_adj[6] = {
	0,
	1,
	6,
	12,
};
static int lowmem_adj_size = 4;
static int lowmem_minfree[6] = {
	3 * 512,	/* 6MB */
	2 * 1024,	/* 8MB */
	4 * 1024,	/* 16MB */
	16 * 1024,	/* 64MB */
};
static int lowmem_minfree_size = 4;

在LMK 中通过进程的oom_adj 与占用内存的大小决定要杀死的进程,oom_adj值越小,越不容易被杀死。其中,lowmem_minfiee 是杀进程的时机,谁被杀,则取决于 lowmem_adj,具体值参考ProcessList 类。
在init.rc中定义了init 进程(系統进程)的oom_adj 为-16,其不可能会被杀死(init 的PID是1),而前台进程是 0。(这里的前台进程是指用户正在使用的 activity所在的进程),例如用户按Home 键回到桌面时的优先级是6,普通的 Service 的进程优先级是 8。

  • 查看某个App的进程
    为了验证我们的保活方法是否有效,最直观的方法是通过 adb 命令查看具体 App 的进程信息,具体命令如下。
adb shell
ps|grep 进程名
cat /proc/PID/oom_adj //其中PID是上述grep得到的进程号
  • Linux am命令
    am 命令是Android 系统中通过adb shell 启动某个 Activity、Service、拨打电话、启动浏
    览器等操作 Android 的命令,其源码在 Am.java中,在shell 环境下执行 am 命令实际是启动
    一个线程执行 Am.iava 中的主函数(main 方法),am 命令后跟的参数都会当作运行时参
    数传递到主函数中,主要实现在 Am.java 的run 方法中。
例如:拨打电话
adb shell
am start -a android.intent.action.CALL -d tel:10086
  • NotificationListenerService
    NotificationListenerService用来监听通知的发送以及移除和排名位置变化,如果我们注册了这个服务,当系统任何一条通知到来或者被移除掉时,都能通过这个服务监听到,甚至做一些管理工作。

添加电池优化白名单

App保活的第一种就是添加白名单,让系统的电池优化机制忽略相应的App。
原生Android进行白名单的添加很方便,各品牌厂商的系统由于定制化了原生Android,在添加白名单的方法上略有不同。除了按照原生Android的逻辑进行添加外,还需要对不同品牌的机型做单独的逻辑处理。

  • 原生Android方法
    原生Android的电池优化机制从API Level引入,其目的在于通过使App进入“休眠”状态而节约电量开销。我们这里需要让App保持运行,于是理所当然要让该机制忽略我们开发的App。
    首先在AndroidManifest中声明权限:
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>

该权限无须用户批准,直接声明即可。
程序运行时先检查自身是否已经在电池优化白名单中,如果在,就无须任何操作;如果不在,就弹出请求添加到白名单的窗口,这个窗口由Android系统提供。无论用户选择拒绝添加活接受添加,我们都要获取用户的选择结果,以便今后在适当的时机弹出提示。

public class BackActivity extends Activity {
    @Override
    protected void onCreate( Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initData();
    }


    private void initData(){
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            if(!isIgnoreBatteryOptimizationStatus(this)){
                requireIgnoreBatteryOptimization(this);
            }
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    private boolean isIgnoreBatteryOptimizationStatus(Activity context){
        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        return powerManager != null && powerManager.isIgnoringBatteryOptimizations(context.getPackageName());
    }

    @RequiresApi(api = Build.VERSION_CODES.M)
    private void requireIgnoreBatteryOptimization(Activity context){
        try{
            Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
            intent.setData(Uri.parse("package:"+context.getPackageName()));
            context.startActivityForResult(intent,0x00);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode == 0x00){
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
                Log.d("Activity","Ignore Battery Optimization Status is "+isIgnoreBatteryOptimizationStatus(this));
            }
        }
    }
}
  • 定制化Android操作系统的处理
    对于各厂商定制化的Android操作系统,除了按照原生Android的方法添加白名单外,还需要添加到各厂商手机管家或安全管家之类App的白名单中。很多App具有跳转到手机管家的功能,并给予用户提示,引导用户进行设置。想要实现这种功能,原理很简单。界面的跳转实际上就是常见的startActivity方法。难点在于如何获取跳转目标的相关设置的包名和activity类名。
    下面介绍三种获取方法:
  1. 使用logcat,大部分手机都是可以看见内置的日志的
  2. 使用RE浏览器
  3. Google上查询

白名单内的App为何会被杀

我们都知道,Andnoid 操作系统有这样的机制:当内存空间不足、系统资源过低时,系统会按照 App 的优先级清理掉那些重要度不高的程序。如果我们的推送服务恰好在被清理的名单中,那么被清理只是时问问题。可见,在测试人员处能及时发现这个问题是当么宝费。毕竟用户的手机可能会同时运行更多的 App,被清理的可能性会更大,也可能会更加频繁。
那么,如何判断确实是由于内存不足造成App被杀呢?这就要通过获取App运行状态来判断了。
首先,我们要拿到那台可以复现问题的机器,然后到去看看它触发内存清理的时刻。
Android 系统中使用一个配置文件来表示内存阀值,该值根据 App 不同运行状态有多个值。一旦可用内存小于等于这个值,相应运行状态的 App 就会被清理掉。下表是配置了2GB内存的小米手机的内存清理阀值以及对应的App的运行状态。

运行状态 內存阈值
FOREGROUD APP (前台进程) 14746(大约 57MB)
VISIBLE APP(可见进程) 18432 (72MB)
SECONDARY SERVER(次要服务) 22118 (大约86MB)
HIDDEN APP(后台进程) 25805(大约 100MB)
CONTENT PROVIDER(内容提供者) 40000(大约156MB)
EMPTY APP(空进程) 55000 (44 215MB)

很明显,在这合设备上,一旦可用内存小于或等于 72MB,只要程序不在前台运行,将会被清理掉。
这里要特别说明前台进程和可见进程的区别。一般来讲,它们本质的不同是当前是否正在处于与用户交互的状态。比如,你现在正在和一个名为A的 App 进行交互,突然名为B的App弹出了一个对话框,此时A 由前合进程转为可见进程(此时 A 的界面被 B的对话框挡住,虽然部分可见,但用户无法直接和 A 交互),B变为前合进程。
注意,即使是相同生产厂商,内存阙值也会由于设备本身搭载内存大小的不同而有所变化。所以,笔者建议在做保活测试时,最好选择一款内存小的机器,并同时运行多个市场上常见的 App,让问题尽快暴露。
只知道系统清理内存的原则还不够,还需要了解你开发的App占用内存以及各进程优先级的情况,这样才能确定问题是否确实是由于内存不足造成的。
要获取App的内存占用量,一种方法是利用Android Profiler,只要App处于Debug模式,就可以使用;另一种方法是通过adb命令,该方法适用型更广泛,不要求App必须对于Debug模式,而且能看到整个手机的内存占用情况。

adb shell dumpsys meminfo <packName>

接着,获取该App的运行优先级(需要取得Root权限)

cat /proc/进程ID/oom_adj

OOM_ADJ值 含义
-16 一般指system进程
-11 框架进程或常驻App,即AndroidManifest中persistent值为true的app
0 前台App
1 可见应用或有persistent进程关联的Service或Provider
2 可感知进程,通常指startService且调用了startForeground的服务
5/8 startService启动的服务,但没有Activity在运行
6 Home Launcher
9-16 缓存进程,通常是切回Home的App、Empty进程等

看到这,你或许明白了那些网上所谓的通告提高优先级或保持前台的方式来保活App的原理了。实际上,它们就是利用OOM_ADJ值的不同,努力多争取进程不被清理掉的可能。
此外,对于没有Root权限的设备,还可以通过执行:

adb shell dumpsys meminfo

通过名称来获取进程优先级
系统总共的可用内存量可以通过以下命令查看:

adb shell cat /proc/menifo

现在,有了系统可用内存、App所属进程优先级以及App内存占用量的信息,足够我们做出判断了。

重新设计推送服务

如何保证推送服务在设备上稳定运行呢?
单方面提升进程优先级,或直接长时间播放无声音频。。。如果每个App都如此暴力,最终的结果就是手机整体运行体验不佳。

  • 推送服务的实现原则
    正如前文所达,笔者不提倡用暴力的方式实现保活,这样会降低用户的整体使用体验,还会徒增手机功耗。所以,要实现推送服务的保活,一方面要结合前面的内容,引导用户添加白名单;另一方面,要使用“轻量级”进程,降低内存占用率。也就是说,让每个 App 的推送服务都单独占用极低的内存。同时,当 App 不再是前台应用时,及时清理内存消耗,让消息接收的逻辑只存在于推送服务的进程中。反过来,该进程也只包含推送服务的相关逻辑。
    另外,由于这样的进程占用的内存极少,可以适当使用提升优先级以及被杀后及时复活的策咯来保护这个进程。
  • 推荐推送服务保活的方式
  1. 创建轻量级进程
    示例如下:
class CounterService extends Service{
    @Override
    public void onCreate() {
        super.onCreate();
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

接着,在AndroidManifest文档中注册该Service

<service 
   android:name = ".CounterService"
   android:process = ":counter"/>

此外,如果读者想要进一步增强进程的存活能力,还可以监听某些系统广播,实现更快地重启进程。当然,这要求用户己经将 App 添加到白名单作为前提。
另一方面,由于用户将我们的 App 添加到白名单中,这就等于充分相信 App 不会在手机上胡作非为,包括系统资源的滥用,这是作为开发者特别要警戒的一点。
2. 集成厂商推送
另一种方式是集成各厂商的推送服务。别急,这里并不是要求开发者去各厂商分别下载推送 SDK,分别阅读开发文档,这样太费事了。
笔者建议使用极光推送,截至作者撰稿时,该平台已经集合了华为、小米、魅族、Vivo 和Oppo 五家手机厂商的推送通道,此外,还支持 Google 的Firebase 以及自建推送通道。
有了这几家厂商的推送通道,实际上就覆盖了市场上的大部分用户。虽然通过极光使用各厂商自己的推送通道可以达到一次集成的目的,但各厂商的 AppID、AppKey 等参数还需要分别进行注册才行。但是,接入这种第三方推送有一个弊端,会被流量劫持。

如果想要进一步了解进程保活相关的技术,可以参考下面这篇博客:《一种提高Android应用进程存活率新方法 》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

探索App保活黑科技 的相关文章

随机推荐

  • 多维时序

    多维时序 MATLAB实现TPA LSTM 时间注意力注意力机制长短期记忆神经网络 多输入单输出 目录 多维时序 MATLAB实现TPA LSTM 时间注意力注意力机制长短期记忆神经网络 多输入单输出 预测效果 基本介绍 环境介绍 程序设计
  • Bellman-Ford算法和Dijkstra算法分别适用的情况有何不同?

    Bellman Ford算法和Dijkstra算法分别适用的情况有何不同 Bellman Ford 求单源最短路 可以判断有无负权回路 若有 则不存在最短路 时效性较好 时间复杂度O VE Bellman Ford算法是求解单源最短路径问题
  • 仅做笔记用:Stable Diffusion 通过 ControlNet 扩展图片 / 扩图

    发觉之前的 Outpainting 脚本效果仍旧不是很理想 这里又找了一下有没有效果更好的途径来扩图 于是就找到了通过 ControlNet 的方式来实现效果更好的扩图 这里临时记录一下在 Stable Diffusion 怎么使用 Con
  • 【VS编译器】使用scanf函数的方法

    1 VS中使用scanf函数的问题 VS中运行以下代码 include
  • windows杀死nginx进程

    查看80端口号列表 tasklist findstr 80 根据端口号查看nginx tasklist findstr 28024 杀死nginx进程 taskkill f t im nginx exe
  • OC门与OD门以及线与逻辑

    OC Open Collector 门又叫集电极开路门 主要针对的是BJT电路 从上往下依次是基极 集电极 发射极 OD Open Drain 门又叫漏极开路门 主要针对的是MOS管 从上往下依次是漏极 栅极 源极 线与逻辑指的是两个输出端
  • [从零开始学习FPGA编程-22]:进阶篇 - 架构 - FPGA内部硬件电路的设计与建模

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 目录 前言 第1章 什么是数字硬件电路 1 1 什么是硬件电路
  • 使用docker-compose启动docker镜像报错exec ./docker-entrypoint.sh: no such file or directory

    报错原因 exec docker entrypoint sh no such file or directory 我们可以在linux下通过cat v命令查看 sh文件 例 cat v Dockerfile WORKDIR tools M
  • 二维向量叉积的几何意义

    叉乘 cross product 相对于点乘 叉乘可能更有用吧 2维空间中的叉乘是 V1 x1 y1 X V2 x2 y2 x1y2 y1x2看起来像个标量 事实上叉乘的结果是个向量 方向在z轴上 上述结果是它的模 在二维空间里 让我们暂时
  • Zotero 知网 PDF与CAJ 抓取模式切换

    Zotero可以通过安装茉莉花插件 jasminum 一键安装https github com l0o0 translators CN内的中文翻译器 从而实现对知网文献元数据以及相应PDF的抓取 但是在使用过程中我发现 由于知网对于学位论文
  • Visual Studio开始Python编程 && Windows下用PyCharm

    Windows搭建python开发环境 首先需要去python的官网下载环境 鼠标移动到Downloads的tab上 在这里可以下载 python的环境还是很人性化的 没有那么多罗里吧嗦的配置什么的 下载好以后直接无脑next就行了 直到f
  • VS中为QT项目添加多个ui

    在VS中创建QT项目 如下图所示 这里已经有一个 ui文件 现在的目标是再添加一个 ui文件并使之在项目中可用 添加新的 ui文件 右击项目 gt 添加 gt 新建项 gt Visual C gt QT 选择一个模板 如 Qt Dialog
  • 目标检测模型从训练到部署,其实如此简单

    目标检测的任务是找出图像中所有感兴趣的目标 物体 确定它们的类别和位置 是计算机视觉领域的核心问题之一 目标检测已应用到诸多领域 比如如安防 无人销售 自动驾驶和军事等 在许多情况下 运行目标检测程序的设备并不是常用的电脑 而是仅包含必要外
  • 分布式事务管理(Seata)

    文章目录 1 概述 分布式事务问题 什么是Seata Seata术语 怎么用 Windows安装 Docker安装 MySQL nacos seata 2 配置官网案例 分析业务逻辑 创建数据库 订单模块 建Module POM YML f
  • Qt 的一些心得

    一 背景刷成黑色 前景色设为白色 方法一 paltette方式 经测试 该方法不会影响到其他控件 推荐使用 QPalette bgpal palette bgpal setColor QPalette Background QColor 0
  • Vue.js基本指令

    目录 插值表达式 v html和v text指令 例1 v on指令 例1 v model指令 例1 v if指令 例1 判断二月份有几天 例2 比较两个数的大小 v show指令 v if和v show的区别 v for 指令 遍历数组
  • 【skynet】 skynet 之 snax

    local skynet require skynet local snax require snax local p calc nil 初始化函数 function init local seed math floor skynet ti
  • xss挑战之旅level 1 - level 4

    Level 1 观察url中的 name参数 向网站提交数据 然后返回到页面上 Level 2 第二关是可以在输入框中输入一个数据提交给服务器 然后提交的数据是会被显示到页面上的 此时再使用第一关的payload已经是不行了 看一下源码 在
  • GitHub建立个人网站(一)

    github可以当做自己的个人博客网站 做一个这样的网站的好处有哪些 装 X 如果网站够炫的话 ps 以后抽空闲下来了 肯定要弄个好看的页面 好好装X 很好的用来总结自己所学的知识 可能因为流量的问题 github上面确实只能当做记载的 但
  • 探索App保活黑科技

    我们来聊聊目前可用的App保活技术 这些方法在目前看来都还可以用 并且效果也很好 但无法保证长期可用 因为Android操作系统本身可能会更改其策略 而且各厂商在定制Android时也会引入自家节电策略 这些都有可能破坏我们原本可用的保活逻