Android Low Storage机制之DeviceStorageMonitorService

一、Android 版本

Android 13

二、low storage简介(DeviceStorageMonitorService)

设备存储监视器服务是一个模块,主要用来:

1.监视设备存储(“/ data”)。
2.每60秒扫描一次免费存储空间(谷歌默认值)
3.当设备的存储空间不足时生成“低存储”通知。 //updateNotifications
4.引导用户管理设备中安装的所有应用程序,并发送意图。 //updateBroadcasts
5.存储严重不足时显示警告对话框。
6.为AMS/PMS提供公共API以查询存储状态

三、DeviceStorageMonitorService关键代码介绍

3.1服务初始化
  1. DeviceStorageMonitorService初始化handler用于check
  2. SystemService.onStart()时,获取通知,添加devicestoragemonitor到Binder Service
  3. 执行check()
    public DeviceStorageMonitorService(Context context) {super(context);mHandlerThread = new HandlerThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND);mHandlerThread.start();mHandler = new Handler(mHandlerThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_CHECK_LOW:checkLow();return;case MSG_CHECK_HIGH:checkHigh();return;}}};}
    public void onStart() {final Context context = getContext();mNotifManager = context.getSystemService(NotificationManager.class);mCacheFileDeletedObserver = new CacheFileDeletedObserver();mCacheFileDeletedObserver.startWatching();// Ensure that the notification channel is set upPackageManager packageManager = context.getPackageManager();boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);if (isTv) {mNotifManager.createNotificationChannel(new NotificationChannel(TV_NOTIFICATION_CHANNEL_ID,context.getString(com.android.internal.R.string.device_storage_monitor_notification_channel),NotificationManager.IMPORTANCE_HIGH));}publishBinderService(SERVICE, mRemoteService);publishLocalService(DeviceStorageMonitorInternal.class, mLocalService);// Kick off pass to examine storage statemHandler.removeMessages(MSG_CHECK_LOW);mHandler.obtainMessage(MSG_CHECK_LOW).sendToTarget();}
3.2 check  data分区

frameworks/base/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java

注意这两个方法

if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel))

if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel))

主要逻辑是进入低内存的时候,判断之前的状态,如果之前是正常的,就发通知,如果一直处理低内存状态,就不处理,避免重发通知处理

