我们先来看一个熟悉的界面:
这是手机的「设置」——「应用」里的已安装应用的详情页,这里面会显示缓存的大小,而且提供了清理缓存的功能,这就是我们做「系统缓存」清理想做的事情。
这里显示的大小是如何计算出来的,它实际上的文件组成是怎么样的呢?可以从 Android 系统自带的 Settings APP 的源码中找到答案。
注:下面的分析基于Android 4.1 源码,比较古老了,但并不妨碍理解。
探索「外部缓存」
按惯例先说结论:
「外部缓存」由所有已安装应用的 /sdcard/Android/data/packagename/cache 文件夹组成。
Settings APP 的源码在 Android 源码树的 packages/apps/Settings 目录里,在它里面能找到 InstalledAppDetails.java 文件,从名字上看它应该就是对应我们上图中的「应用详情页」,它是一个 Fragment,在它的onResume方法中调用了refreshUi方法,它里面又调用了refreshSizeInfo方法:
private void refreshSizeInfo() {
if (mAppEntry.size == ApplicationsState.SIZE_INVALID
|| mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) {
......
} else {
......
long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize;
if (mLastCacheSize != cacheSize) {
mLastCacheSize = cacheSize;
mCacheSize.setText(getSizeStr(cacheSize));
}
......
}
}
这个方法定义在文件 packages/apps/Settings/src/com/android/settings/applications/InstalledAppDetails.java 中。
很显然这里的cacheSize就是对应上图里的缓存大小,从这几行代码的字面意义里可以看出缓存是由「内部缓存」加「外部缓存」组成,甚至可以初步推测出本节的结论,当然这里要继续深究一下其中的原理。
找到给mAppEntry赋值的地方:
private boolean refreshUi() {
......
mAppEntry = mState.getEntry(packageName);
......
}
这个方法定义在文件 packages/apps/Settings/src/com/android/settings/applications/InstalledAppDetails.java 中。
继续看getEntry里做了什么:
AppEntry getEntry(String packageName) {
......
for (int i=0; i
ApplicationInfo info = mApplications.get(i);
if (packageName.equals(info.packageName)) {
entry = getEntryLocked(info);
break;
}
}
.......
}
这个方法定义在文件 packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java 中。
找到给mApplications添加数据的地方:
void addPackage(String pkgName) {
try {
synchronized (mEntriesMap) {
......
ApplicationInfo info = mPm.getApplicationInfo(pkgName,
PackageManager.GET_UNINSTALLED_PACKAGES |
PackageManager.GET_DISABLED_COMPONENTS);
mApplications.add(info);
if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
}
......
}
} catch (NameNotFoundException e) {
}
}
这个方法定义在文件 packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java 中。
它在mApplications.add(info);后顺便发了个消息,经过MSG_LOAD_ENTRIES到MSG_LOAD_ICONS到MSG_LOAD_SIZES的消息链,我们看到一个从名字上就看出来很重要的关键方法调用getPackageSizeInfo:
class BackgroundHandler extends Handler {
......
@Override
public void handleMessage(Message msg) {
......
switch (msg.what) {
......
case MSG_LOAD_ENTRIES: {
......
if (numDone >= 6) {
......
} else {
sendEmptyMessage(MSG_LOAD_ICONS);
}
} break;
case MSG_LOAD_ICONS: {
......
if (numDone >= 2) {
......
} else {
sendEmptyMessage(MSG_LOAD_SIZES);
}
} break;
case MSG_LOAD_SIZES: {
synchronized (mEntriesMap) {
......
mPm.getPackageSizeInfo(mCurComputingSizePkg, mStatsObserver);
......
}
} break;
}
}
......
}
这个类定义在文件 packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java 中。
mPm是PackageManager类型的,这是一个抽象类型,它的实现类为ApplicationPackageManager,ApplicationPackageManager.getPackageSizeInfo里调用了IPackageManager.getPackageSizeInfo,IPackageManager的实例在ContexImpl.getPackageManager方法里通过ActivityThread.getPackageManager()获得,它的方法调用最终是反映到通过 Binder 机制返回的PackageManagerService实例上,我们找到getPackageSizeInfo的最终实现:
public class PackageManagerService extends IPackageManager.Stub {
......
public void getPackageSizeInfo(final String packageName,
final IPackageStatsObserver observer) {
......
Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new MeasureParams(stats, observer);
mHandler.sendMessage(msg);
}
......
}
这个方法定义在文件 frameworks/base/services/java/com/android/server/pm/PackageManagerService.java 中。
这里我们注意msg.obj的类型为MeasureParams,INIT_COPY消息对应的处理:
class PackageHandler extends Handler {
private boolean mBound = false;
......
public void handleMessage(Message msg) {
......
doHandleMessage(msg);
......
}
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
......
if (!mBound) {
if (!connectToService()) {