Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 4.4(KitKat)中apk包的安裝過程

Android 4.4(KitKat)中apk包的安裝過程

編輯:關於Android編程

 
 

其實對於apk包的安裝,4.4和之前版本沒大的差別。Android中app安裝主要有以下幾種情況:系統啟動時安裝,adb命令安裝,Google Play上下載安裝和通過PackageInstaller安裝。安裝的最核心方法是scanPackageLI(),以上幾個安裝方式最後都是調用這個函數完成主要工作的,區別在於在此之前的處理過程不同。本文以前兩種為主,簡要介紹這四種安裝過程。

 

一個最一般的apk包(不是系統app,沒有共享lib,不是裝在sd上或是forward-lock的app)裝完後內容會體現在這麼幾個目錄:

/data/app // apk包

/data/app-lib// native lib

/data/data //數據目錄,其中的lib目錄指向上面的/data/app-lib目錄

/data/dalvik-cache/data@app@[email protected] //優化或編譯後的Java bytecode

 

 

一、啟動時安裝

 

Android啟動時會把已有的app安裝一遍,過程主要分三部分:讀取安裝信息,掃描安裝,寫回安裝信息。讀取和寫回主要是針對於一坨安裝信息文件。這些信息保證了啟動後app與上一次的一致。關鍵步是掃描指定目錄下的apk並安裝。Android中apk主要分布在以下幾個目錄,意味著啟動時要掃描的主要也是這幾個目錄:

系統核心應用:/system/priv-app
系統app:/system/app
非系統app:/data/app(安裝於手機存儲的一般app)或/mnt/asec//pkg.apk(sdcard或forward-locked)
受DRM保護app:/data/app-private
vendor-specific的app: /vendor/app
資源型app:/system/framework

 

整個啟動時安裝的流程大體如下:

\

 

幾個同名函數一開始看得會有些混淆,內層scanPackageLI()比較復雜這裡省略細節。下面更加詳細地分析下流程。故事從System Server開始,實現在/frameworks/base/services/java/com/android/server/SystemServer.java中:

241            pm = PackageManagerService.main(context, installer,
242                    factoryTest != SystemServer.FACTORY_TEST_OFF,
243                    onlyCore);
調用PMS的main()函數,其實現位於/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java:

 

1040    public static final IPackageManager main(Context context, Installer installer,
1041            boolean factoryTest, boolean onlyCore) {
1042        PackageManagerService m = new PackageManagerService(context, installer,
1043                factoryTest, onlyCore);
1044        ServiceManager.addService(package, m);
1045        return m;
1046    }

 

這裡構造了PMS並加到ServiceManager中,這樣其它的組件就可以用該服務了。PMS的構造函數中:

 

1084        mSettings = new Settings(context); // 用於存放和操作動態安裝信息,具體地說,如uid, permission等。
...
1115        mInstaller = installer;  // installd daemon的proxy類。
…
1124            mHandlerThread.start(); // 啟動PMS的工作線程。
1125            mHandler = new PackageHandler(mHandlerThread.getLooper()); // PMS會通過mHandler丟活給工作線程。
...
1129            File dataDir = Environment.getDataDirectory(); // /data
1130            mAppDataDir = new File(dataDir, data); // /data/data
1131            mAppInstallDir = new File(dataDir, app); // /data/app
1132            mAppLibInstallDir = new File(dataDir, app-lib); // /data/app-lib

 

可以看到PMS除了主線程,還會有一個叫PackageManager的工作線程。它主要是用在其它安裝方式中,因為啟動時沒什麼用戶交互,基本上不需要把工作交給後台。

 

262    final HandlerThread mHandlerThread = new HandlerThread(PackageManager,
263            Process.THREAD_PRIORITY_BACKGROUND);
權限信息在/etc/permissions目錄中,由readPermissions()函數讀取,保存在Settings中的mPermissions中。接下來在PMS構造函數中調用readLPw()來解析packages.xml文件得到上次的安裝信息。

 

 

1144            mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
1145                    mSdkVersion, mOnlyCore);
Android啟動時要掃描和安裝現有app。或許是為了防止corrupted信息的存在,或許是為了能在升級系統或更新系統屬性後保持app也valid,亦或是因為有些信息(如uid)必須是動態生成和調整的。總之,為了要還原現有app的安裝信息,這些信息被放在/data/system/packages.xml裡,由Settings管理。另外/data/system/packages.list記錄了app的uid和數據路徑等信息。readLPw()就是用於恢復這些信息,實現位於/frameworks/base/services/java/com/android/server/pm/Settings.java。readLPw()先打開packages.xml,再通過XmlPullParser類來解析其內容。它會根據不同的tag調用相應的函數來讀取信息:

 

