利用IPackageManager接口进行缓存垃圾清理
- 获取IPackageManager
- getPackageSizeInfo方法介绍
- freeStorageAndNotify方法释放存储空间
- clearApplicationUserData方法清除用户数据
- 删除cache文件目录来清除缓存
- PMS源码中LI、LIF、LP(LPw、LPr)的含义
- 结语
获取IPackageManager
通过Context.getPackageManager
方法可得到PackageManager
,通过PackageManager
可实现一些应用处理。
public PackageManager getPackageManager(){
if(mPackageManager !=null){
return mPackageManager;
}
IPackageManager pm = ActivityThread.getPackageManager();
if(pm != null){
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
PackageManager
的实质为获取IPackageManager
,即PackageManagerService
的客户端代理接口,PackageManager
为抽象类,实际实现为ApplicationPackageManager
,为包级类,无法直接访问。IPackageManager
获取的两种方式如下:
IPackageManager pm = ActivityThread.getPackageManager()
IPackageManager pm = AppGlobals.getPackageManager()
实际都是通过ActivityThread.getPackageManager()
。
getPackageSizeInfo方法介绍
getPackageSizeInfo
为获取应用缓存大小,方法参数如下:
public void getPackageSizeInfo(String packageName, int userHandle, IPackageStatsObserver observer)
检索包的大小信息。由于这可能需要一点时间,结果将被发送回给定的观察者。需要android.Manifest.permission.GET_PACKAGE_SIZE
权限。 参数packageName 要获取其大小信息的包的名称 。参数 userHandle 应获取其大小信息的用户。 参数observer 操作完成时收到通知的观察者回调。使用 PackageStats
对象(包含包的代码、数据和缓存大小)和表示操作状态的布尔值调用观察者的回调,观察者为空时表示不需要回调。 此方法android弃用, 使用StorageStatsManager
代替,StorageStatsManager
用法详见博客:android利用StorageStatsManager获取应用程序的存储信息。
PackageStatsObserver packageStatsObserver = new PackageStatsObserver();
try {
iPackageManager.getPackageSizeInfo(packageName, UserHandle.myUserId(), packageStatsObserver);
} catch (RemoteException e) {
e.printStackTrace();
}
private static class PackageStatsObserver extends IPackageStatsObserver.Stub {
@Override
public void onGetStatsCompleted(PackageStats packageStats, boolean b) throws RemoteException {
String packageName = packageStats.packageName
long cacheSize = packageStats.cacheSize
String cacheStr = Formatter.formatShortFileSize(mContext, packageStats.cacheSize)
}
}
可调用Formatter.formatShortFileSize(mContext, cacheSize)
格式化文件大小。
freeStorageAndNotify方法释放存储空间
PackageDataObserver packageDataObserver = new PackageDataObserver();
try {
iPackageManager.clearApplicationUserData(packageName, packageDataObserver, UserHandle.myUserId());
StatFs stat = new StatFs(Environment.getDataDirectory().getAbsolutePath());
iPackageManager.freeStorageAndNotify(volumeUuid, (long) stat.getBlockCount() * (long) stat.getBlockSize(), packageDataObserver);
iPackageManager.deleteApplicationCacheFiles(packageName, packageDataObserver);
} catch (RemoteException e) {
e.printStackTrace();
}
private static class PackageDataObserver extends IPackageDataObserver.Stub {
@Override
public void onRemoveCompleted(String packageName, boolean result) throws RemoteException {
}
}
freeStorageAndNotify
方法通过删除所有应用程序中按 LRU 排序的缓存文件列表来释放存储空间。如果设备上当前可用的空闲存储空间大于或等于请求的空闲存储空间,则不会清除缓存文件。如果设备上当前可用的存储空间小于请求的可用存储空间,则删除所有应用程序中的部分或全部缓存文件(基于上次访问时间),以将设备上的可用存储空间增加到请求的值。无法保证从所有应用程序中清除所有缓存文件将清除足够的存储空间以实现所需的值。freeStorageSize 系统要释放的存储字节数。说如果freeStorageSize是XX,当前空闲存储是YY,如果XX小于YY,直接返回。如果可能,则释放 XX-YY 字节数。 观察者回调用于通知操作何时完成。
方法参数:
public void freeStorageAndNotify(@Nullable String volumeUuid, long freeStorageSize,
@Nullable IPackageDataObserver observer)
最终通过frameworks/native/cmds/installd/binder/android/os/IInstalld.aidl
的AIDL接口调用到frameworks/native/cmds/installd/InstalldNativeService.cpp
的Native服务进行存储释放。
Installer
方法:
InstalldNativeService.cpp
的方法:
clearApplicationUserData方法清除用户数据
通过ActivityManager
清除用户数据方式如下:
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
boolean res = am.clearApplicationUserData(packageName, packageDataObserver);
其过程AMS与PMS通信调用clearApplicationUserData
清除用户数据。
利用IPackageManager
清除用户数据的方式如下:
try {
iPackageManager.clearApplicationUserData(packageName, packageDataObserver, UserHandle.myUserId());
} catch (RemoteException e) {
e.printStackTrace();
}
clearApplicationUserData
清除用户数据的过程不仅清除保存的用户数据,同时也清除了缓存数据cacheFiles即deleteApplicationCacheFiles
,如果单独清除缓存可调用如下方法:
iPackageManager.deleteApplicationCacheFiles(packageName, packageDataObserver);
deleteApplicationCacheFiles
方法清除应用缓存,即清除cache目录。
clearApplicationUserData
最终在PMS访问Installer
与InstalldNativeService
的Native服务进行数据清理。
Installer
方法:
InstalldNativeService.cpp
的方法:
删除cache文件目录来清除缓存
除了通过上述PackageManager.deleteApplicationCacheFiles(packageName, packageDataObserver)
还有另一种方法,通过删除cache文件目录:
public void clearALLCache() {
List<PackageInfo> packList = mPackageManager.getInstalledPackages(PackageManager.DELETE_CHATTY);
for (int i=0; i < packList.size(); i++)
{
PackageInfo packInfo = packList.get(i);
//非系统应用
if ((packInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
try {
Context context = getApplicationContext().createPackageContext(packInfo.packageName,Context.CONTEXT_IGNORE_SECURITY);
deleteCache(context);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void deleteCache(Context context) {
try {
File dir = context.getCacheDir();
deleteDir(dir);
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean deleteDir(File dir) {
if (dir != null && dir.isDirectory()) {
String[] children = dir.list();
for (String child : children) {
boolean success = deleteDir(new File(dir, child));
if (!success) {
return false;
}
}
return dir.delete();
} else if(dir!= null && dir.isFile()) {
return dir.delete();
} else {
return false;
}
}
通过得到安装应用列表,遍历得到应用package的Context
,通过Context
得到缓存目录,遍历文件目录进行删除。
PMS源码中LI、LIF、LP(LPw、LPr)的含义
PackageManagerService
内部使用的两个锁来进行应用数据操作。用LI、LIF、LP(LPw、LPr)后缀名来标志加锁处理,因为其中开头字母L指的是Lock
锁,而后的字母I和P指的是两个锁,I表示mInstallLock
同步锁(安装锁),P表示mPackages
同步锁(包锁)。LPw、LPr中的w表示writing,r表示reading。LIF中的F表示Freeze
。mPackages
同步锁,是指操作mPackages
时,用对mPackages加锁保护起来。mPackages
同步锁用来保护内存中已经解析的包信息和其他相关状态。mPackages
同步锁是细粒度的锁,只能短时间持有这个锁,因为争抢mPackages
锁的请求很多,短时间持有mPackages
锁,可以让其他请求等待的时间短些。mInstallLock
同步锁,是指安装App的时候,对安装的处理要用mInstaller
加锁保护起来。同时用来保护所有对installd
的访问。installd
通常包含对应用数据的繁重操作。
由于installd
是单线程的,并且installd
的操作通常很慢,所以在已经持有mPackages
同步锁的时候,千万不要再请求mInstallLock同步锁。反之,在已经持有mInstallLock
同步锁的时候,可以去请求mPackages
同步锁。
具体参考PackageManagerService中的方法名中的LI、LIF、LPw、LPr的含义
结语
以上为利用IPackageManager
接口进行缓存垃圾清理,此为系统应用调用的接口,在使用过程中需考虑特殊应用是否需要清理以免删除重要数据!!!