0. Thanks
1. 基本知识
一般来说,我们自己程序的文件会存在两个地方:
/data/data/<packageName>
此路径下主要是存储,数据库数据,SharedPreference,和其他的一些数据,当然也有缓存。
-
cache:内部缓存目录
-
databases:数据库数据
-
shared_prefs:SharedPreference
-
lib:app的依赖包
-
/sdcard/Android/data/<packageName>
可以看到,在这两个目录下,又分了,cache
,files
等好多个目录。一般缓存都会存在cache
下。
通过content的API,我们可以获取到相应的目录:
LogUtils.i("ARouterOneActivity", this.getCacheDir().getAbsolutePath());
LogUtils.i("ARouterOneActivity", this.getFilesDir().getAbsolutePath());
LogUtils.i("ARouterOneActivity", this.getObbDir().getAbsolutePath());
LogUtils.i("ARouterOneActivity", this.getExternalCacheDir().getAbsolutePath());
LogUtils.i("ARouterOneActivity", this.getExternalFilesDir(Environment.DIRECTORY_DCIM).getAbsolutePath());
复制代码
/data/data/com.chestnut.RouterArchitecture.ModulesCommon/cache
/data/data/com.chestnut.RouterArchitecture.ModulesCommon/files
/storage/emulated/0/Android/obb/com.chestnut.RouterArchitecture.ModulesCommon
/storage/emulated/0/Android/data/com.chestnut.RouterArchitecture.ModulesCommon/cache
/storage/emulated/0/Android/data/com.chestnut.RouterArchitecture.ModulesCommon/files/DCIM
复制代码
2. 清理缓存
一般app都有一个清理自身缓存的功能,我们先明确, 清理缓存,也就是清理我们存在: /data/data/<packageName>/cache
/sdcard/Android/data/<packageName>/cache
这两个路径下的文件。
先得到缓存的大小:
- 使用递归获取某个文件夹的大小:
/**
* 递归方式 计算文件的大小
* @param file 文件or目录
* @return 大小
*/
private static long getTotalSizeOfFilesInDir(final File file) {
if (file.isFile())
return file.length();
final File[] children = file.listFiles();
long total = 0;
if (children != null)
for (final File child : children)
total += getTotalSizeOfFilesInDir(child);
return total;
}
复制代码
- 计算大小:
File file1 = new File("/sdcard/Android/data/{pkg}/cache".replace("{pkg}", pkg));
File file2 = new File("/data/data/{pkg}/cache".replace("{pkg}", pkg));
复制代码
- 得到的是byte的大小,我们需要转化成合适的显示。
/**
* Byte与Byte的倍数
*/
public static final int BYTE = 1;
/**
* KB与Byte的倍数
*/
public static final int KB = 1024;
/**
* MB与Byte的倍数
*/
public static final int MB = 1048576;
/**
* GB与Byte的倍数
*/
public static final int GB = 1073741824;
/**
* 字节数转合适大小
* <p>保留3位小数</p>
*
* @param byteNum 字节数
* @return 1...1024 unit
*/
public static String byte2FitSize(long byteNum) {
if (byteNum < 0) {
return "shouldn't be less than zero!";
} else if (byteNum < KB) {
return String.format(Locale.getDefault(), "%.3fB", (double) byteNum);
} else if (byteNum < MB) {
return String.format(Locale.getDefault(), "%.3fKB", (double) byteNum / KB);
} else if (byteNum < GB) {
return String.format(Locale.getDefault(), "%.3fMB", (double) byteNum / MB);
} else {
return String.format(Locale.getDefault(), "%.3fGB", (double) byteNum / GB);
}
}
复制代码
当然,其实很多应用的缓存目录都不是放在这两个文件夹下,像微信都直接在sd卡上新建一个目录来存储,我们写自己app的清理缓存功能的时候,记得要把这些都考虑进去。
- 然后直接delete吧。
/**
* 删除目录下的所有文件。
*
* @param filePath 目录
*/
public static void deleteFolderFile(String filePath, boolean deleteThisPath) {
if (!TextUtils.isEmpty(filePath)) {
try {
File file = new File(filePath);
if (file.isDirectory()) {// 如果下面还有文件
File files[] = file.listFiles();
for (int i = 0; i < files.length; i++) {
deleteFolderFile(files[i].getAbsolutePath(), true);
}
}
if (deleteThisPath) {
if (!file.isDirectory()) {// 如果是文件,删除
file.delete();
} else {// 目录
if (file.listFiles().length == 0) {// 目录下没有文件或者目录,删除
file.delete();
}
}
}
} catch (Exception e) {}
}
}
复制代码
3. 清理其他APP的缓存
在某些项目中,我们可能接到这样的需求,清理其他APP的缓存。 在上面的Thanks
链接中,我们可以知道,可以使用反射系统API+系统权限去做。
- 再总结一下应用设置页面中的缓存:
-
缓存:29.54MB,是统计了:
/data/data/<packageName>/cache
/sdcard/Android/data/<packageName>/cache
的大小 -
清理缓存其实是系统调用了方法:
android.content.pm.PackageManager.deleteApplicationCacheFiles
清理以上的目录。
/**
* 清理具体应用的缓存
* @param context 上下文
* @param pkg 包名
* @param removeCallback 回调
*/
public static void cleanAppInternalCache(Context context, String pkg, CommonContract.Function2<String,Boolean> removeCallback) {
File file = new File("/data/data/<packageName>/cache".replace("<packageName>", pkg));
if (file.exists()) {
try {
PackageManager mPackageManager = context.getPackageManager();
if (mPackageManager!=null) {
Method mFreeStorageAndNotifyMethod = mPackageManager.getClass().getMethod("deleteApplicationCacheFiles", String.class, IPackageDataObserver.class);
mFreeStorageAndNotifyMethod.invoke(mPackageManager, pkg, new IPackageDataObserver.Stub() {
@Override
public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException {
if (removeCallback!=null) {
removeCallback.onAction(packageName, succeeded);
}
}
});
}
} catch (Exception e) {
e.printStackTrace();
if (removeCallback!=null) {
removeCallback.onAction(pkg, false);
}
}
}
else {
if (removeCallback!=null) {
removeCallback.onAction(pkg, true);
}
}
}
复制代码
- 获取缓存大小的方法是调用了系统的:
android.content.pm.PackageManager.getPackageSizeInfo
,得到一个PackageStats
对象,里面就包含了缓存的基本信息。
/**
* 获取到缓存的大小
* 1. /data/data/<packageName>
* 2. /sdcard/Android/data/<packageName>
* @param context context
* @param pkg pkg
* @param getCallback getCallback
*/
public static void getAppSizeInfo(Context context, String pkg, CommonContract.Function1<PackageStats> getCallback) {
try {
Method method = PackageManager.class.getDeclaredMethod("getPackageSizeInfo", String.class, IPackageStatsObserver.class);
method.invoke(context.getPackageManager(), pkg, new IPackageStatsObserver.Stub() {
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException {
if (getCallback!=null) {
if (succeeded)
getCallback.onAction(pStats);
else
getCallback.onAction(null);
}
}
});
} catch (Exception e) {
e.printStackTrace();
if (getCallback!=null) {
getCallback.onAction(null);
}
}
}
复制代码
- 数据清理是系统调用了方法:
android.content.pm.PackageManager.clearApplicationUserData
/**
* 清理具体应用的数据+缓存
* 1. /data/data/<packageName>
* 2. /sdcard/Android/data/<packageName>
* @param context 上下文
* @param pkg 包名
* @param removeCallback 回调
*/
public static void cleanAppDataAndCache(Context context, String pkg, CommonContract.Function2<String,Boolean> removeCallback) {
try {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (activityManager!=null) {
Method mFreeStorageAndNotifyMethod = activityManager.getClass().getMethod("clearApplicationUserData", String.class, IPackageDataObserver.class);
mFreeStorageAndNotifyMethod.invoke(activityManager, pkg, new IPackageDataObserver.Stub() {
@Override
public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException {
if (removeCallback!=null) {
removeCallback.onAction(packageName, succeeded);
}
}
});
}
} catch (Exception e) {
e.printStackTrace();
if (removeCallback!=null) {
removeCallback.onAction(pkg, false);
}
}
}
复制代码
要使用上面写好的方法,你还有做这些:
-
首先我们要准备好系统的两个AIDL:
android.content.pm.IPackageStatsObserver
android.content.pm.IPackageDataObserver
,一个是获取缓存的监听器,一个是清理缓存的监听器 -
你的应用要打上系统的签名,或者是想方设法得到
root
权限 -
需要声明一些系统权限:
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA"/>
<uses-permission android:name="android.permission.DELETE_CACHE_FILES"/>
复制代码