一个小需求:计算Android App所占用d的手机内存(RAM)大小、App所产生的数据(Data)大小、App本身所占用的磁盘空间(ROM)大小。当然,这个就必须用到PackageManager了。
1、查看Android中PackageManager源码,找到getPackageSizeInfo方法:
/** *
Retrieve the size information for a package. *
Since this may take a little while, the result will *
be posted back to the given observer. The calling context *
should have the {@link android.Manifest.permission#GET_PACKAGE_SIZE} permission. * *
@param packageName The name of the package whose size information is to be retrieved *
@param observer An observer callback to get notified when the operation *
is complete. *
{@link android.content.pm.IPackageStatsObserver#onGetStatsCompleted(PackageStats, boolean)} *
The observer's callback is invoked with a PackageStats object(containing the *
code, data and cache sizes of the package) and a boolean value representing *
the status of the operation. observer may be null to indicate that *
no callback is desired. * *
@hide */ public abstract void getPackageSizeInfo(String
packageName, IPackageStatsObserver
observer); |
2、getPackageSizeInfo方法有两个参数,第一个是需要计算的App包名,第二个是一个回调。不过IPackageStatesObserver这个class在API里貌似找不到,找了点儿资料,需要通过Android AIDL的方式来搞。方法:
1)、在src目录下新建android.content.pm包
2)、在该包下新建PackageStats.aidl文件,内容如下:
package android.content.pm; parcelable
PackageStats; |
3)、在该包下新建IPackageStatsObserver.aidl接口文件,内容如下:
package android.content.pm; import android.content.pm.PackageStats; /** *
API for package data change related callbacks from the Package Manager. *
Some usage scenarios include deletion of cache directory, generate *
statistics related to code, data, cache usage(TODO) *
{@hide} */ oneway interface IPackageStatsObserver
{ void onGetStatsCompleted(in
PackageStats pStats, boolean succeeded); } |
3、getPackageSizeInfo方法不能通过context.getPackageManager.getPackageSizeInfo的方式来调用,因为它其实是一个invoke受限的方法,所以必须通过反射实现:
/** *
获取Android Native App的缓存大小、数据大小、应用程序大小 * *
@param context *
Context对象 *
@param pkgName *
需要检测的Native App包名 *
@throws NoSuchMethodException *
@throws InvocationTargetException *
@throws IllegalAccessException */ public static void getPkgSize( final Context
context, String pkgName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException
{ //
getPackageSizeInfo是PackageManager中的一个private方法,所以需要通过反射的机制来调用 Method
method = PackageManager. class .getMethod( "getPackageSizeInfo" , new Class[]
{ String. class ,
IPackageStatsObserver. class }); //
调用 getPackageSizeInfo 方法,需要两个参数:1、需要检测的应用包名;2、回调 method.invoke(context.getPackageManager(), new Object[]
{ pkgName, new IPackageStatsObserver.Stub()
{ @Override public void onGetStatsCompleted(PackageStats
pStats, boolean succeeded) throws RemoteException
{ //
子线程中默认无法处理消息循环,自然也就不能显示Toast,所以需要手动Looper一下 Looper.prepare(); //
从pStats中提取各个所需数据 Toast.makeText(context, "缓存大小=" +
Formatter.formatFileSize(context, pStats.cacheSize) + "\n数据大小=" +
Formatter.formatFileSize(context, pStats.dataSize) + "\n程序大小=" +
Formatter.formatFileSize(context, pStats.codeSize), Toast.LENGTH_LONG).show(); //
遍历一次消息队列,弹出Toast Looper.loop(); } } }); } |
我是直接在Observer回调中通过Toast的方式直接显示出来的,不过这个回调是在子线程中异步完成的,子线程中默认不处理消息循环,所以Toast.show无法被正确执行,需要手动将Toast显示部分的内容,加到Looper中,分别调用Looper.prepare和Looper.loop方法即可!
4、当然,根据PackageManager中getPackageSizeInfo注释中的提示,还需要在AndroidManifest.xml中加入permission:
< uses-permission android:name = "android.permission.GET_PACKAGE_SIZE" ></ uses-permission > |