1712                String tagName = parser.getName();
1713                if (tagName.equals(package)) {
1714                    readPackageLPw(parser);
1715                } else if (tagName.equals(permissions)) {
1716                    readPermissionsLPw(mPermissions, parser);
1717                } else if (tagName.equals(permission-trees)) {
1718                    readPermissionsLPw(mPermissionTrees, parser);
1719                } else if (tagName.equals(shared-user)) {
1720                    readSharedUserLPw(parser);
1721                } else if (tagName.equals(preferred-packages)) {
1722                    // no longer used.
1723                } else if (tagName.equals(preferred-activities)) {
1724                    // Upgrading from old single-user implementation;
1725                    // these are the preferred activities for user 0.
1726                    readPreferredActivitiesLPw(parser, 0);
1727                } else if (tagName.equals(updated-package)) {
1728                    readDisabledSysPackageLPw(parser);

 

packages.xml中的一個apk包對應的package結構大體如下:


...

其中readPackageLPw()調用addPackageLPw()注冊app信息到變量mPackages中,uid信息到mUserIds/mOtherUserIds中,准確地說,應該是把這些信息從文件恢復回去。addPackageLPw()會創建PackageSetting,該類描述了該apk包的安裝信息。
325        p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString,
326                vc, pkgFlags);
327        p.appId = uid;
328        if (addUserIdLPw(uid, p, name)) { 
329            mPackages.put(name, p); 
330            return p;
331        }

注意Android中app的uid的范圍區間是FIRST_APPLICATION_UID ~LAST_APPLICATION_UID,即10000 ~ 99999,FIRST_APPLICATION_UID之下的給系統應用。

 

碰到shared user就更麻煩了,因為有時多個app為了共享權限會共享一個uid。如果一個app要用共享uid,需要在apk的AndroidManifest.xml文件中申明android:sharedUserId=...,詳見 http://developer.android.com/guide/topics/manifest/manifest-element.html。 這時就要先為其創建PendingPackage並放到mPendingPackages等共享uid部分搞定了再處理。在packages.xml中共享用戶表示為:

...

readSharedUserLPw()用來處理app安裝信息中的共享用戶部分。它會調用addSharedUserLPw()來添加共享用戶。addSharedUserLPw()為共享用戶創建SharedUserSetting類(SharedUserSetting包含了一個PackageSetting的集合),再調用addUserIdLPw()函數注冊到mUserIds中(或mOtherUserIds),成功的話另外還會寫入mSharedUsers中。

 

345        s = new SharedUserSetting(name, pkgFlags);
346        s.userId = uid;
347        if (addUserIdLPw(uid, s, name)) {
348            mSharedUsers.put(name, s);
349            return s;
350        }
綜合上面的信息,就有了下面這樣的結構:

 

\


我們知道packages.xml放了app在之前安裝時的配置信息。這裡可以有兩點推論:當一個app卸載後packages.xml中該app的信息也被刪除了。當卸載以後下一次安裝同一個app時會重新生成,uid不會被保留。


