一键清理流程图
一键清理流程图.png系统缓存分析
Android 已安装 app /data/data/packagename/cache 文件夹和 /sdcard/Android/data/packagename/cache 文件夹组成
原生设置(Settings) - 已安装应用 - 详情页
Paste_Image.pngSettings APP 使用了 PackageManager.getPackageSizeInfo 方法来做此事,难道 so easy?屁颠屁颠去查了一下 Android API,发现 PacakgeManager 的文档中压根就没有出现 getPackageSizeInfo 的身影,好吧这是一个不对外公开的 API.
Settings 计算缓存大小方法:
@Override
public void handleMessage(Message msg) {
......
switch (msg.what) {
......
case MSG_LOAD_SIZES: {
synchronized (mEntriesMap) {
......
mPm.getPackageSizeInfo(mCurComputingSizePkg, mStatsObserver);
......
}
} break;
}
}
......
使用 getPackageSizeInfo 需要解决的问题:
Paste_Image.png
- getPackageSizeInfo 方法是一个 @hide 方法,需要通过反射来调用;
- 使用getPackageSizeInfo 需要在AndroidManiFest.xml 文件中申明权限GET_PACKAGE_SIZE
- 传给 getPackageSizeInfo 方法的第二个参数类型 IPackageStatsObserver 是在 android.content.pm 包下,需要自已通过 aidl 方式定义
计算缓存大小的实现:
实现流程:
- 工程的 src/main 目录下创建包目录结构 aidl/android/content/pm
- Android 源码 frameworks/base/core/java/android/content/pm 目录下的 IPackageStatsObserver.aidl 与其依赖的 PackageStats.aidl 拷贝到上面一步创建的目录里;
- AndroidManifest.xml 里声明需要 GET_PACKAGE_SIZE 权限
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"></uses-permission>
- 获取缓存大小实现
PackageManager pm = mContext.getPackageManager();
List<ApplicationInfo> installedPackages = pm.getInstalledApplications(PackageManager.MATCH_UNINSTALLED_PACKAGES);
mScanTotalCount = installedPackages.size();
for (int i = 0; i < mScanTotalCount; i++) {
ApplicationInfo appInfo = installedPackages.get(i);
getAppCacheSize(appInfo.packageName,observer );
}
public void getAppCacheSize(String packageName, IPackageStatsObserver.Stub observer) {
try {
PackageManager pm = ContextUtil.applicationContext.getPackageManager();
Method getPackageSizeInfo = pm.getClass()
.getMethod("getPackageSizeInfo", String.class, IPackageStatsObserver.class);
getPackageSizeInfo.invoke(pm, packageName, observer);
} catch (NoSuchMethodException e ) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
private class PackageSizeObserver extends IPackageStatsObserver.Stub {
@Override
public void onGetStatsCompleted(PackageStats packageStats, boolean succeeded)
throws RemoteException {
if (packageStats == null || !succeeded) {
} else {
AppEntry entry = new AppEntry();
entry.packageName = packageStats.packagename;
entry.cacheSize = packageStats.cacheSize + packageStats.externalCacheSize;
// do something else,比如把 entry 通过消息发送给需要的地方,或者添加到你的列表里
}
}
}
系统缓存清理
借鉴系统Settings清理缓存方案
public void onClick(View v) {
......
} else if (v == mClearCacheButton) {
// Lazy initialization of observer
if (mClearCacheObserver == null) {
mClearCacheObserver = new ClearCacheObserver();
}
mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
}
......
}
系统缓存清理实现
- AndroidManiFest.xml 中添加权限:
<uses-permission android:name="android.permission.DELETE_CACHE_FILES"/>
- 将 Android 源码 frameworks/base/core/java/android/content/pm 目录下的 IPackageDataObserver.aidl copy到工程pm目录下面;
- 删除缓存实现
private void deleteCacheFile(String packageName) {
Log.d(TAG, "deleteCacheFile: ");
try {
Method deleteApplicationCacheFiles = PackageManager.class.getDeclaredMethod("deleteApplicationCacheFiles", String.class, IPackageDataObserver.class);
deleteApplicationCacheFiles.invoke(pm, packageName, new PackageDataObserver());
}
catch (NoSuchMethodException e) {
e.printStackTrace();
}
catch (InvocationTargetException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public class PackageDataObserver extends IPackageDataObserver.Stub {
@Override
public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException {
Log.d(TAG, "onRemoveCompleted: "+packageName);
}
@Override
public IBinder asBinder() {
return null;
}
}
4.app 使用DELETE_CACHE_FILES权限 需要系统签名;
扫描其他文件
//获取SDCard 文件目录
File externalDir = Environment.getExternalStorageDirectory();
if (externalDir != null) {
traverPath(externalDir, 0);
}
private void traverPath(File root, int level) {
if (root == null || !root.exists() || level > SCAN_LEVEL) {
return;
}
File[] listFiles = root.listFiles();
for (File file : listFiles) {
mInfProgress.setPath(file.getAbsolutePath());
if (file.isFile()) {
String fileName = file.getName();
if (fileName.endsWith(".apk") || fileName.endsWith(".log") || fileName.endsWith(".tmp") || fileName.endsWith(".temp")) {
mDataSize += file.length();
CleanDataInfo cleanInfo = new CleanDataInfo();
// List<String> filePath = new ArrayList<>();
cleanInfo.setPath(file.getAbsolutePath());
cleanInfo.setSize(mDataSize);
listsInfo.add(cleanInfo);
Log.d(MyApplication.TAG, "traverPath: " + cleanInfo.getPath());
}
if (mInfProgress != null) {
mScanListener.onProgress(mInfProgress);
}
}
else {
if (level < SCAN_LEVEL) {
traverPath(file, level + 1);
}
}
}
}
网友评论