在负责文件系统模块的过程中,经常会碰到由于系统空间被消耗完而导致的问题,因此要确保为系统功能(如数据库同步)保留一定的空间。在功能机中一般是由文件系统模块预留,那么在Android系统是怎样对设备存储空间进行管理和监控的呢?
如果你在使用Android手机时有过把memory填满或者即将填满的经历,也许你会注意到在这种情况下手机的Notifications栏会有“Storagespacerunningout”的通知。当点开该通知你会发现Setting–>Storage settings –>Device memory 下会有如下提示:Not enoughstorage space.
这个服务的实现是在android/framework/base/services/java/com/android/server/DeviceStorageMonitorService.java。DeviceStorageMonitorService类实现了一个监控设备上存储空间的服务。如果设备的剩余存储空间小于某一个阀值(默认是存储空间的10%,这个值可以设置)时将会向用户发送剩余空间不足的警告,让用户释放一些空间。
/** * Constructor to run service. initializes the disk space threshold value * and posts an empty message to kickstart the process. */ public DeviceStorageMonitorService(Context context) { mLastReportedFreeMemTime = 0; mContext = context; mContentResolver = mContext.getContentResolver(); //create StatFs object mDataFileStats = new StatFs(DATA_PATH); //获取Data分区信息; mSystemFileStats = new StatFs(SYSTEM_PATH); //获取System分区信息; mCacheFileStats = new StatFs(CACHE_PATH); //获取Cache分区信息; //initialize total storage on device // 初始化设备总空间信息 // mTotalMemory 用于保存Data分区总空间; mTotalMemory = ((long)mDataFileStats.getBlockCount() * mDataFileStats.getBlockSize())/100L; /* 创建3个Intent,分别用于通知存储空间不足(ACTION_DEVICE_STORAGE_LOW)、 存储空间回复正常(ACTION_DEVICE_STORAGE_OK)和存储空间满(ACTION_DEVICE_STORAGE_FULL)。 由于每个Intent都设置了FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT标志,因此这三个Intent只 能由注册了的BroadcastReceiver接收。 */ mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW); mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK); mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL); mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL); mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); // cache storage thresholds /* 查询Seetings数据库中sys_storage_threshod_percentage的值,默认是10,即当DATA_PATH 目录下剩余空间少于其总空间的10%时,认为空间不足(ACTION_DEVICE_STORAGE_LOW)。 */ mMemLowThreshold = getMemThreshold(); /* 查询Settings数据库中的sys_storage_full_threshold_bytes的值,默认是1MB,即当DATA_PATH 目录下剩余空间小于等于1M时,任务空间已满,剩余的部分是保留给系统使用的。 */ mMemFullThreshold = getMemFullThreshold(); /* 开始检查,存储空间; */ checkMemory(true); }
checkMemory()方法:
private final void checkMemory(boolean checkCache) { //if the thread that was started to clear cache is still running do nothing till its //finished clearing cache. Ideally this flag could be modified by clearCache // and should be accessed via a lock but even if it does this test will fail now and //hopefully the next time this flag will be set to the correct value. //如果线程正在清除缓存CACHE_PATH ,那么不进行空间检查。 if(mClearingCache) { if(localLOGV) Slog.i(TAG, “Thread already running just skip”); //make sure the thread is not hung for too long long diffTime = System.currentTimeMillis() – mThreadStartTime; if(diffTime > (10*60*1000)) { Slog.w(TAG, “Thread that clears cache file seems to run for ever”); } } else { //重新计算3个分区的剩余空间大小; restatDataDir(); if (localLOGV) Slog.v(TAG, “freeMemory=”+mFreeMem); //post intent to NotificationManager to display icon if necessary ////如果剩余空间低于mMemLowThreshold,先做一次缓存清理; if (mFreeMem < mMemLowThreshold) { if (!mLowMemFlag) { if (checkCache) { // See if clearing cache helps // Note that clearing cache is asynchronous and so we do a // memory check again once the cache has been cleared. //首先清除缓存 mThreadStartTime = System.currentTimeMillis(); mClearSucceeded = false; clearCache(); } else { //如果空间仍然低于mMemLowThreshold,发送广播并在状态来设置一个 //警告通知; Slog.i(TAG, “Running low on memory. Sending notification”); sendNotification(); mLowMemFlag = true; } } else { if (localLOGV) Slog.v(TAG, “Running low on memory ” + “notification already sent. do nothing”); } } else { if (mLowMemFlag) { //剩余空间不小于mMemLowThreshold,且已经设置了mLowMemFlag,则 //取消空间不足广播。 Slog.i(TAG, “Memory available. Cancelling notification”); cancelNotification(); mLowMemFlag = false; } } if (mFreeMem < mMemFullThreshold) { //如果空间已满,则发送空间已满的广播; if (!mMemFullFlag) { sendFullNotification(); mMemFullFlag = true; } } else { if (mMemFullFlag) { //空间不满且已经发送了空间已满的广播,则在此取消。 cancelFullNotification(); mMemFullFlag = false; } } } if(localLOGV) Slog.i(TAG, “Posting Message again”); //keep posting messages to itself periodically //DEFAULT_CHECK_INTERVAL为1分钟,即每1分钟会触发一次检查 postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL); } //mLowMemFlag和mMemFullFlag为是否发送了广播的标识。
clearData方法:
当空间不足时,DSMS会先尝试clearCache函数,该函数内部会与PackageManager-Service(以下简称PKMS)交互,其代码如下: [-->DeviceStorageManagerService.java::clearCache] private final void clearCache() { if (mClearCacheObserver == null) { //创建一个CachePackageDataObserver对象,当PKMS清理完空间时会回调该对象的 //onRemoveCompleted函数 mClearCacheObserver = new CachePackageDataObserver(); } mClearingCache = true;//设置mClearingCache的值为true,表示我们正在清理空间 try { //调用PKMS的freeStorageAndNotify函数以清理空间,这个函数在分析PKMS时再介绍 IPackageManager.Stub.asInterface( ServiceManager.getService("package")). freeStorageAndNotify(mMemLowThreshold, mClearCacheObserver); //该函数在PackageManagerservice.java } ...... } CachePackageDataObserver是DSMS定义的内部类,其onRemoveCompleted函数很简单,就是重新发送消息DEVICE_MEMORY_WHAT (postCheckMemoryMsg(false, 0);),让DSMS再检测一次存储空间。如果剩余空间小于10%,则发送sendNotification()提示 storage space running out。
总结: (1)首先在构造函数中,获取data,system,cache分区信息,然后注册四个intent,分别为低内存,内存ok,内存满,内存没有满四种情况。然后获取settings数据库里的data目录下剩余空间少于其总空间的百分比值,获取数据库中data目录下剩余空间的大小临界值(用于提示用户空间已满)。然后开始检查,存储空间;checkMemory(true); (2) 在检查存储空间时,首先判断如果线程正在清除缓存CACHE_PATH ,那么不进行空间检查。否则重新计算3个分区的剩余空间大小。如果剩余空间低于百分比10%,如果需要做缓存清理,先做一次缓存清理;清理完毕后会再次进行新一轮的checkMemory,如果剩余空间低于百分比10%并不用做缓存清理并且没有发通知,则发送通知告诉用户内部空间超出最低值10%,如果此时空间百分比正常,但已发送通知,则将通知取消。同样的,如果空间已满,大于full的临界值,则发送空间已满的广播;空间不满且已经发送了空间已满的广播,则取消。最后会每1分钟会触发一次检查空间checkMemory。 如下:清理完毕后会再次进行新一轮的checkMemory: 做一下onRemoveCompleted动作,该动作发送检查空间的消息【postCheckMemoryMsg(false, 0);这里的false就是不再执行clearCache】,然后handle处理该消息DEVICE_MEMORY_WHAT,再次进入checkMemory(false),发送通知告诉用户storage space running out,空間剩10%時會出現该提示。