回到readLPw()後,處理前面那些因為用了共享用戶而待處理的app,也就是mPendingPackages裡的那坨。完了再回到PackageManagerService()。mSharedLibraries裡放的一些共享的java庫,這裡會調用dexopt()對它們進行優化。
1199                        if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
1200                            alreadyDexOpted.add(lib);
1201                            mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
1202                            didDexOpt = true;
1203                        }
alreadyDexOpted記錄已經運行過dexopt的文件,像啟動類和上面的共享庫。下面是對framework裡的包和類進行優化。
1231                for (int i=0; i<frameworkfiles.length; ...="" 1243="" 1244="" if="" pre="">接下來,監控/system/framework目錄並掃描該目錄。
1276            // Find base frameworks (resource packages without code).
1277            mFrameworkInstallObserver = new AppDirObserver(
1278                frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
這裡用Observer模式來監視目錄變動。它依賴於Linux kernel提供的Inotify機制。實現主要位於/frameworks/base/core/java/android/os/FileObserver.java和/frameworks/base/core/jni/android_util_FileObserver.cpp。對於它的繼承類(如AppDirObserver),只要實現onEvent()方法來處理文件或目錄變動即可。onEvent()的實現比如/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java中:
6384        public void onEvent(int event, String path) {
...
6450                        p = scanPackageLI(fullPath, flags,
6451                                SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
6452                                System.currentTimeMillis(), UserHandle.ALL);
...
6460                            synchronized (mPackages) {
6461                                updatePermissionsLPw(p.packageName, p,
6462                                        p.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0);
6463                            }
...
6471                synchronized (mPackages) {
6472                    mSettings.writeLPr();
6473                }
掃描該目錄的目的是要安裝裡邊的apk包。主要實現函數是scanDirLI():
1280            scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
1281                    | PackageParser.PARSE_IS_SYSTEM_DIR
1282                    | PackageParser.PARSE_IS_PRIVILEGED,
1283                    scanMode | SCAN_NO_DEX, 0);
對於其它幾個目錄(/system/priv-app,/system/app,/vendor/app, /data/app, /data/app-private),也是一樣的:
1380                mAppInstallObserver = new AppDirObserver(
1381                    mAppInstallDir.getPath(), OBSERVER_EVENTS, false, false);
1382                mAppInstallObserver.startWatching();
1383                scanDirLI(mAppInstallDir, 0, scanMode, 0);
…
全安裝好了就可以更新權限信息並且寫回安裝信息了。
1446            updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
1447                    | (regrantPermissions
1448                            ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
1449                            : 0));
...
1457            // can downgrade to reader
1458            mSettings.writeLPr();
這樣啟動時安裝主要工作就差不多完成了。下面回頭看一下重頭戲 - 目錄的掃描和安裝,也就是scanDirLI()函數:
scanDirLI()
	scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
		PackageParser pp = new PackageParser(scanPath);
		final PackageParser.Package pkg = pp.parsePackage(scanFile, scanPath, mMetrics, parseFlags);
			assmgr = new AssetManager();
			parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
			pkg = parsePackage(res, parser, flags, errorText); // parse AndroidManifest.xml
		...
		PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user);
可以看到scanPackageLI()和parsePackage()皆有重載版本。基本上內層的版本才是做事的。內層的parsePackage(res, ...)函數用於解析AndroidManifest.xml文件。實現在/frameworks/base/core/java/android/content/pm/PackageParser.java。至於AndroidManifest.xml是apk中必不可少的配置文件。詳見http://developer.android.com/guide/topics/manifest/manifest-intro.html,沒有的話Android壓根不讓你裝。
1034            String tagName = parser.getName();
1035            if (tagName.equals(application)) {
1036                if (foundApp) {
1037                    if (RIGID_PARSER) {
1038                        outError[0] =  has more than one ;
1039                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1040                        return null;
1041                    } else {
1042                        Slog.w(TAG,  has more than one );
1043                        XmlUtils.skipCurrentTag(parser);
1044                        continue;
1045                    }
1046                }
1047
1048                foundApp = true;
1049                if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
1050                    return null;
1051                }
1052            } else if (tagName.equals(keys)) {
1053                if (!parseKeys(pkg, res, parser, attrs, outError)) {
1054                    return null;
1055                }
1056            } else if (tagName.equals(permission-group)) {
1057                if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
1058                    return null;
1059                }
1060            } else if (tagName.equals(permission)) {
1061                if (parsePermission(pkg, res, parser, attrs, outError) == null) {
1062                    return null;
1063                }
1064            } else if (tagName.equals(permission-tree)) {
1065                if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
1066                    return null;
1067                }
1068            } else if (tagName.equals(uses-permission)) {
1069                if (!parseUsesPermission(pkg, res, parser, attrs, outError)) {
1070                    return null;
1071                }
比較重要的如parseApplication()是解析application標簽裡的東西。application標簽裡包含Android四大組件(Activity, Receiver, Service, Content Provider)信息和庫等信息。一頓解析後,返回結果PackageParser.Package對象pkg,這個類基本上就包含了AndroidManifest.xml裡的信息。接下來scanPackageLI(pkg, ...)被調用,前面返回的解析結果pkg被當作參數傳入。scanPackageLI(pkg, ...)干的事還挺多的。如處理共享用戶,注冊包信息,調用NativeLibraryHelper和Installer的相關函數進行安裝等等。

如果該app使用了共享用戶,則調用getSharedUserLPw()函數獲取該共享uid的SharedUserSetting,沒有的話就新建,然後分配uid:

 

245    SharedUserSetting getSharedUserLPw(String name,
246            int pkgFlags, boolean create) {
247        SharedUserSetting s = mSharedUsers.get(name);
248        if (s == null) {
249            if (!create) {
250                return null;
251            }
252            s = new SharedUserSetting(name, pkgFlags);
253            s.userId = newUserIdLPw(s);
254            Log.i(PackageManagerService.TAG, New shared user  + name + : id= + s.userId);
255            // < 0 means we couldn't assign a userid; fall out and return
256            // s, which is currently null
257            if (s.userId >= 0) {
258                mSharedUsers.put(name, s);
259            }
260        }
261
262        return s;
263    }
在scanPackageLI(pkg, ...)函數中,下面調用getPackageLPw()得到該apk包的PackageSetting對象,有些在前面readPackageLPw()時就已經恢復好了,還沒有的那些這兒就會新建:

 

4302            // Just create the setting, don't add it yet. For already existing packages
4303            // the PkgSetting exists already and doesn't have to be created.
4304            pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
4305                    destResourceFile, pkg.applicationInfo.nativeLibraryDir,
4306                    pkg.applicationInfo.flags, user, false);

 

