Android之获取应用程序(包)的大小-----PackageManager的使用(二)
http://blog.csdn.net/qinjuning/article/details/6892054
?
通過第一部分 << Android中獲取應用程序(包)的信息-----PackageManager的使用(一) >>的介紹,對PackageManager以及AndroidManife.xml定義的節點信息類XXXInfo類都有了一定的認識。
??????????本部分的內容是如何獲取安裝包得大小,包括緩存大小(cachesize)、數據大小(datasize)、應用程序大小(codesize)。
本部分的知識點涉及到AIDL、Java反射機制。理解起來也不是很難。
???
????? 關于安裝包得大小信息封裝在PackageStats類中,該類很簡單,只有幾個字段:
??????????????? PackageStats類:
???????????????? 常用字段:
???????????????????????????? public long cachesize?????????? 緩存大小
???????????????????????????? public long codesize???????????? 應用程序大小
???????????????????????????? public long datasize????????????? 數據大小
???????????????????????????? public String packageName? 包名
?
???????? PS:應用程序的總大小 = cachesize? + codesize? + datasize
??????? 也就是說只要獲得了安裝包所對應的PackageStats對象,就可以獲得信息了。但是在AndroidSDK中并沒有顯示提供方法來
獲得該對象,是不是很苦惱呢?但是,我們可以通過放射機制來調用系統中隱藏的函數(@hide)來獲得每個安裝包得信息。
具體方法如下:
??????? 第一步、? 通過放射機制調用getPackageSizeInfo()? 方法原型為:??????????????
/*@param packageName 應用程序包名*@param observer 當查詢包得信息大小操作完成后,將回調給IPackageStatsObserver類中的onGetStatsCompleted()方法,* ,并且我們需要的PackageStats對象也封裝在其參數里.* @hide //隱藏函數的標記*/public abstract void getPackageSizeInfo(String packageName,IPackageStatsObserver observer);{//}??????? 內部調用流程如下,這個知識點較為復雜,知道即可,
???????? getPackageSizeInfo方法內部調用getPackageSizeInfoLI(packageName, pStats)方法來完成包狀態獲取。
getPackageSizeInfoLI方法內部調用Installer.getSizeInfo(String pkgName, String apkPath,String fwdLockApkPath,?? PackageStats
pStats),繼而將包狀態信息返回給參數pStats。getSizeInfo這個方法內部是以本機Socket方式連接到Server,
然后向server發送一個文本字符串命令,格式:getsize apkPath fwdLockApkPath 給server。Server將結果返回,并解析到pStats
中。掌握這個調用知識鏈即可。
?
?
???? 第二步、? 由于需要獲得系統級的服務或類,我們必須加入Android系統形成的AIDL文件,共兩個:
???????????? IPackageStatsObserver.aidl 和 PackageStats.aidl文件。并將其放置在android.pm.content包路徑下。
?? 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); }PackageStats.aidl文件
package android.content.pm;parcelable PackageStats;????? ?第三步、? 創建一個類繼承至IPackageStatsObserver.Stub (樁,)它本質上實現了Binder機制。當我們把該類的一個實例通過getPackageSizeInfo()調用時,并該函數繼而啟動了啟動中間流程去獲取相關包得信息大小,當掃描完成后,最后將查詢信息回調至該類的onGetStatsCompleted(in PackageStats pStats, boolean succeeded)方法,信息大小封裝在此實例上。例如:
//aidl文件形成的Bindler機制服務類public class PkgSizeObserver extends IPackageStatsObserver.Stub{/*** 回調函數,* @param pStatus ,返回數據封裝在PackageStats對象中* @param succeeded 代表回調成功*/ @Overridepublic void onGetStatsCompleted(PackageStats pStats, boolean succeeded)throws RemoteException {// TODO Auto-generated method stubcachesize = pStats.cacheSize ; //緩存大小datasize = pStats.codeSize ; //數據大小 codesize = pStats.codeSize ; //應用程序大小}}???????
?????? 第四步、? 最后我們可以獲取 pStats的屬性,獲得它們的屬性值,通過調用系統函數Formatter.formateFileSize(long size)轉換
為對應的以kb/mb為計量單位的字符串。
?
???? 很重要的一點:為了能夠通過反射獲取應用程序大小,我們必須加入以下權限,否則,會出現警告并且得不到實際值。
???????
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE"></uses-permission>
 ?