如果是低内存到正常内存状态,那就自动把通知取消掉

    @WorkerThreadprivate void checkLow() {Slog.w(TAG, "AAAAA >>> checkLow()");final StorageManager storage = getContext().getSystemService(StorageManager.class);final int seq = mSeq.get();// Check every mounted private volume to see if they're low on spacefor (VolumeInfo vol : storage.getWritablePrivateVolumes()) {Slog.w(TAG, "AAAAA >>> checkLow() updateNotifications");final File file = vol.getPath();final long fullBytes = storage.getStorageFullBytes(file);final long lowBytes = storage.getStorageLowBytes(file);//默认500M或总空间的%5// Automatically trim cached data when nearing the low threshold;// when it's within 150% of the threshold, we try trimming usage// back to 200% of the threshold.if (file.getUsableSpace() < (lowBytes * 3) / 2) {final PackageManagerInternal pm =LocalServices.getService(PackageManagerInternal.class);//lowBytes的1.5倍容量时触发freeStoragetry {pm.freeStorage(vol.getFsUuid(), lowBytes * 2, 0);} catch (IOException e) {Slog.w(TAG, e);}}// Send relevant broadcasts and show notifications based on any// recently noticed state transitions.final UUID uuid = StorageManager.convert(vol.getFsUuid());final State state = findOrCreateState(uuid);final long totalBytes = file.getTotalSpace();//data总大小final long usableBytes = file.getUsableSpace();//可使用大小int oldLevel = state.level;int newLevel;//判断是LEVEL_LOW,LEVEL_FULL还是LEVEL_NORMALif (mForceLevel != State.LEVEL_UNKNOWN) {// When in testing mode, use unknown old level to force sending// of any relevant broadcasts.oldLevel = State.LEVEL_UNKNOWN;newLevel = mForceLevel;} else if (usableBytes <= fullBytes) {newLevel = State.LEVEL_FULL;} else if (usableBytes <= lowBytes) {newLevel = State.LEVEL_LOW;} else if (StorageManager.UUID_DEFAULT.equals(uuid)&& usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {newLevel = State.LEVEL_LOW;} else {newLevel = State.LEVEL_NORMAL;}// Log whenever we notice drastic storage changesif ((Math.abs(state.lastUsableBytes - usableBytes) > DEFAULT_LOG_DELTA_BYTES)|| oldLevel != newLevel) {EventLogTags.writeStorageState(uuid.toString(), oldLevel, newLevel,usableBytes, totalBytes);state.lastUsableBytes = usableBytes;}//发送通知updateNotifications(vol, oldLevel, newLevel);//发送广播updateBroadcasts(vol, oldLevel, newLevel, seq);state.level = newLevel;}//没有check消息,继续60s检测一次// Loop around to check again in future; we don't remove messages since// there might be an immediate request pending.if (!mHandler.hasMessages(MSG_CHECK_LOW)) {mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK_LOW),LOW_CHECK_INTERVAL);}if (!mHandler.hasMessages(MSG_CHECK_HIGH)) {mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK_HIGH),HIGH_CHECK_INTERVAL);}}
 private void updateNotifications(VolumeInfo vol, int oldLevel, int newLevel) {Slog.w(TAG, "AAAAA >>> updateNotifications() oldLevel="+oldLevel+",,,,,newLevel="+newLevel);final Context context = getContext();final UUID uuid = StorageManager.convert(vol.getFsUuid());if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {//调起通知Slog.w(TAG, "AAAAA >>> updateNotifications() do Notification");Intent lowMemIntent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);lowMemIntent.putExtra(StorageManager.EXTRA_UUID, uuid);lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);final CharSequence title = context.getText(com.android.internal.R.string.low_internal_storage_view_title);final CharSequence details = context.getText(com.android.internal.R.string.low_internal_storage_view_text);PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent,PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);Notification notification =new Notification.Builder(context, SystemNotificationChannels.ALERTS).setSmallIcon(com.android.internal.R.drawable.stat_notify_disk_full).setTicker(title).setColor(context.getColor(com.android.internal.R.color.system_notification_accent_color)).setContentTitle(title).setContentText(details).setContentIntent(intent).setStyle(new Notification.BigTextStyle().bigText(details)).setVisibility(Notification.VISIBILITY_PUBLIC).setCategory(Notification.CATEGORY_SYSTEM).extend(new Notification.TvExtender().setChannelId(TV_NOTIFICATION_CHANNEL_ID)).build();notification.flags |= Notification.FLAG_NO_CLEAR;mNotifManager.notifyAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,notification, UserHandle.ALL);FrameworkStatsLog.write(FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED,Objects.toString(vol.getDescription()),FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED__STATE__ON);} else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {//取消通知Slog.w(TAG, "AAAAA >>> updateNotifications() cancel Notification");mNotifManager.cancelAsUser(uuid.toString(), SystemMessage.NOTE_LOW_STORAGE,UserHandle.ALL);FrameworkStatsLog.write(FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED,Objects.toString(vol.getDescription()),FrameworkStatsLog.LOW_STORAGE_STATE_CHANGED__STATE__OFF);}}
    private void updateBroadcasts(VolumeInfo vol, int oldLevel, int newLevel, int seq) {if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, vol.getFsUuid())) {// We don't currently send broadcasts for secondary volumesreturn;}//lowStorage广播action  ACTION_DEVICE_STORAGE_LOWfinal Intent lowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS).putExtra(EXTRA_SEQUENCE, seq);//正常Storage广播action  ACTION_DEVICE_STORAGE_OKfinal Intent notLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS).putExtra(EXTRA_SEQUENCE, seq);if (State.isEntering(State.LEVEL_LOW, oldLevel, newLevel)) {//只发送一次广播ACTION_DEVICE_STORAGE_LOW,粘性广播,进程注册肯定会收到广播getContext().sendStickyBroadcastAsUser(lowIntent, UserHandle.ALL);} else if (State.isLeaving(State.LEVEL_LOW, oldLevel, newLevel)) {//恢复正常移除lowIntent粘性广播,发送normal的普通广播getContext().removeStickyBroadcastAsUser(lowIntent, UserHandle.ALL);getContext().sendBroadcastAsUser(notLowIntent, UserHandle.ALL);}final Intent fullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT).putExtra(EXTRA_SEQUENCE, seq);final Intent notFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT).putExtra(EXTRA_SEQUENCE, seq);//发送FULL Storage广播ACTION_DEVICE_STORAGE_FULLif (State.isEntering(State.LEVEL_FULL, oldLevel, newLevel)) {getContext().sendStickyBroadcastAsUser(fullIntent, UserHandle.ALL);} else if (State.isLeaving(State.LEVEL_FULL, oldLevel, newLevel)) {getContext().removeStickyBroadcastAsUser(fullIntent, UserHandle.ALL);getContext().sendBroadcastAsUser(notFullIntent, UserHandle.ALL);}}
    private static class State {private static final int LEVEL_UNKNOWN = -1;private static final int LEVEL_NORMAL = 0;//空间正常private static final int LEVEL_LOW = 1;//空间低private static final int LEVEL_FULL = 2;//空间满了
}
3.3 低内存判断值

frameworks/base/core/java/android/os/storage/StorageManager.java

  //总空间的百分比(默认是5%)和 500M 的最小值 作为最低空间提醒的标准public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW = 5;private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);public long getStorageLowBytes(File path) {final long lowPercent = Settings.Global.getInt(mResolver,Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW);final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;final long maxLowBytes = Settings.Global.getLong(mResolver,Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES);return Math.min(lowBytes, maxLowBytes);}

四、查看剩余空间的方法

1、df -h 查看 data 目录空间


 

2、从桌面"设置"应用中查看存储

自动填满磁盘空间apk:https://download.csdn.net/download/banzhuantuqiang/89331283

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

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

相关文章

亚信安慧AntDB:数字化转型的关键力量

在数字化浪潮的推动下&#xff0c;数据已成为推动经济发展的新动力。亚信安慧AntDB数据库凭借其卓越性能和灵活的应用能力&#xff0c;在满足我国IT系统与产业数据多样化需求的过程中发挥着重要作用。AntDB数据库承载着无限可能&#xff0c;随着国家数字化转型的不断深入&#…

【项目】教你手把手完成博客系统(三)显示用户信息 | 实现退出登录 | 实现发布博客

文章目录 教你手把手完成博客系统&#xff08;三&#xff09;7.实现显示用户信息1.约定前后端交互接口2.前端通过ajax发起请求3.服务器处理请求 8.实现退出登录1.约定前后端的接口2.前端发起请求3.服务器处理请求 9.实现发布博客1.约定前后端的交互接口2.前端构造请求3.服务器处…

回溯法的重要延展题目

留个坑&#xff01;&#xff01;&#xff01; 1.332重新安排行程 332. 重新安排行程 - 力扣&#xff08;LeetCode&#xff09; 2.51 N皇后问题 51. N 皇后 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 印象中&#xff0c;对不同角度进行扫描&#xff0c;从…

web自动化-JavaScript操作

做UI自动化的时候&#xff0c;有些操作无法直接通过selenium自带方法操 作成功&#xff0c;那么就需要借助前端js操作实现。 比如浏览器的滚动条这种不是html页面的内容&#xff0c;无法直接通过selenium 控制到。需要借助JavaScript控制。比如有些点击操作无法通过普通点击鼠…

齐护K210系列教程(三十)_多任务切换

多任务切换 1&#xff0c;任务1的设定2&#xff0c;任务2的设定3&#xff0c;主程序4&#xff0c; 课程资源联系我们 在开发项目时&#xff0c;我们常会用到AIstart的多个任务来切换应用&#xff0c;比如当我识别到某种卡片时&#xff0c;要切换到别的任务&#xff0c;这样就要…

链游中的代币(Token)或加密货币(Cryptocurrency)是如何产生和使用的?

在区块链游戏&#xff08;链游&#xff09;中&#xff0c;代币和加密货币不仅是游戏经济的核心&#xff0c;也是连接现实世界与虚拟游戏世界的桥梁。这些数字货币不仅赋予了游戏内资产的真实价值&#xff0c;还为玩家提供了全新的互动和交易方式。下面&#xff0c;我们将深入探…

CentOS-7安装教程

目录 安装 修改主机名 配置静态IP 镜像下载地址 https://mirrors.aliyun.com/centos/7.9.2009/isos/x86_64/CentOS-7-x86_64-DVD-2009.iso VMware Workstation Pro下载 VMware Workstation Pro各版本下载&#xff08;2024.5.5之后&#xff09;(Windows与Linux安装包不限…

【okhttp】小问题记录合集

can’t create native thread 问题描述 OkHttpClient 每次使用都new创建&#xff0c;造成OOM&#xff0c;提示can’t create native thread… 问题分析 没有将OkHttpClient单例化. 每个client对象都有自己的线程池和连接池&#xff0c;如果为每个请求都创建一个client对象&a…

使用v-model完成数据的双向绑定

创作灵感 面试问道了&#xff0c;没答出来&#xff0c;呜呜呜~ v-model实现双向绑定的原理 首先我们要知道&#xff0c;v-model实现的双向绑定其实只是props与emit的简化版本。其中&#xff0c;props负责父组件向子组件传递值&#xff0c;emit负责子组件向父组件传递值。 在…

视频推拉流EasyDSS系统如何在清理缓存文件的同时不影响缓存读写?

视频推拉流EasyDSS视频直播点播平台可提供一站式的视频转码、点播、直播、视频推拉流、播放H.265视频等服务&#xff0c;搭配RTMP高清摄像头使用&#xff0c;可将无人机设备的实时流推送到平台上&#xff0c;实现无人机视频推流直播、巡检等应用。 有用户咨询&#xff0c;视频推…

Git 的安装和使用

一、Git 的下载和安装 目录 一、Git 的下载和安装 1. git 的下载 2. 安装 二、Git 的基本使用-操作本地仓库 1 初始化仓库 1&#xff09;创建一个空目录 2&#xff09;git init 2 把文件添加到版本库 1&#xff09;创建文件 2&#xff09;git add . 3&#xff09;g…

在SpringBoot自定义指标并集成Prometheus和Grafana监控

前沿 写这篇文章的目的是发现自己整天埋头写业务代码但忽略了主动发现问题的能力&#xff0c;这里指的是监控和报警。结合工作中发现Prometheus和Grafana还是主流一些。本文介绍如何使用自定义指标&#xff0c;并使用Prometheus进行监控并报警&#xff0c;同时在 Grafana 进行…

重学java 40.多线程 — 死锁和线程状态

—— 24.5. 一、死锁 1.死锁介绍&#xff08;锁嵌套就有可能产生死锁&#xff09; 指的是两个或者两个以上的线程在执行的过程中由于竞争同步锁而产生的一种阻塞现象;如果没有外力的作用,他们将无法继续执行下去,这种情况称之为死锁 例&#xff1a; 两线程处于互相等待的状态&a…

上位机图像处理和嵌入式模块部署(mcu常见三种烧录方法)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 和单纯的windows上位机开发、嵌入式linux开发不一样&#xff0c;mcu的开发&#xff0c;是需要通过烧录器把编译好的镜像烧入到开发板里面的。这是很…

Spark RDD 操作实战

Spark RDD 基础 更多spark相关知识请查看官方接口文档 PySpark是Spark的PythonAPI&#xff0c;允许Python调用Spark编程模型。 配置spark环境 !apt-get install openjdk-8-jdk-headless -qq > /dev/null !wget -q www-us.apache.org/dist/spark/spark-2.4.8/spark-2.4.8…

16、Go Gin 深入理解Gin中间件

中间件介绍 Gin框架允许开发者在处理请求的过程汇总&#xff0c;加入用户自己的钩子&#xff08;Hook&#xff09;函数 这个钩子函数就叫中间件&#xff0c;中间件适合处理一些公共的业务逻辑 比如登录认证&#xff0c;权限校验、数据分页、记录日志、耗时统计等 1、全局中…

STM32通用定时器的应用实例(基于STM32F103)

目录 概述 1 STM32Cube配置项目 1.1 准备环境 1.2 配置项目参数 1.3 生成Project 2 HAL函数 2.1 初始化函数&#xff1a;HAL_TIM_Base_Init 2.2 中断模式启动定时器函数&#xff1a;HAL_TIM_Base_Start 2.3 定时器回调函数&#xff1a; HAL_TIM_PeriodElapsedCallback…

ElasticSearch操作之重置密码脚本

ElasticSearch操作之重置密码脚本 #!/bin/bash # 使用样例 ./ES密码重置.sh 旧密码 新密码# 输入旧密码 es_old_password$1# 设置新的密码变量 es_password$2# 正确响应 es_reponse{"acknowledged":true}# 检查Elasticsearch是否在运行 if pgrep -f elasticsearch &g…

2024年5月计算机视觉论文推荐:包括扩散模型、视觉语言模型、图像编辑和生成、视频处理和生成以及图像识别等各个主题

我们今天总结下2024年5月发表的最重要的论文&#xff0c;重点介绍了计算机视觉领域的最新研究和进展&#xff0c;包括扩散模型、视觉语言模型、图像编辑和生成、视频处理和生成以及图像识别等各个主题。 Diffusion Models 1、Dual3D: Efficient and Consistent Text-to-3D Ge…