getPackageLPw()實現在/frameworks/base/services/java/com/android/server/pm/Settings.java中:

 

392    private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
393            String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
394            String nativeLibraryPathString, int vc, int pkgFlags,
395            UserHandle installUser, boolean add, boolean allowInstall) {
…
457                p = new PackageSetting(name, realName, codePath, resourcePath,
458                        nativeLibraryPathString, vc, pkgFlags);
...
520                        // Assign new user id
521                        p.appId = newUserIdLPw(p);

 

newUserIdLPw()函數為app分配uid。這樣,該應用對應的uid就設置好了。

 

回到scanPackageLI()中,下面設置進程名,進程名默認就是包名。所以我們在ps裡看到的都是包名。

 

4422        pkg.applicationInfo.processName = fixProcessName(
4423                pkg.applicationInfo.packageName,
4424                pkg.applicationInfo.processName,
4425                pkg.applicationInfo.uid);
對於大部分全新安裝的一般應用而言,接下來為應用創建數據目錄:

 

 

3987    private int createDataDirsLI(String packageName, int uid, String seinfo) {
3988        int[] users = sUserManager.getUserIds();
3989        int res = mInstaller.install(packageName, uid, uid, seinfo);
...
3993        for (int user : users) {
3994            if (user != 0) {
3995                res = mInstaller.createUserData(packageName,
3996                        UserHandle.getUid(user, uid), user);
Installer是一個代理類,它會和後台的installd通信並讓installd完成具體工作。installd的實現位於/frameworks/native/cmds/installd/commands.c,install()會創建app目錄(/data/data/),並作lib目錄的軟鏈接,如/data/data/xxx/lib -> /data/app-lib/xxx。

 

 

下面設置原生庫目錄為/data/data//lib,它指向/data/app-lib。再把原生庫文件(如有)解壓到該目錄下。

 

4556                if (pkgSetting.nativeLibraryPathString == null) {
4557                    setInternalAppNativeLibraryPath(pkg, pkgSetting);
4558                } else {
4559                    pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString;
4560                }
...
4577        if (pkg.applicationInfo.nativeLibraryDir != null) {
4578            try {
4579                File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
...
4605                        try {
4606                            if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) { 
4607                                Slog.e(TAG, Unable to copy native libraries);
4608                                mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
4609                                return null;
這裡調用copyNativeLibrariesForInternalApp(),它會調用NativeLibraryHelper.copyNativeBinariesIfNeededLI()把apk裡的原生庫解壓出來放到/data/app-lib的對應目錄下。

接下來PMS調用performDexOptLI()優化Java的bytecode,即dex文件。
4638        if ((scanMode&SCAN_NO_DEX) == 0) {
4639            if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false)
4640                    == DEX_OPT_FAILED) {
4641                mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
4642                return null;
4643            }
4644        }
真正的工作還是在後台installd進程中完成, installd中的dexopt()函數會根據當前是運行dalvik還是art虛擬機來選擇調用run_dexopt()或run_dex2oat()。

 

 

741        if (strncmp(persist_sys_dalvik_vm_lib, libdvm, 6) == 0) {
742            run_dexopt(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
743        } else if (strncmp(persist_sys_dalvik_vm_lib, libart, 6) == 0) {
744            run_dex2oat(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
745        } else {
746            exit(69);   /* Unexpected persist.sys.dalvik.vm.lib value */
747        }
前者適用於dalvik,將dex優化成odex文件。後者適用於art,將dex直接一步到位編譯成oat文件(也就是可執行代碼)了。由於下層是執行了/system/bin/dexopt或/system/bin/dex2oat文件,dexopt()將之放到子進程去做,自己作為父進程等待它結束。以art為例,/system/bin/dex2oat被調用(實現位於/art/dex2oat/dex2oat.cc)。輸出的elf文件放在/data/dalvik-cache/data@app@@classes.dex。注意對於dalvik和art,這個文件名稱相同但性質截然不同。dalvik下,該文件為優化後的bytecode。而art下,這個就是可執行文件了。

 

如果是更新已有的app,還要讓ActivityManager調用killApplication()把進程殺掉。app都更新了,老的還留內存裡跑,這不合適。

 

4743            killApplication(pkg.applicationInfo.packageName,
4744                        pkg.applicationInfo.uid, update pkg);
之後將安裝的app注冊到PMS的mPackages中,mPackges是個Hash表,保存了從包名到PackageParser.Package的映射。注意Settings裡也有個mPackages,那裡保存的是包名到PackageSetting的映射。前者主要是app配置文件中的信息,而後者是安裝過程中的信息。可以粗略理解為一個是靜態信息,一個是動態信息。
4763            mSettings.insertPackageSettingLPw(pkgSetting, pkg);
	// PackageSetting <= PackageParser.Package
	addPackageSettingLPw(p, pkg.packageName, p.sharedUser)
		mPackages.put(name, p);
4764            // Add the new setting to mPackages
4765            mPackages.put(pkg.applicationInfo.packageName, pkg);
下面把app中的組件信息(content provider, service, receiver, activity)記錄到系統中。另外根據前面app配置文件中的權限信息進行初始化。

 

 

 

4811            int N = pkg.providers.size();
4812            StringBuilder r = null;
4813            int i;
4814            for (i=0; i

 

回到PMS構造函數中。下面就是收尾工作了。主要包括更新共享庫信息,更新權限信息,以及寫回安裝信息。所有包都解析完了,意味著所有共享庫信息都已解析,這兒就可以調用updateAllSharedLibrariesLPw()為那些使用動態庫的app綁定動態庫信息了。下面updatePermissionsLPw()函數用於賦予app相應權限:

 

5365    private void updatePermissionsLPw(String changingPkg,
5366            PackageParser.Package pkgInfo, int flags) {
...
5430        // Now update the permissions for all packages, in particular
5431        // replace the granted permissions of the system packages.
5432        if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
5433            for (PackageParser.Package pkg : mPackages.values()) {
5434                if (pkg != pkgInfo) {
5435                    grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0);
5436                }
5437            }
5438        }
在AndroidManifest.xml中app會申請一些權限,比如讀取位置信息,讀取聯系人,操作攝像頭等等。AndroidManifest.xml中的格式如:

這裡的permission_name被放到requestedPermissions,代表該app申請該權限。經過grantPermissionsLPw()裡判斷能否給予相應權限,如果允許則授予權限(即把權限對應的gid加到app的gid列表中,因為權限在Linux中對應物就是group,由gid表示),並把該權限加到grantedPermissions中,代表已授予該權限。
5445    private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace) {
...
5467        final int N = pkg.requestedPermissions.size();
5468        for (int i=0; i最後,writeLPr()將安裝信息寫回packages.xml文件,這也是一開始readLPw()讀的那個文件。這樣下次啟動時就可以按照這裡邊的信息重新安裝了。
1261    void writeLPr() {
...
1315            serializer.startTag(null, permission-trees);
1316            for (BasePermission bp : mPermissionTrees.values()) {
1317                writePermissionLPr(serializer, bp);
1318            }
1319            serializer.endTag(null, permission-trees);
1320
1321            serializer.startTag(null, permissions);
1322            for (BasePermission bp : mPermissions.values()) {
1323                writePermissionLPr(serializer, bp);
1324            }
1325            serializer.endTag(null, permissions);
1326
1327            for (final PackageSetting pkg : mPackages.values()) {
1328                writePackageLPr(serializer, pkg);
1329            }
總結下幾個主要類的用途:
PackageManagerService: apk包安裝服務
Settings: 管理app的安裝信息
PackageSetting: app的動態安裝信息
SharedUserSetting: 共享Linux用戶
PackageParser.Package: app的靜態配置信息。
Pm: pm命令實現類
Installer: installd daemon代理類
HandlerThread: PMS工作線程
它們之間的大致關系:
\

二、adb install安裝

 

 

通過adb install命令安裝時流程略有不同,主要是scanPackageLI()之前的流程不同。host機上的adb從/system/core/adb/adb.c中的main()開始:main()->adb_commandline()->install_app()->pm_command()->send_shellcommand(),其中會把安裝包從host傳到guest上的臨時目錄。
接下來guest裡的pm命令(/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java)接手,大體流程如下:

 

runInstall()
	installPackageWithVerificationAndEncryption()
	doHandleMessage() // INIT_COPY
	doHandleMessage() // MCS_BOUND
		startCopy()
			InstallParams.handleStartCopy()
				InstallArgs args = createInstallArgs(this)
				args.copyApk() // FileInstallArgs或AsecInstallArgs,取決於是否是forward-lock或裝在sd card上。
					createCopyFile() // 拷貝生成類似於/data/app/vmdl-842267127.tmp這樣的臨時文件,因為這時候包都沒解析,不知道包名。
			handleReturnCode()
				processPendingInstall() //異步方式安裝,因為安裝過程可能較長。
					installPackageLI()
這裡用到了一開始提到的PMS工作線程,doHandleMessage()就是工作線程用於處理丟給它的消息的。

 

\

這裡有幾個設計模式值得學習的。首先,Pm中得到PMS的代理類,然後調用installPackageWithVerificationAndEncryption()進行安裝。

 

90        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService(package));
...
957            mPm.installPackageWithVerificationAndEncryption(apkURI, obs, installFlags,
958                    installerPackageName, verificationParams, encryptionParams);
由於安裝時間一般較長,這裡的obs用了Observer模式來監視安裝完成事件。

 

 

其次,InstallArgs用了Strategy模式,而createInstallArgs()使用了簡單工廠模式。在handleStartCopy()中,只要根據安裝類型生成相應的InstallArgs對象,然後調用統一接口copyApk()等就行了。

\
另外,HandlerParams和其繼承類采用了Template method模式。其中基類中的startCopy()是模板函數,繼承類實現handleStartCopy(),handleServiceError()和handleReturnCode()來完成不同工作。HandlerParams包含了要工作線程做的工作內容,工作線程只要取出HandlerParams對象,調用其startCopy()接口,因此這裡也用了Command模式的思想。

\

這裡的installPackageLI()做了很多前一種安裝方式中scanPackageLI(file, ...)的工作,接著它會根據該app是否是全新安裝調用replacePackageLI()或installNewPackageLI()。

 

9061        if (!args.doRename(res.returnCode, pkgName, oldCodePath)) { // 前面拷貝apk時是隨機取了臨時名字的,這裡用doRename()函數為其“正名”。
...
9068        if (replace) {
9069            replacePackageLI(pkg, parseFlags, scanMode, args.user,
9070                    installerPackageName, res);
9071        } else {
9072            installNewPackageLI(pkg, parseFlags, scanMode, args.user,
9073                    installerPackageName, res);
9074        }
如果是全新的apk,調用installNewPackageLI()進行安裝。它調用scanPackageLI()完成主要安裝工作。
8601    private void installNewPackageLI(PackageParser.Package pkg,
8602            int parseFlags, int scanMode, UserHandle user,
8603            String installerPackageName, PackageInstalledInfo res) {
...
8630        PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
8631                System.currentTimeMillis(), user);
從這開始就熟悉了吧,和啟動時安裝的流程差不多了,最後調用updateSettingsLI()來更新安裝信息。

 

 

三、Google Play網絡下載安裝


Google Play的包名為com.android.vending。由於是閉源的,看不了源碼。不過從反匯編粗略地看,應該是先把apk包下載到:
/data/data/com.android.providers.downloads/cache/downloadfile.apk
然後調用installPackage()安裝,類似於:

PackageManager pm = context.getPackageManager();
pm.installPackage(packageURI, observer, flags, null);
接著就和上面一樣了:installPackage()->installPackageWithVerification()->installPackageWithVerificationAndEncryption()。

 


四、點選apk文件安裝

這種情況下,會通過PackageInstaller安裝app。在/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java中:

 

284            pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
285                    installerPackageName, verificationParams, null);
後面的故事又都熟悉了吧。

 

</frameworkfiles.length;>
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved