最近在做appStore时,涉及到一个第三方app下载视频后将车机存储撑爆的问题,从而影响到我们车机自带的app使用,问题很是严重,于是就需要提供缓存清理以及垃圾清理的功能,当用户使用第三方app时,存储空间达到一定量的时候会提示用户去清理,否则不允许使用第三方app。
获取各个应用程序的缓存大小,可以通过使用PackageManager.getPackageSizeInfo方法来获取,但是它被隐藏了,所以我们只能通过反射来调用该方法
一、首先我们需要获取到系统中所有已安装app的基本信息
List installedPackages = mPackageManager.getInstalledPackages(0);
for (PackageInfo packageInfo : installedPackages) {
getCacheSize(packageInfo);//反射调用getPackageSizeInfo
}
二、反射调用getPackageSizeInfo
/**
* 获取到缓存的大小
*
* @param packageInfo
*/
private void getCacheSize(PackageInfo packageInfo) {
try {
//通过反射获取到当前的方法
Method method = PackageManager.class.getDeclaredMethod("getPackageSizeInfo", String.class, IPackageStatsObserver.class);
/**
* 第一个参数表示当前的这个方法由谁调用的
* 第二个参数表示包名
*/
method.invoke(mPackageManager, packageInfo.applicationInfo.packageName, new MyIPackageStatsObserver(packageInfo));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
三、调用getPackageSizeInfo需要两个参数,第一个参数是应用包名,通过packageInfo可以获取到,第二个参数是一个IPackageStatsObserver类型的回调,所有查询的结果都是从IPackageStatsObserver里面的onGetStatsCompleted方法里面回调回来的。onGetStatsCompleted是异步调用的,所以我们需要通过handler将结果更新到UI上.
public static final int HANDLER_UPDATE_CACHE_MSG = 0XF0F1;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (HANDLER_UPDATE_CACHE_MSG == msg.what) {
if (msg.obj != null && msg.obj instanceof CacheInfo) {
CacheInfo info = (CacheInfo) msg.obj;
mCleanCacheAdapter.addDataAndRefresh(info);
}
}
}
};
private class MyIPackageStatsObserver extends IPackageStatsObserver.Stub {
private PackageInfo packageInfo;
public MyIPackageStatsObserver(PackageInfo packageInfo) {
this.packageInfo = packageInfo;
}
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException {
//获取到当前手机应用的缓存大小
long cacheSize = pStats.cacheSize;// 缓存大小
long dataSize = pStats.dataSize; // 数据大小
//如果当前的缓存大小大于0的话,则表示有缓存
if (cacheSize > 0) {
CacheInfo cacheInfo = new CacheInfo();
Drawable icon = packageInfo.applicationInfo.loadIcon(mPackageManager);
String appName = packageInfo.applicationInfo.loadLabel(mPackageManager).toString();
String packageName = packageInfo.packageName;
cacheInfo.setAppName(appName);
cacheInfo.setPackageName(packageName);
cacheInfo.setIcon(icon);
cacheInfo.setCacheSize(cacheSize);
Message.obtain(mHandler, HANDLER_UPDATE_CACHE_MSG, cacheInfo).sendToTarget();
LogUtils.d(mTAG,"---读取缓存中------>" + cacheSize + "---cacheInfos.size()--" + mCacheInfos.size());
}
}
}
四、补充:注意点: IPackageStatsObserver.aidl和PackageStats.aidl文件都是从系统中拿出来的,需要放到指定的文件夹下(android.content.pm),否则会出现找不到的错误。上面的CacheInfo只是一个简单的javaBean用来存放每个应用所查询到的数据。
五、前面四步已经成功的将所有的app缓存大小都获取到了,接下来就是如何清理这些缓存,首先说说一键清理,就是一次性清理所有的缓存,可以调用PackageManager的freeStorageAndNotify的方法,同样还是@hide,所以我们依旧要通过反射来调用,直接前几步骤差不多直接看代码
public void cleanAll() {
//获取到当前应用程序里面所有的方法
try {
StatFs stat = new StatFs(Environment.getDataDirectory().getAbsolutePath());
Method mFreeStorageAndNotifyMethod = mPackageManager.getClass().getMethod(
"freeStorageAndNotify", long.class, IPackageDataObserver.class);
mFreeStorageAndNotifyMethod.invoke(mPackageManager,
(long) stat.getBlockCount() * (long) stat.getBlockSize(),
new MyIPackageDataObserver()
);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
class MyIPackageDataObserver extends IPackageDataObserver.Stub {
public MyIPackageDataObserver() {
}
@Override
public void onRemoveCompleted(String packageName, boolean succeeded) throws RemoteException {
LogUtils.e(mTAG,"当前清理的包名:"+packageName);
if (succeeded){
LogUtils.e(mTAG,"清理成功!");
}else {
LogUtils.e(mTAG,"清理失败!");
}
}
}
前面那些操作所需要的权限:
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
六、到此为止,我所查询到的资料基本都是这些步骤,如果需要单独清理的应用数据的话,大都是直接跳到系统清理界面,通过系统操作清理。不过我们车机很显然不可能跳到系统界面然后清理,所以只能另寻他路,后来查到ActivityManager:clearApplicationUserData这个方法,是用于应用清除用户数据,相当于在设置中清除应用数据,同样也是隐藏的所以我们还是通过反射来调用,具体代码就不提供了,跟上面方式是一样的。
七、然后第六步操作之后运行会直接挂掉,提示缺少android.permission.CLEAR_APP_USER_DATA权限,然而加上之后还是会挂掉,还是提示缺少这个权限,也就是说普通的app我们是无法获取到这个权限的,所以我们还需要在AndroidManifest.xml里面配置android:sharedUserId=”android.uid.system”,这句配置的意思就是获取系统的进程权限,然后我们的app还需要调用系统平台的签名,这样就能单独的清理指定应用的缓存数据了。其实这个有很大的缺陷,由于应用和系统签名是绑定在一起的,所以这个应用只能在指定的系统上安装,其他的系统是安装不了的,我们车机系统是自己定制的,所以这个方式,对我这个应用来说是没问题的。