Android Service之设备存储空间监控 DeviceStorageMonitorService

简介: Android Service之设备存储空间监控 DeviceStorageMonitorService

Android Service之设备存储空间监控


在负责文件系统模块的过程中,经常会碰到由于系统空间被消耗完而导致的问题,因此要确保为系统功能(如数据库同步)保留一定的空间。在功能机中一般是由文件系统模块预留,那么在Android系统是怎样对设备存储空间进行管理和监控的呢?

如果你在使用Android手机时有过把memory填满或者即将填满的经历,也许你会注意到在这种情况下手机的Notifications栏会有“Storage space running out”的通知。当点开该通知你会发现Setting-->Storage settings –>Device memory 下会有如下提示:Not enough storage space.

这个服务的实现是在frameworks\base\services\core\java\com\android\server\storage\DeviceStorageMonitorService.java。DeviceStorageMonitorService类实现了一个监控设备上存储空间的服务。如果设备的剩余存储空间小于某一个阀值(默认是存储空间的10%,这个值可以设置)时将会向用户发送剩余空间不足的警告,让用户释放一些空间。

下面就分析一下这个类。首先看一下该服务是如何被添加进来的。在frameworks\base\services\java\com\android\server\SystemServer.java中使用ServiceManager.addService()来添加系统服务: startOtherServices

t.traceBegin("StartDeviceMonitor");
mSystemServiceManager.startService(DeviceStorageMonitorService.class);
t.traceEnd();

构造方法

通过handler 轮询check 设备存储使用情况

public DeviceStorageMonitorService(Context context) {
        super(context);
        mHandlerThread = new HandlerThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND);
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_CHECK:
                        check();
                        return;
                }
            }
        };
    }

 

@Override
    public void onStart() {
        final Context context = getContext();
        mNotifManager = context.getSystemService(NotificationManager.class);
        mCacheFileDeletedObserver = new CacheFileDeletedObserver();
        mCacheFileDeletedObserver.startWatching();
        // Ensure that the notification channel is set up
        PackageManager packageManager = context.getPackageManager();
        boolean isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
        if (isTv) { 盒子属于TV  创建NotificationChannel
            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 state
        mHandler.removeMessages(MSG_CHECK);
        mHandler.obtainMessage(MSG_CHECK).sendToTarget();
    }
/**
     * Core logic that checks the storage state of every mounted private volume.
     * Since this can do heavy I/O, callers should invoke indirectly using
     * {@link #MSG_CHECK}.
     */
    @WorkerThread
    private void check() {
        final StorageManager storage = getContext().getSystemService(StorageManager.class);
        final int seq = mSeq.get();
        // Check every mounted private volume to see if they're low on space
        for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
            final File file = vol.getPath();
       获取系统设定的存储阈值
            final long fullBytes = storage.getStorageFullBytes(file);
            final long lowBytes = storage.getStorageLowBytes(file);
            // 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 PackageManagerService pms = (PackageManagerService) ServiceManager
                        .getService("package");
                try {
                    pms.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();
            final long usableBytes = file.getUsableSpace();
            int oldLevel = state.level;
            int newLevel;
      获取设备存储状态
            if (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) && !isBootImageOnDisk()
                    && usableBytes < BOOT_IMAGE_STORAGE_REQUIREMENT) {
                newLevel = State.LEVEL_LOW;
            } else {
                newLevel = State.LEVEL_NORMAL;
            }
            // Log whenever we notice drastic storage changes
            if ((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;
        }
        // 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)) {
            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CHECK),
                    DEFAULT_CHECK_INTERVAL);
        }
    }

StorageManager

位于 frameworks\base\core\java\android\os\storage\StorageManager.java 获取系统设置的阈值

/**
     * Return the number of available bytes at which the given path is
     * considered full.
     *
     * @hide
     */
    @UnsupportedAppUsage
    public long getStorageFullBytes(File path) {
        return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
                DEFAULT_FULL_THRESHOLD_BYTES);
    }
/**
     * Return the number of available bytes at which the given path is
     * considered running low on storage.
     *
     * @hide
     */
    @UnsupportedAppUsage
    public long getStorageLowBytes(File path) {
        final long lowPercent = Settings.Global.getInt(mResolver,
                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
        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);
    }

Settings字段

位于 frameworks\base\core\java\android\provider\Settings.java

/**
         * Maximum byte size of the low storage threshold. This is to ensure
         * that {@link #SYS_STORAGE_THRESHOLD_PERCENTAGE} does not result in an
         * overly large threshold for large storage devices. Currently this must
         * be less than 2GB. This default is 500MB.
         *
         * @hide
         */
        public static final String
                SYS_STORAGE_THRESHOLD_MAX_BYTES = "sys_storage_threshold_max_bytes";
        /**
         * Minimum bytes of free storage on the device before the data partition
         * is considered full. By default, 1 MB is reserved to avoid system-wide
         * SQLite disk full exceptions.
         *
         * @hide
         */
        public static final String
                SYS_STORAGE_FULL_THRESHOLD_BYTES = "sys_storage_full_threshold_bytes";

可以使用串口命令进行调整

settings put global sys_storage_threshold_percentage 95
settings put global sys_storage_threshold_max_bytes 5000000000
SettingsProvider的数据保存在文件/data/system/users/0/settings_***.xml和数据库settings.db中
           写入顺序                    key                    value         写入的apk包名
 <setting id="177" name="sys_storage_threshold_percentage" value="95" package="com.android.shell" />

Android Service之设备存储空间监控 - kaffeel - 博客园 (cnblogs.com)

目录
相关文章
|
2月前
|
移动开发 监控 Android开发
Android & iOS 使用 ARMS 用户体验监控(RUM)的最佳实践
本文主要介绍了 ARMS 用户体验监控的基本功能特性,并介绍了在几种常见场景下的最佳实践。
381 14
|
5月前
|
Android开发 iOS开发 UED
探索未来:Android与iOS在智能穿戴设备上的较量
随着科技的飞速进步,智能穿戴设备已经成为我们日常生活中不可或缺的一部分。本文将深入探讨两大操作系统——Android和iOS——在智能穿戴领域的竞争与发展,分析它们各自的优势与挑战,并预测未来的发展趋势。通过比较两者在设计哲学、生态系统、用户体验及创新技术的应用等方面的差异,揭示这场较量对消费者选择和市场格局的影响。 【7月更文挑战第31天】
59 0
|
4月前
|
Shell Linux 开发工具
"开发者的救星:揭秘如何用adb神器征服Android设备,开启高效调试之旅!"
【8月更文挑战第20天】Android Debug Bridge (adb) 是 Android 开发者必备工具,用于实现计算机与 Android 设备间通讯,执行调试及命令操作。adb 提供了丰富的命令行接口,覆盖从基础设备管理到复杂系统操作的需求。本文详细介绍 adb 的安装配置流程,并列举实用命令示例,包括设备连接管理、应用安装调试、文件系统访问等基础功能,以及端口转发、日志查看等高级技巧。此外,还提供了常见问题的故障排除指南,帮助开发者快速解决问题。掌握 adb 将极大提升 Android 开发效率,助力项目顺利推进。
127 0
|
6月前
|
存储 API 文件存储
47. 【Android教程】SharedPreferences 存储
47. 【Android教程】SharedPreferences 存储
78 2
|
6月前
|
调度 Android开发
43. 【Android教程】服务:Service
43. 【Android教程】服务:Service
64 2
|
3月前
|
监控 安全 Java
Kotlin 在公司上网监控中的安卓开发应用
在数字化办公环境中,公司对员工上网行为的监控日益重要。Kotlin 作为一种基于 JVM 的编程语言,具备简洁、安全、高效的特性,已成为安卓开发的首选语言之一。通过网络请求拦截,Kotlin 可实现网址监控、访问时间记录等功能,满足公司上网监控需求。其简洁性有助于快速构建强大的监控应用,并便于后续维护与扩展。因此,Kotlin 在安卓上网监控应用开发中展现出广阔前景。
30 1
|
4月前
|
存储 安全 API
Android经典实战之存储方案对比:SharedPreferences vs MMKV vs DataStore
本文介绍了 Android 开发中常用的键值对存储方案,包括 SharedPreferences、MMKV 和 DataStore,并对比了它们在性能、并发处理、易用性和稳定性上的特点。通过实际代码示例,帮助开发者根据项目需求选择最适合的存储方案,提升应用性能和用户体验。
149 1
|
4月前
|
Android开发
基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离
本文介绍了如何在基于Amlogic T972的Android 9.0系统上使用Platform平台驱动框架和设备树(DTS),实现设备与驱动的分离,并通过静态枚举在设备树中描述设备,自动触发驱动程序的加载和设备创建。
89 0
基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离
|
4月前
|
Android开发 C语言
基于Amlogic 安卓9.0, 驱动简说(二):字符设备驱动,自动创建设备
这篇文章是关于如何在基于Amlogic T972的Android 9.0系统上,通过自动分配设备号和自动创建设备节点文件的方式,开发字符设备驱动程序的教程。
86 0
基于Amlogic 安卓9.0, 驱动简说(二):字符设备驱动,自动创建设备
|
4月前
|
自然语言处理 Shell Linux
基于Amlogic 安卓9.0, 驱动简说(一):字符设备驱动,手动创建设备
本文是关于在Amlogic安卓9.0平台上创建字符设备驱动的教程,详细介绍了驱动程序的编写、编译、部署和测试过程,并提供了完整的源码和应用层调用示例。
122 0
基于Amlogic 安卓9.0, 驱动简说(一):字符设备驱动,手动创建设备