(轉)Android學習-應用程序管理

在前段時間,公司要求作一個Android系統的應用程序管理,要實現卸載程序、清除數據、中止正在運行的服務這幾大模塊,如今將代碼粗略總結以下:java

主要運用到的類有android

PackageManager數據庫

ActivityManager緩存

ApplicationInfo服務器

RunningServiceInfo數據結構

Methodapp

還有兩個android.pm下的源文件用於生成樁,IPackageStatsObserver.java  和 IPackageDataObserver.java,由名字能夠看出,他們是跟包的狀態和大小有關的,在網上找到這兩個文件的源碼後,把他們放在工程src目錄下的android.pm包下,本身建包。框架

首先要得到系統中已經裝了的apk,apk分爲兩類第一是系統的apk,第二是第三方的apk,因此在獲取apk時能夠指定一個過濾器,見以下代碼:ide

 

[java]  view plain copy
 
 
  1. // 添加過濾 ,獲得所有程序,系統程序,用戶本身安裝的程序  
  2.     private List<AppInfo> queryFilterAppInfo(int filter) {  
  3.         // 查詢全部已經安裝的應用程序  
  4.         List<ApplicationInfo> listAppcations = pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);  
  5.         Collections.sort(listAppcations,new ApplicationInfo.DisplayNameComparator(pm));// 排序  
  6.         List<AppInfo> appInfos = new ArrayList<AppInfo>(); // 保存過濾查到的AppInfo  
  7.         // 根據條件來過濾  
  8.         switch (filter) {  
  9.         case FILTER_ALL_APP: // 全部應用程序  
  10.             appInfos.clear();  
  11.             for (ApplicationInfo app : listAppcations) {  
  12.                 if (app.packageName.equals("com.android.appmanager")) { // 過濾本身  
  13.                     continue;  
  14.                 }  
  15.                 appInfos.add(getAppInfo(app));  
  16.             }  
  17.             return appInfos;  
  18.         case FILTER_SYSTEM_APP: // 系統程序  
  19.             appInfos.clear();  
  20.             for (ApplicationInfo app : listAppcations) {  
  21.                 if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {  
  22.                     if (app.packageName.equals("com.android.appmanager"<span style="font-family:Arial, Helvetica, sans-serif;">)</span>// wifi { // 過濾本身  
  23.                             continue;  
  24.                         }  
  25.                     appInfos.add(getAppInfo(app));  
  26.                 }  
  27.             }  
  28.             return appInfos;  
  29.         case FILTER_THIRD_APP: // 第三方應用程序  
  30.             appInfos.clear();  
  31.             for (ApplicationInfo app : listAppcations) {  
  32.                 // 非系統程序  
  33.                 if ((app.flags & ApplicationInfo.FLAG_SYSTEM) <= 0) {  
  34.                     if (app.packageName.equals("com.android.appmanager"))  
  35.                             continue;  
  36.                         }  
  37.                     appInfos.add(getAppInfo(app));  
  38.                 }  
  39.                 // 原本是系統程序,被用戶手動更新後,該系統程序也成爲第三方應用程序了  
  40.                 else if ((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {  
  41.                     if (app.packageName.equals("geeya.android.appmanage")) { // 過濾本身  
  42.                         continue;  
  43.                     }  
  44.                     appInfos.add(getAppInfo(app));  
  45.                 }  
  46.             }  
  47.             break;  
  48.         default:  
  49.             return null;  
  50.         }  
  51.         return appInfos;  
  52.     }  

 

AppInfo是我本身定義的一個類,裏面包含了應用程序的包名、數據區大小、代碼區大小、等等一些屬性。函數

 

好,如今咱們來獲取app包的數據區大小、緩存區大小、代碼區大小,這裏要用反射的機制去獲取PackageManager類的隱藏方法getPackageSizeInfo(),這個方法的具體實現是經過回調函數來實現的,這裏要用到IPackageStatsObserver這個類生成的樁。

 

