很久没写了,做下笔记,跟踪一下Android系统对存储空间的管理和监控:
对于Android系统的存储空间是由DeviceStorageMonitorService这个服务来进行管理的,现在我们来浅析一下这个类的加载流程和空间管理。
DeviceStorageMonitorService类实现了一个监控设备上存储空间的服务。如果设备的剩余存储空间小于某一个阀值(默认是存储空间的10%和500M,一般选择它们中的最小值为默认值,这个值可以设置)时将会向用户发送剩余空间不足的警告,让用户释放一些空间
位置:frameworks\base\services\core\java\com\android\server\storage\DeviceStorageMonitorService.java
开机启动位置:
我们知道一般service都是由SystemServer加载进来的,这个服务也是,在SystemServer.java由
mSystemServiceManager.startService(DeviceStorageMonitorService.class);启动服务。
服务起来后又干了些啥?发现它会先创建4个intent
mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
//存储空间不足
mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
//存储空间正常
mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
//存储空间已满
mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
由于每个Intent都设置了FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT标志,因此这三个Intent只能由注册了的BroadcastReceiver接收。
接着我们走到onstart()中,
mMemLowThreshold = sm.getStorageLowBytes(DATA_PATH); //设置的存储空间不足的大小
从StorageManager.java 的getStorageLowBytes()方法中我们可得到
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);
}
选择的是Math.min(lowBytes, maxLowBytes),返回它们的最小值,DEFAULT_THRESHOLD_PERCENTAGE=10;
DEFAULT_THRESHOLD_MAX_BYTES=500* MB_IN_BYTES;//即500M
同样mMemFullThreshold = sm.getStorageFullBytes(DATA_PATH);可得到
public long getStorageFullBytes(File path) {
return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
DEFAULT_FULL_THRESHOLD_BYTES);
}
DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;//1M
接着重点函数checkMemory(true);开始检查,存储空间
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 {
restatDataDir(); //重新计算3个分区的剩余空间大小;
if (localLOGV) Slog.v(TAG, "freeMemory="+mFreeMem+" mMemLowThreshold="+mMemLowThreshold);
//post intent to NotificationManager to display icon if necessary
if (mFreeMem < mMemLowThreshold) {
if (checkCache) {
// We are allowed to clear cache files at this point to
// try to get down below the limit, because this is not
// the initial call after a cache clear has been attempted.
// In this case we will try a cache clear if our free
// space has gone below the cache clear limit.
if (mFreeMem < mMemCacheStartTrimThreshold) {
// We only clear the cache if the free storage has changed
// a significant amount since the last time.
if(localLOGV) Slog.i(TAG, "mMemCacheStartTrimThreshold="+mMemCacheStartTrimThreshold);
if ((mFreeMemAfterLastCacheClear-mFreeMem)
>= ((mMemLowThreshold-mMemCacheStartTrimThreshold)/4)) {
// 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.
if(localLOGV) Slog.i(TAG, "mFreeMemAfterLastCacheClear="+mFreeMemAfterLastCacheClear);
mThreadStartTime = System.currentTimeMillis();
mClearSucceeded = false;
clearCache();//如果剩余空间低于mMemLowThreshold,先做一次缓存清理
}
}
} else {
// This is a call from after clearing the cache. Note
// the amount of free storage at this point.
mFreeMemAfterLastCacheClear = mFreeMem;
if (!mLowMemFlag) {
// We tried to clear the cache, but that didn't get us
// below the low storage limit. Tell the user.
Slog.i(TAG, "Running low on memory. Sending notification");
sendNotification();//如果空间仍然低于mMemLowThreshold, 发送广播并 在状态来设置一个 警告通知,在未取消广播只跑一次
mLowMemFlag = true;
} else {
if (localLOGV) Slog.v(TAG, "Running low on memory " +
"notification already sent. do nothing");
}
}
} else {
mFreeMemAfterLastCacheClear = mFreeMem;
if (mLowMemFlag) {//如果 剩余空间 不小于mMemLowThreshold,且已经设置了mLowMemFlag,则 取消空间不足广播。
Slog.i(TAG, "Memory available. Cancelling notification");
cancelNotification();
mLowMemFlag = false;
}
}
if (!mLowMemFlag && !mIsBootImageOnDisk && mFreeMem < BOOT_IMAGE_STORAGE_REQUIREMENT) {
Slog.i(TAG, "No boot image on disk due to lack of space. Sending notification");
sendNotification();
mLowMemFlag = true;
}
if (mFreeMem < mMemFullThreshold) {
if (!mMemFullFlag) {
sendFullNotification(); //如果空间已满,则发送空间已满的广播;即小余1M
mMemFullFlag = true;
}
} else {
if (mMemFullFlag) {
cancelFullNotification();//如果 空间不满 且已经发送了空间已满的广播,则在此取消。
mMemFullFlag = false;
}
}
}
if(localLOGV) Slog.i(TAG, "Posting Message again");
//keep posting messages to itself periodically
postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL);//DEFAULT_CHECK_INTERVAL为1分钟,即每1分钟 会触发一次检查;
}
void postCheckMemoryMsg(boolean clearCache, long delay) {
// Remove queued messages
mHandler.removeMessages(DEVICE_MEMORY_WHAT);
mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT,
clearCache ?_TRUE : _FALSE, 0),
delay);
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//don't handle an invalid message
if (msg.what != DEVICE_MEMORY_WHAT) {
Slog.e(TAG, "Will not process invalid message");
return;
}
checkMemory(msg.arg1 == _TRUE);
}
};
而在clearCache会立即检查一次
private void clearCache() {
if (mClearCacheObserver == null) {
// Lazy instantiation
mClearCacheObserver = new CachePackageDataObserver();
}
mClearingCache = true;
try {
if (localLOGV) Slog.i(TAG, "Clearing cache");
IPackageManager.Stub.asInterface(ServiceManager.getService("package")).
freeStorageAndNotify(null, mMemCacheTrimToThreshold, mClearCacheObserver);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to get handle for PackageManger Exception: "+e);
mClearingCache = false;
mClearSucceeded = false;
}
}
且clearCache值设置为false
private class CachePackageDataObserver extends IPackageDataObserver.Stub {
public void onRemoveCompleted(String packageName, boolean succeeded) {
mClearSucceeded = succeeded;
mClearingCache = false;
if(localLOGV) Slog.i(TAG, " Clear succeeded:"+mClearSucceeded
+", mClearingCache:"+mClearingCache+" Forcing memory check");
postCheckMemoryMsg(false, 0);
}
}
总结:
(1)首先在构造函数中,获取data,system,cache分区信息,然后注册四个intent,分别为低内存,内存ok,内存满,内存没有满四种情况。然后获取settings数据库里的data目录下剩余空间少于其总空间的百分比值,获取数据库中data目录下剩余空间的大小临界值(用于提示用户空间已满)。然后开始检查,存储空间;checkMemory(true);
(2)
在检查存储空间时,首先判断如果线程正在清除缓存CACHE_PATH ,那么不进行空间检查。否则重新计算3个分区的剩余空间大小。如果剩余空间低于设置值,如果需要做缓存清理,先做一次缓存清理;清理完毕后会再次进行新一轮的checkMemory,如果剩余空间低于还是低于设置值,并不用做缓存清理并且没有发通知,则发送通知告诉用户内部空间低于设置值,如果此时空间百分比正常,但已发送通知,则将通知取消。同样的,如果空间已满,大于full的临界值,则发送空间已满的广播;空间不满且已经发送了空间已满的广播,则取消。最后会每1分钟会触发一次检查空间checkMemory。
如下:清理完毕后会再次进行新一轮的checkMemory:
做一下onRemoveCompleted动作,该动作发送检查空间的消息【postCheckMemoryMsg(false, 0);这里的false就是不再执行clearCache】,然后handle处理该消息DEVICE_MEMORY_WHAT,再次进入checkMemory(false),发送通知告诉用户storage space running out,空间小于设置值会出现该提示。