???? 流程圖如下:
Demo說明:
????????????? 在第一部分應用得基礎上,我們添加了一個新功能,點擊任何一個應用后后,彈出顯示該應用的包信息大小的對話框。
??????? 截圖如下:
?
1、dialg_app_size.xml 文件<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="wrap_content"android:layout_height="wrap_content"><LinearLayout android:layout_width="wrap_content"android:layout_height="wrap_content" android:orientation="horizontal"><TextView android:layout_width="100dip"android:layout_height="wrap_content" android:text="緩存大小:"></TextView><TextView android:layout_width="100dip" android:id="@+id/tvcachesize"android:layout_height="wrap_content"></TextView></LinearLayout><LinearLayout android:layout_width="wrap_content"android:layout_height="wrap_content" android:orientation="horizontal"><TextView android:layout_width="100dip"android:layout_height="wrap_content" android:text="數據大小:"></TextView><TextView android:layout_width="100dip" android:id="@+id/tvdatasize"android:layout_height="wrap_content"></TextView></LinearLayout><LinearLayout android:layout_width="wrap_content"android:layout_height="wrap_content" android:orientation="horizontal"><TextView android:layout_width="100dip"android:layout_height="wrap_content" android:text="應用程序大小:"></TextView><TextView android:layout_width="100dip" android:id="@+id/tvcodesize"android:layout_height="wrap_content"></TextView></LinearLayout><LinearLayout android:layout_width="wrap_content"android:layout_height="wrap_content" android:orientation="horizontal"><TextView android:layout_width="100dip"android:layout_height="wrap_content" android:text="總大小:"></TextView><TextView android:layout_width="100dip" android:id="@+id/tvtotalsize"android:layout_height="wrap_content"></TextView></LinearLayout> </LinearLayout>2、另外的資源文件或自定義適配器復用了第一部分,請知悉。3、添加AIDL文件,如上。4、主文件MainActivity.java如下:package com.qin.appsize;import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List;import com.qin.appsize.AppInfo;import android.app.Activity; import android.app.AlertDialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.IPackageStatsObserver; import android.content.pm.PackageManager; import android.content.pm.PackageStats; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.RemoteException; import android.text.format.Formatter; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener;public class MainActivity extends Activity implements OnItemClickListener{private static String TAG = "APP_SIZE";private ListView listview = null;private List<AppInfo> mlistAppInfo = null;LayoutInflater infater = null ; //全局變量,保存當前查詢包得信息private long cachesize ; //緩存大小private long datasize ; //數據大小 private long codesize ; //應用程序大小private long totalsize ; //總大小@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.browse_app_list);listview = (ListView) findViewById(R.id.listviewApp);mlistAppInfo = new ArrayList<AppInfo>();queryAppInfo(); // 查詢所有應用程序信息BrowseApplicationInfoAdapter browseAppAdapter = new BrowseApplicationInfoAdapter(this, mlistAppInfo);listview.setAdapter(browseAppAdapter);listview.setOnItemClickListener(this);}// 點擊彈出對話框,顯示該包得大小public void onItemClick(AdapterView<?> arg0, View view, int position,long arg3) {//更新顯示當前包得大小信息queryPacakgeSize(mlistAppInfo.get(position).getPkgName()); infater = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);View dialog = infater.inflate(R.layout.dialog_app_size, null) ;TextView tvcachesize =(TextView) dialog.findViewById(R.id.tvcachesize) ; //緩存大小TextView tvdatasize = (TextView) dialog.findViewById(R.id.tvdatasize) ; //數據大小TextView tvcodesize = (TextView) dialog.findViewById(R.id.tvcodesize) ; // 應用程序大小TextView tvtotalsize = (TextView) dialog.findViewById(R.id.tvtotalsize) ; //總大小//類型轉換并賦值tvcachesize.setText(formateFileSize(cachesize));tvdatasize.setText(formateFileSize(datasize)) ;tvcodesize.setText(formateFileSize(codesize)) ;tvtotalsize.setText(formateFileSize(totalsize)) ;//顯示自定義對話框AlertDialog.Builder builder =new AlertDialog.Builder(MainActivity.this) ;builder.setView(dialog) ;builder.setTitle(mlistAppInfo.get(position).getAppLabel()+"的大小信息為:") ;builder.setPositiveButton("確定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {// TODO Auto-generated method stubdialog.cancel() ; // 取消顯示對話框}});builder.create().show() ;}public void queryPacakgeSize(String pkgName) throws Exception{if ( pkgName != null){//使用放射機制得到PackageManager類的隱藏函數getPackageSizeInfoPackageManager pm = getPackageManager(); //得到pm對象try {//通過反射機制獲得該隱藏函數Method getPackageSizeInfo = pm.getClass().getDeclaredMethod("getPackageSizeInfo", String.class,IPackageStatsObserver.class);//調用該函數,并且給其分配參數 ,待調用流程完成后會回調PkgSizeObserver類的函數getPackageSizeInfo.invoke(pm, pkgName,new PkgSizeObserver());} catch(Exception ex){Log.e(TAG, "NoSuchMethodException") ;ex.printStackTrace() ;throw ex ; // 拋出異常} }}//aidl文件形成的Bindler機制服務類public class PkgSizeObserver extends IPackageStatsObserver.Stub{/*** 回調函數,* @param pStatus ,返回數據封裝在PackageStats對象中* @param succeeded 代表回調成功*/ @Overridepublic void onGetStatsCompleted(PackageStats pStats, boolean succeeded)throws RemoteException {// TODO Auto-generated method stubcachesize = pStats.cacheSize ; //緩存大小datasize = pStats.dataSize ; //數據大小 codesize = pStats.codeSize ; //應用程序大小totalsize = cachesize + datasize + codesize ;Log.i(TAG, "cachesize--->"+cachesize+" datasize---->"+datasize+ " codeSize---->"+codesize) ;}}//系統函數,字符串轉換 long -String (kb)private String formateFileSize(long size){return Formatter.formatFileSize(MainActivity.this, size); }// 獲得所有啟動Activity的信息,類似于Launch界面public void queryAppInfo() {PackageManager pm = this.getPackageManager(); // 獲得PackageManager對象Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);// 通過查詢,獲得所有ResolveInfo對象.List<ResolveInfo> resolveInfos = pm.queryIntentActivities(mainIntent, 0);// 調用系統排序 , 根據name排序// 該排序很重要,否則只能顯示系統應用,而不能列出第三方應用程序Collections.sort(resolveInfos,new ResolveInfo.DisplayNameComparator(pm));if (mlistAppInfo != null) {mlistAppInfo.clear();for (ResolveInfo reInfo : resolveInfos) {String activityName = reInfo.activityInfo.name; // 獲得該應用程序的啟動Activity的nameString pkgName = reInfo.activityInfo.packageName; // 獲得應用程序的包名String appLabel = (String) reInfo.loadLabel(pm); // 獲得應用程序的LabelDrawable icon = reInfo.loadIcon(pm); // 獲得應用程序圖標// 為應用程序的啟動Activity 準備IntentIntent launchIntent = new Intent();launchIntent.setComponent(new ComponentName(pkgName,activityName));// 創建一個AppInfo對象,并賦值AppInfo appInfo = new AppInfo();appInfo.setAppLabel(appLabel);appInfo.setPkgName(pkgName);appInfo.setAppIcon(icon);appInfo.setIntent(launchIntent);mlistAppInfo.add(appInfo); // 添加至列表中}}} }
 獲取應用程序信息大小就是這么來的,整個過程相對而言還是挺簡單的,比較難理解的是AIDL文件的使用和回調函數的處理。
仔細研究后,才有所理解。
??
?
?????? 關于PackageManager的使用的源代碼已上傳,下載地址:http://download.csdn.net/detail/qinjuning/3775856
?
? ? PS: 源碼中的AIDL所在包名錯誤,導致報錯,應為:android.content.pm。請大家修改后運行
?
?
把 getDeclaredMethod() 改成 getMethod()
 
?
總結
以上是生活随笔為你收集整理的Android之获取应用程序(包)的大小-----PackageManager的使用(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Android之中获取应用程序(包)的信
- 下一篇: Android 之PackageMana