[java]  view plain copy
 
 
  1. // aidl文件造成的Bindler機制服務類  
  2.     public class PkgSizeObserver extends IPackageStatsObserver.Stub {  
  3.         /*** 
  4.          * 回調函數, 
  5.          *  
  6.          * @param pStatus 
  7.          *            ,返回數據封裝在PackageStats對象中 
  8.          * @param succeeded 
  9.          *            表明回調成功 
  10.          */  
  11.         @Override  
  12.         public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) throws RemoteException {  
  13.             long cachesize; // 緩存大小  
  14.             long datasize; // 數據大小  
  15.             long codesize; // 應用程序大小  
  16.             long totalsize; // 總大小  
  17.             // System.out.println("data start init!");  
  18.             synchronized (Integer.class) {  
  19.                 cachesize = pStats.cacheSize; // 緩存大小  
  20.                 datasize = pStats.dataSize; // 數據大小  
  21.                 codesize = pStats.codeSize; // 應用程序大小  
  22.                 totalsize = cachesize + datasize + codesize;  
  23.                 Message msg = mHandler.obtainMessage();  
  24.   
  25.                 msg.what = MSG_SIZE_CHANGE;  
  26.                 Bundle bundle = new Bundle();  
  27.                 bundle.putLong("cachesize", cachesize);  
  28.                 bundle.putLong("datasize", datasize);  
  29.                 bundle.putLong("codesize", codesize);  
  30.                 bundle.putLong("totalsize", totalsize);  
  31.   
  32.                 bundle.putString("packageName", pStats.packageName);  
  33.                 msg.obj = bundle;  
  34.                 mHandler.sendMessage(msg);  
  35.             }  
  36.         }  
  37.     }  
  38.       
  39.     //獲取每一個apk的大小信息,包括數據區、代碼區、緩存區  
  40.     // 查詢包的大小信息  
  41.     public void queryPacakgeSize(String pkgName) throws Exception {  
  42.         if (pkgName != null) {  
  43.             // 使用放射機制獲得PackageManager類的隱藏函數getPackageSizeInfo  
  44.             PackageManager pm = getPackageManager(); // 獲得pm對象  
  45.             try {  
  46.                 // 經過反射機制得到該隱藏函數  
  47.                 Method getPackageSizeInfo = pm.getClass().getDeclaredMethod("getPackageSizeInfo", String.class,  
  48.                         IPackageStatsObserver.class);  
  49.                 getPackageSizeInfo.invoke(pm, pkgName, new PkgSizeObserver());  
  50.             } catch (Exception ex) {  
  51.                 // Log.e(TAG, "NoSuchMethodException");  
  52.                 ex.printStackTrace();  
  53.                 throw ex; // 拋出異常  
  54.             }  
  55.         }  
  56.     }  

或獲得app的大小數據後,封裝成消息發送出去,這是最好的方法!!

 

這裏也介紹一個將long型數據轉換成文件大小格式的數據。

 

[java]  view plain copy
 
 
  1. // 系統函數,字符串轉換 long -String (kb)  
  2. private String formateFileSize(long size) {  
  3.     return Formatter.formatFileSize(MainActivity.this, size);  
  4. }  

 

 

好,如今咱們來清除用戶數據,這裏要用到以前下載的那個文件IPackageDataObserver,跟獲取app大小同樣的,經過回調來實現。

 

[java]  view plain copy
 
 
  1.      // 清除用戶數據的操做  
  2. class ClearUserDataObserver extends IPackageDataObserver.Stub {  
  3.     public void onRemoveCompleted(final String packageName,final boolean succeeded) {  
  4.         final Message msg = mHandler.obtainMessage();  
  5.         if (succeeded) {  
  6.             msg.what = CLEAR_USER_DATA;  
  7.         } else {  
  8.             msg.what = NOT_CLEAR_USER_DATA;  
  9.         }  
  10.         mHandler2.sendMessage(msg);  
  11.     }  
  12. }  
  13. // 清除apk的數據  
  14. public void clearAppUserData(String pkgname){  
  15.     // 經測試,該方法不能用反射獲得,沒辦法,我只好這樣寫,只能在源碼下編譯。  
  16.     pm.clearApplicationUserData(pkgname, new ClearUserDataObserver());  
  17. }  

 

 

