在Android中,有幾個比較重要的Service。java
ActivityManagerService-------主要負責管理全部的Activity的邏輯android
WindowManagerService-------主要負責Android中窗口相關的邏輯數據結構
PackageManagerService-------主要是用來處理apk的安裝,卸載和應用程序信息的獲取的app
今天咱們主要研究一下PackageManagerService,由於這個Service咱們在平時開發過程當中會常常的遇到,因此瞭解了它的啓動流程,對於咱們App的開發會有很大的幫助。說到這裏估計有些同窗就會說,咱們平時開發不多用到PackageManagerService啊?確實咱們不多直接使用PackageManagerService這個類,可是咱們會常用PackageManager這個類吧,好比咱們要拿到當前應用版本時,一般會使用以下代碼:函數
PackageManager packageManager = getPackageManager(); // getPackageName()是你當前類的包名,0表明是獲取版本信息 PackageInfo packInfo = packageManager.getPackageInfo(getPackageName(),0);
PackageManager是什麼?它僅僅是一個接口而已,它的實現類是ApplicationPackageManager,可是當你去研究ApplicationPackageManager的源碼的時候,你會發現,它的功能其實都是經過一個mPM的變量完成的,它的類型是IPackageManager類型,若是知道Android中Binder機制(關於Binder機制能夠參考個人另一篇文章:http://blog.csdn.net/yuanzeyao/article/details/12954641)的同窗相信已經知道這個mPM是什麼東西了,它就是PackageManagerService在客戶端的一個代理,經過這個代理客戶端能夠調用到PackageManagerService中的一些方法,如獲取某一個應用的版本號,其實版本號這些信息最終都是保存在PackageManagerService中的,咱們只有經過mPM這個代理才能拿到這些信息。下面我給出一個類圖,大體的描述一下PackageManager,ApplicationpackageManager和PackageManagerService的關係。學習
經過上面拿到版本號的例子,咱們知道其實手機中全部App的信息都是保存在了PackageManagerService中的,咱們今天研究PackageManagerService的目的其實就是熟悉PackageManagerService在啓動的過程當中到底作了什麼,是如何保存這些信息的。PackageManagerService是經過SystemService啓動的,主要是經過調用PackageManagerService的main函數開始,在main函數中,其實就是建立了一個PackageManagerService的實例而且在ServiceManager中註冊,進入PackageManagerService的構造函數,你會發現這裏作了不少重量級的操做,這個也是Android系統啓動比較慢的一個主要緣由。在開始學習以前我須要提醒你們,這裏涉及到的數據結構很是多,咱們沒有必要知道每個數據結構有什麼,由於那樣可能致使咱們越陷越深,最終走不出來,咱們能夠先從宏觀分析它的啓動過程,而後再去分析它涉及的數據結構,因此爲了讓你們有一個比較清晰的輪廓,我先給出兩個時序圖,而後咱們能夠跟着這些時序圖走,咱們就永遠不會迷路了。之因此給兩個時序圖,是由於我以爲PackageManagerServiec的啓動過程大體能夠分爲兩部分:ui
建議:看文章時,最好手邊有一份Android源碼,由於這裏涉及到的代碼很是多,我不方便將代碼都貼出來spa
一、掃描xml文件並解析成對應的數據結構.net
二、掃描apk文件並解析成對應的數據結構debug
好了,咱們先看第一階段的時序圖
根據圖1-1,發現這裏引入了一個數據結構Settings,這個類主要功能就是存儲第一階段掃描xml後解析結果的,具體的結構咱們後面會分析的,建立了Setttings實例以後,調用addSharedUserLPw方法,當你查看該函數的實現時,你會發現這裏有引入了一個SharedUserSetting類型的結構,這個咱們暫且不用去分析,接下來就調用readPermissions去掃描/system/etc/permissions/中的全部的xml文件,解析結果都存儲到了上面建立的Settings類型變量了,最後調用readLPw函數讀取/data/system/packages.xml文件,第一階段就這麼簡單,就是掃描xml並解析。接下來咱們詳細分析一下Settings這個數據結構吧,我先給出這個類的類圖
這裏我列出了Settings這個類中比較重要的字段:
mSettingFilename:這個字段就是/data/system/packages.xm文件
mBackupSettingsFilename:這個字段就是/data/system/packages_backup.xml文件,這個文件不必定存在,若是存在,那麼說明上次跟新packages.xml文件出錯了。
mPackageListFilename:這個字段是/data/system/packages.list
mPackages:這個字段是一個HashMap,key 是包名,值是一個新的數據結構PackageSetting,主要包含了一個app的基本信息,如安裝位置,lib位置等等
mSharedUsers:這個字段也是一個HashMap,key是相似"android.ui.system"這樣的字段,在Android中每個應用都有一個uid,兩個有相同uid的應用能夠運行在同一個進程中的,因此爲了讓兩個應用運行在用一個進程中,每每會在Androidmanifest.xml文件中設置shareUserId這個屬性,這個屬性就是一個字符串,可是咱們知道在Linux系統中一個uid是一個整型,因此爲了將字符串和整型對應起來,就有了SharedUserSetting類型,剛纔說key是shareUserId這個屬性的值,那麼值就是SharedUserSetting類型了,ShareUserdSetting中除了name(其實就是key),uid(對應Linux系統的uid),還有一個列表字段,記錄了當前系統中有相同shareUserId的應用。
mPermissions:這個字段主要保存的是/system/etc/permissions/platform.xml中的的permission標籤的內容,由於Android系統是基於Linux系統的,因此也有用戶組的概念,在platform.xml中定義了一些權限,而且指定了哪些用戶組具備這些權限,一旦一個應用屬於某一個用戶組,那麼它就擁有了這個用戶組的全部權限
mPermissionTrees:這個字段對應packages.xml文件中的permission-trees標籤
Settings的結構就簡單的介紹到這裏吧,下面開始第二個階段:apk文件的掃描並解析。
在Android系統中,apk主要存在如下三個目錄:
/system/app:存放的是系統app
/vender/app:存放時廠商預裝app
/data/app:用戶本身安裝的app
其實還有一個地方會放一個apk文件,只不過這個apk文件很特殊,只有資源文件,沒有代碼,/system/framework/framework-res.apk
另外對於apk的解析,其實就是解析Androidmanifest.xml文件,例如版本號,包名,Application的各類屬性,包含哪些Activity,Service等等,定義了哪些權限以及運行該應用須要哪些權限等等。一樣也是先來看一看時序圖吧,以下圖:
根據圖1-2,能夠看出掃描並解析apk文件其實調用的就是scanDirLI方法,咱們就進入該方法看看吧
private void scanDirLI(File dir, int flags, int scanMode, long currentTime) { //拿到指定目錄中全部的文件 String[] files = dir.list(); if (files == null) { Log.d(TAG, "No files in app dir " + dir); return; } int i; for (i=0; i<files.length; i++) { File file = new File(dir, files[i]); //過濾掉非apk文件 if (!isPackageFilename(files[i])) { // Ignore entries which are not apk's continue; } PackageParser.Package pkg = scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime); // 若是解析失敗,而且非系統app,那麼刪掉 if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 && mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) { // Delete the apk Slog.w(TAG, "Cleaning up failed install of " + file); file.delete(); } } }
其實這個方法的邏輯很是簡單,首先拿到指定目錄下面的全部文件,並過濾掉非apk文件,而後調用scanPackageLI方法進行解析,注意這裏的scanPackageLI方法第一個參數是File,從圖1-2能夠看到後面還有一個scanPackageLI方法,它的第一個參數是PackageParser.Package。咱們繼續看第一個scanPackageLI作了什麼吧,從時序圖能夠看到首先建立了PackageParser.Package對象,並調用了parsePackage方法,這裏一樣要注意的一點就是這個parsePackage方法第一個參數是File類型,後面一樣也有一個重載的方法,第一個參數類型是Resources,咱們先看第一個parsePackage,它的邏輯其實也很簡單,就是經過AssertManager拿到了apk文件的Resource文件,而後調用重載的parsePackage方法,在重載的parsePackage方法中,經過傳入的Resources對象,拿到Androidmanifest.xml文件,並進行解析,並將結果以PackageParser.Package的形式返回給PackageManagerService,PackageManagerService爲了方便之後的訪問,須要將這個Package對象保存起來,因而調用了第二個scanPackageLI方法,咱們如今以Package例的activity數據爲例,看看怎麼保存Package中的數據
N = pkg.activities.size(); r = null; for (i=0; i<N; i++) { PackageParser.Activity a = pkg.activities.get(i); a.info.processName = fixProcessName(pkg.applicationInfo.processName, a.info.processName, pkg.applicationInfo.uid); mActivities.addActivity(a, "activity"); if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } r.append(a.info.name); } }
這裏調用了mActivitys.addActivity方法,這裏的mActivitys是ActivityIntentResolver類型,咱們看看addActivity具體幹了什麼
public final void addActivity(PackageParser.Activity a, String type) { final boolean systemApp = isSystemApp(a.info.applicationInfo); //private final HashMap<ComponentName, PackageParser.Activity> mActivities // = new HashMap<ComponentName, PackageParser.Activity>(); mActivities.put(a.getComponentName(), a); if (DEBUG_SHOW_INFO) Log.v( TAG, " " + type + " " + (a.info.nonLocalizedLabel != null ? a.info.nonLocalizedLabel : a.info.name) + ":"); if (DEBUG_SHOW_INFO) Log.v(TAG, " Class=" + a.info.name); final int NI = a.intents.size(); for (int j=0; j<NI; j++) { PackageParser.ActivityIntentInfo intent = a.intents.get(j); if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) { intent.setPriority(0); Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity " + a.className + " with priority > 0, forcing to 0"); } if (DEBUG_SHOW_INFO) { Log.v(TAG, " IntentFilter:"); intent.dump(new LogPrinter(Log.VERBOSE, TAG), " "); } if (!intent.debugCheck()) { Log.w(TAG, "==> For Activity " + a.info.name); } // addFilter(intent); } }
這裏實際上是將activity數據保存到了一個HashMap數據中,這裏涉及到了一個Intent機制的一些代碼,我將會在下面一篇文章中詳解介紹怎麼經過Intent啓動Android中的組件的。這裏就再也不介紹了。