一個小需求:計算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 > |