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)

目录
相关文章
|
21天前
|
调度 Android开发
43. 【Android教程】服务:Service
43. 【Android教程】服务:Service
18 2
|
1月前
|
Java Linux API
统计android设备的网络数据使用量
统计android设备的网络数据使用量
31 0
|
4天前
|
XML 监控 安全
Android App性能优化之卡顿监控和卡顿优化
本文探讨了Android应用的卡顿优化,重点在于布局优化。建议包括将耗时操作移到后台、使用ViewPager2实现懒加载、减少布局嵌套并利用merge标签、使用ViewStub减少资源消耗,以及通过Layout Inspector和GPU过度绘制检测来优化。推荐使用AsyncLayoutInflater异步加载布局,但需注意线程安全和不支持特性。卡顿监控方面,提到了通过Looper、ChoreographerHelper、adb命令及第三方工具如systrace和BlockCanary。总结了Choreographer基于掉帧计算和BlockCanary基于Looper监控的原理。
15 3
|
25天前
|
安全 物联网 测试技术
构建未来:Android与IoT设备的无缝交互深入探索软件自动化测试的未来趋势
【5月更文挑战第30天】在物联网(IoT)技术快速发展的当下,Android系统因其开放性和广泛的用户基础成为了连接智能设备的首选平台。本文将探讨如何通过现代Android开发技术实现智能手机与IoT设备的高效、稳定连接,并分析其中的挑战和解决方案。我们将深入挖掘Android系统的底层通信机制,提出创新的交互模式,并通过实例演示如何在Android应用中集成IoT控制功能,旨在为开发者提供一套可行的指导方案,促进IoT生态系统的进一步发展。
|
30天前
|
Shell 开发工具 Android开发
|
10天前
|
存储 API 开发工具
kotlin安卓开发,如何获取设备的唯一id, 有哪些开源库
在Kotlin的Android开发中,获取设备唯一ID的方法包括不稳定的ANDROID_ID、需要权限的IMEI、使用UUID与SharedPreference结合,以及考虑隐私的Firebase Installations ID和Advertising ID。由于隐私问题和Google Play政策,IMEI和ANDROID_ID不推荐作为长期唯一标识。推荐使用UUID(首次安装时生成并存储),或在涉及广告时使用Advertising ID(需用户同意),而Firebase Installations ID则提供了一种合规的设备标识选项。在选择方法时,必须遵守隐私指南和政策。
|
1月前
|
Android开发
Android监听USB设备插拔
Android监听USB设备插拔
69 7
|
1月前
|
Android开发
Android获取蓝牙设备列表的方法
Android获取蓝牙设备列表的方法
51 5
|
1月前
|
Shell Android开发
ADB更改Android设备屏幕显示方向
ADB更改Android设备屏幕显示方向
48 5
|
1月前
|
Android开发
Android 获取 USB设备列表
Android 获取 USB设备列表 【5月更文挑战第6天】
46 4