1. 程式人生 > >計算Android App佔用的各種空間大小

計算Android App佔用的各種空間大小

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