好,如今到卸載程序的時候了,看代碼

 

[java]  view plain copy
 
 
  1.       // 卸載apk   
  2. ublic void unInstallApp(String pkgname) {  
  3. Log.e("unInstallApp(String pkgname)","pkgname  is"+ pkgname);  
  4. Intent intent = new Intent();  
  5. // 該動做是我在android框架層本身定義的一個動做,DELETE.HIDE,代表直接就卸載了。不通過系統原生的那一個對話。  
  6. intent.setAction("android.intent.action.DELETE.HIDE"); // 本身定義的動做,DELETE.HIDE,不須要通過系統的確認卸載界面。直接卸載!  
  7. Uri packageURI = Uri.parse("package:" + pkgname);  
  8. intent.setData(packageURI);  
  9. startActivity(intent);  


關於apk的管理就差很少了,如今來看看正在運行的服務的管理

 

首先,獲取正在運行的服務:

這裏個人RunningInfo是我本身定義的一個類,主要服務的一些屬性,好比包名、uid、pid等等那些

 

[java]  view plain copy
 
 
  1. // 獲得正在運行的服務  
  2. public List<RunningInfo> getRunningService() {  
  3.     List<RunningServiceInfo> runServiceList = am.getRunningServices(30);  
  4.     List<RunningInfo> Services_List = new ArrayList<RunningInfo>(); // 保存過濾查到的AppInfo  
  5.     Log.e("getRunningService.size = ",  
  6.             new Integer(runServiceList.size()).toString());  
  7.     String pkgname = "";  
  8.     ApplicationInfo appByPkgName = null;  
  9.     for (RunningServiceInfo info : runServiceList) {  
  10.         pkgname = info.service.getPackageName();  
  11.         // System.out.println("service package is :" + pkgname +  
  12.         // "   pid = "+ info.pid); // 程序的ID號  
  13.         // 過濾掉這些系統自己的服務。只顯示用戶安裝的服務  
  14.         if (pkgname.equals("com.android.appmanager") ) { // 過濾本身  
  15.                 continue;  
  16.             }  
  17.         try {  
  18.             appByPkgName = pm.getApplicationInfo(pkgname,  
  19.                     PackageManager.GET_UNINSTALLED_PACKAGES); // 最後一個參數通常爲0  
  20.                                                                 // 就好。  
  21.         } catch (NameNotFoundException e) {  
  22.             // Log.e("MainActivity 841 line","appByPkgName = pm.getApplicationInfo(pkgname, PackageManager.GET_UNINSTALLED_PACKAGES) Exception !");  
  23.             e.printStackTrace();  
  24.         }  
  25.   
  26.         Services_List.add(getRunningInfo(appByPkgName)); // 裏面含有相同的包的服務。這裏哦咱們只要求顯示一個便可。  
  27.     }  
  28.     // 放入set中過濾相同包名的, 這裏我複寫了RunningInfo 的compareTo方法你 規則是 pkgName相等兩個對象就算相等!  
  29.     Set<RunningInfo> set = new HashSet<RunningInfo>();  
  30.     for (RunningInfo x : Services_List) {  
  31.         set.add(x);  
  32.     }  
  33.     for (RunningInfo y : set) {  
  34.         Services_List.add(y);  
  35.     }  
  36.     return Services_List;  
  37. }  



 

好,獲取到了正在運行的服務以後,就能夠隨意中止服務了,中止服務的代碼是:

 

[java]  view plain copy
 
 
  1.       // 強行中止一個app  
  2. ublic boolean stopApp(String pkgname) {  
  3. boolean flag = false;  
  4. ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);  
  5. try {  
  6.     Method forceStopPackage;  
  7.     forceStopPackage = am.getClass().getDeclaredMethod("forceStopPackage", String.class); // 反射獲得隱藏方法(hide)  
  8.     forceStopPackage.setAccessible(true);//獲取私有成員變量的值  
  9.     forceStopPackage.invoke(am, pkgname);  
  10.     flag = true;  
  11. catch (IllegalArgumentException e) {  
  12.     e.printStackTrace();  
  13.     flag = false;  
  14. catch (IllegalAccessException e) {  
  15.     e.printStackTrace();  
  16.     flag = false;  
  17. catch (InvocationTargetException e) {  
  18.     e.printStackTrace();  
  19.     flag = false;  
  20. catch (SecurityException e) {  
  21.     e.printStackTrace();  
  22.     flag = false;  
  23. catch (NoSuchMethodException e) {  
  24.     e.printStackTrace();  
  25.     flag = false;  
  26. }  
  27. return flag;  

一樣也是用反射的機制來獲得隱藏類。

 

到這裏,應用程序管理的功能就差很少了,剩下就只是界面上的事情和程序的處理流程上的事情,應該還好!

 

 

 

 

 

原文:http://www.ophonesdn.com/article/show/20

 

 

1、概述

      07年,Google公司推出了基於Java語言的Android平臺,引發了IT業界的一致關注,同年,播思通信公司開始研發基於其的OPhone平臺。

      08年,第一款Android手機G1面世,這款Google爲HTC量身打造的滑蓋手機由T Mobile發行,得到了用戶的極大好評,也爲整個Android平臺的正式商用做出了榜樣。

      09年,將是OPhone/Android手機的第一個井噴年,播思通信聯合中國移動和各個手機廠商即將推出多款觸屏手機。同時,Android社區也日趨壯大,已擁有各類應用五百多個,並在快速增加之中。

      本文將對Android應用結構及Android系統的應用管理加以介紹。

      本文面對的讀者是對Android應用的研發人員、或有過Android開發經驗的入門者。

2、Android應用結構 

什麼是Android應用?

      理解什麼是Android應用是Android入門所必須的,也是瞭解應用管理的前提。

      那什麼是Android應用呢?即指基於Android開發,編譯,運行在Android平臺的應用。這種應用在編譯階段被打成一個jar包,以.apk結尾,包內包含了全部運行階段須要的代碼與資源,其中主要分爲三塊:描述文件AndroidManifest.xml、代碼段和運行時資源。

AndroidManifest.xml

      AndroidManifest.xml是Android應用的描述文件,它描述了該應用的相關信息,主要包括如下各個元素。

包名(package)

      指定本應用內java主程序包的包名。當沒有指定apk的文件名時,編譯後產生程序包將以此命名。本包名應當在Android系統運行時惟一。

認證(certificate)

      指定本應用程序所授予的信任級別,目前有的認證級別有platform(system)、shared、media以及應用自定義的認證。不一樣的認證能夠享受不一樣的權限。

權限組(permission-group)

      權限組的定義是爲了描述一組具備共同特性的權限。Android系統中預訂了一些組,它們是:

  • android.permission-group.COST_MONEY
  • android.permission-group.MESSAGES
  • android.permission-group.MESSAGES
  • android.permission-group.PERSONAL_INFO
  • android.permission-group.LOCATION
  • android.permission-group.NETWORK
  • android.permission-group.ACCOUNTS
  • android.permission-group.HARDWARE_CONTROLS
  • android.permission-group.PHONE_CALLS
  • android.permission-group.SYSTEM_TOOLS
  • android.permission-group.DEVELOPMENT_TOOLS

      從字面意思咱們就能夠理解每一個組的特性。具體含義能夠參考SDK文檔。

權限(permission)

      權限用來描述是否擁有作某件事的權力。Android系統中權限是分級的,前分爲普通級別(Normal),危險級別(dangerous),簽名級別(signature)和系統/簽名級別(signature or system)。

      系統中全部預約義的權限根據做用的不一樣,分別屬於不一樣的級別。

      對於普通和危險級別的權限,咱們稱之爲低級權限,應用申請即授予。其餘兩級權限,咱們稱之爲高級權限或系統權限,應用擁有platform級別的認證才能申請。

      當應用試圖在沒有權限的狀況下作受限操做,應用將被系統殺掉以警示。

      系統應用可使用任何權限。權限的聲明者可無條件使用該權限。

      目前Android系統定義了許多權限,經過SDK文檔用戶能夠查詢到哪些操做須要哪些權限,而後按需申請。

權限樹(permission-tree)

      權限樹的設置是爲了統一管理一組權限,聲明於該樹下的權限全部者歸屬該應用。系統提供了API,應用能夠在運行時動態添加。
     PackageManager.addPermission()
 

使用權限(uses-permission)

      應用須要的權限應當在此處申請,所申請的權限應當被系統或某個應用所定義,不然視爲無效申請。
      同時,使用權限的申請須要遵循權限授予條件,非platform認證的應用沒法申請高級權限。
 

SDK(uses-sdk)

      標識本應用運行的SDK版本。高兼容性的應用能夠忽略此項。

application

      application是Android應用內最高級別(top level)的模塊,每一個應用內最多隻能有一個application,若是應用沒有指定該模塊,一個默認的application將被啓用。

      application將在應用啓動時最早被加載,並存活在應用的整個運行時生命週期。所以一些初始化的工做適合在本模塊完成。

      Application元素有許多屬性,其中:「persistent」表示本應用是否爲常駐內存,「enable」表示本應用當前是否應當被加載。

      其它相關屬性請參考SDK文檔,開發者能夠根據須要添加。

      在AndroidManifest.xml文件中,運行時模塊的定義都做爲本模塊的子元素。

      當運行時模塊被調度時,若是應用沒有啓動,將首先啓動應用進行初始化,而後調度對應模塊。

activity

      activity是application模塊的運行時子元素,標識了一個UI。除了application,一個應用能夠聲明並實現零至多個其它運行時模塊,activity也一樣。

      activity也包含了許多定義它工做狀態的屬性,其中:「name」是必須的,它指定了該activity所在的文件名,若是該文件所屬包不一樣於該應用的包名(即本描述文件的最開始處),那麼名字前面須要加入所在包名。

      activity經過增長intent-fliter來標識哪些intent能夠被處理,同時intent也是調度activity的主要參數。

      做爲一個運行時的對象,activity的調度方式大體分爲兩種:一種是指定activit所在類名直接調度,另外一種是利用activity能夠處理的intent進行調度。

      同一種intent能夠被不一樣應用的不一樣activity處理。當出現此種狀況時,用戶須要選擇具體的activity。

receiver

      receiver也是application的運行時子元素。

      receiver經過增長intent-fliter來標識它須要接受哪些intent。當收到intent後,receiver將根據不一樣的intent進行不一樣的處理。

      當一個Intent發出後,全部註冊了該intent的receiver都將會收到,系統會根據receiver在系統中的註冊次序順序發送。當一個receiver處理完該Intent後,系統纔會向下一個receiver發送。

      當一個receiver有多個未接收的intent時,將按照intent發送的次序順序接收。

service

      service也是application的運行時子元素。Service屬於後臺模塊,啓動後將長時間運行,除非中止該service或所在應用進程被殺死。

      service從功能上分爲兩種,一種是服務於本應用,此時的service是一個普通的運行時模塊,另外一種是服務於全部應用或對應應用,此時須要定義API並將之公佈來與其它應用進行交互。

      service須要經過API:startService()添加到service管理器中,添加後即在後臺運行。它接受外界信息的方式分兩種:一種是增長IntentFilter來接收intent,一種是外界應用調用該service所定義的API。

provider

      provider也是application的運行時子元素。它繼承於ContentProvider,是對該應用管理的用戶數據的結構化接入,是基於數據庫操做方式的封裝。

      若是應用容許外部應用訪問/管理它的用戶數據,provider是Android平臺提供的最佳方式。

activity-alias

      顧名思義,是已有activity的別名。

uses-library

      標識應用啓動所必須的共享庫。

代碼段

      應用全部的java文件被放入一個包結構,該包命名爲classes.dex。

運行時資源

      Android應用運行時所需的各類資源有layout,drawable,string,style等類型。編譯後全部資源統一存放在項目路徑/res裏,按照用途的不一樣存放在各個子文件夾中。

      編譯階段,全部resource將被排序,每一個resource在程序包中都擁有惟一的標識,同時一個名爲resources.arsc的文件生成並被置入安裝包中,該文件包含了全部索引以供運行時快速查詢。而resource以一樣目錄結構組織放入安裝包中。

應用管理

      應用管理是對系統中全部應用整個生命週期的管理。Android系統中,應用都是以.apk文件的形式存在,所以應用管理也能夠簡單理解爲對.apk文件的管理。

      從一個應用在系統中的生命週期來看,應用管理能夠分爲安裝,卸載和使用等部分。

應用安裝

      Android系統中,安裝應用時不能指定安裝目錄,全部的應用都只能裝在預置文件夾下。

      Android系統支持數種安裝方式,而播思通信的Ophone平臺在此基礎上支持更多方式。

<使用預置安裝工具

      Android系統中,應用安裝包.apk文件屬於默認支持的文件類型,它的的mime type被定義爲"application/vnd.android.package-archive"。

      系統內置了安裝工具來解析並安裝.apk文件。你能夠從OPhone平臺的文件管理器中找到對應安裝包,點擊便可啓動安裝步驟。這裏給出了相應的代碼,有興趣的開發者能夠嘗試使用。

  1. Intent apkintent = new Intent(Intent.ACTION_VIEW);                 
  2. final Uri puri = Uri.fromFile(new File(path));    //path is the path of source apk   
  3. apkintent.setDataAndType(puri, "application/vnd.android.package-archive");   
  4. startActivity(apkintent);  
Java代碼   收藏代碼
  1. Intent apkintent = new Intent(Intent.ACTION_VIEW); final Uri puri = Uri.fromFile(new File(path)); //path is the path of source apk apkintent.setDataAndType(puri, "application/vnd.android.package-archive"); startActivity(apkintent);  
 

使用API安裝

      Android系統提供了安裝應用的API:PackageManager.installPackage(final Uri packageURI, 
     final IPackageInstallObserver observer, final int flags); 

  • uri給出了原文件的地址
  • observer實現了回調函數packageInstalled(final String packageArchiveName, final int returnCode) 以檢查安裝是否成功
  • flags提供安裝標識。Android平臺提供的標識有: 
1.PackageManager.FORWARD_LOCK_PACKAGE = 0x00000001
標識本應用屬於受限應用,應用將會被安裝到保護目錄,只有該應用能夠訪問代碼段和不屬於resource的asset資源。
2.PackageManager.REPLACE_EXISTING_PACKAGE = 0x00000002;
應用如已存在,則先刪除再安裝。
3.PackageManager.SDCARD_PACKAGE = 0x00000004;
 

      由OPhone平臺擴展,標識該應用安裝目錄爲存儲卡,默認爲data分區。

      由於手機中data分區大小有限,所以OPhone平臺默認將應用裝到存儲卡上。

      本API須要"android.permission.INSTALL_PACKAGES"權限,該權限屬於簽名級別,所以只有系統級別的應用程序才能使用。

拷貝即安裝

      Android支持拷貝即安裝的方式,即將安裝包拷貝到預置安裝目錄便可自動安裝,但全部的預置目錄對於用戶不可見。

      OPhone平臺擴展後,用戶可將安裝包拷入存儲卡的app目錄下,系統將監控該文件夾,檢測到新的安裝文件後自動安裝。

      這種方式適合一次安裝大量應用,可是也有壞處。一是沒法知道哪些應用安裝成功,哪些安裝失敗, 二是安裝失敗的應用將會做爲垃圾存放在預置目錄中,在系統啓動時延長啓動時間,三是過多的應用可能將data分區的空間佔用過多,影響系統的正常使用。

安裝步驟

  • 解析安裝包是否合法,若是合法即加載應用,若是非法即放棄安裝
  • 取出代碼段,放入/data/dalvik-cache目錄
  • 創建/data/data/包名的文件夾,以存放用戶數據
  • 將安裝包攜帶的lib放入/data/data/包名/lib中
  • 以上步驟完成後將安裝包拷到安裝目錄
  • 加載該應用信息 
相關文章
相關標籤/搜索