【讀書筆記】 Android 應用程序的安裝和顯示過程

這是羅昇陽的《Android 系統源代碼情景分析》一書中第16章,Android 應用程序的安裝和顯示過程,一章的摘要。


一、應用程序的安裝過程

PackageManagerService 安裝一個應用程序中,主要完成兩件事:

1、解析這個應用程序的配置文件 AndroidManifest.xml , 獲取它的安裝信息;

2、爲這個應用程序分配 Linux 用戶 ID 和用戶組 ID,以便它可以在系統中獲得合適的運行權限。


System 進程啓動時,會調用 PackgeManagerService 類 main 方法,將 PackageManagerService 啓動。

如果是從網絡上下載應用安裝到設備上,系統會通過調用 PackageManager 的 installPackager 方法安裝應用程序。



1、通過對系統中特定目錄掃描,對保存在裏面的應用程序進程安裝

/data/app 目錄是保存由用戶自己安裝的應用程序;

/data/app-private 目錄保存的是受 DRM 保護的私有應用程序;

/system/framework 目錄保存的是應用是資源類型的;

/system/app 目錄是保存系統自帶的應用程序;

/vendor/app 目錄是保存設備廠商提供的應用程序。

通過 scanDirLI(...) 方法


2、Settings.readLPw(...)

Settings 裏面有個重要的成員:

mSettingsFileName:

保存上一次應用程序安裝的信息, 文件地址是 /data/system/packages.xml

mBackupSettingsFileName:

備份packages.xml 文件, 文件地址是 /data/system/packages-backup.xml

// 獲取上一次安裝所分配的 Linux 用戶 ID

readPackageLPw(...)

// 獲取上一次安裝所分配的 Linux 用戶組 ID

readSharedUserLPw(...)

3.Setting.addUserIdLPw(...)

用戶只能用的 ID 範圍在 1000~19999 之間;低於 1000 以下的 ID 用於特權用戶;

    private boolean addUserIdLPw(int uid, Object obj, Object name) {
        if (uid > Process.LAST_APPLICATION_UID) {  // LAST_APPLICATION_UID =  19999
            return false;
        }

        if (uid >= Process.FIRST_APPLICATION_UID) {  // FIRST_APPLICATION_UID =  10000
            int N = mUserIds.size();
            final int index = uid - Process.FIRST_APPLICATION_UID;
            while (index >= N) {
                mUserIds.add(null);  // mUserIds 是 ArrayList
                N++;
            }
            if (mUserIds.get(index) != null) {
                PackageManagerService.reportSettingsProblem(Log.ERROR, "Adding duplicate user id: " + uid + " name=" + name);
                return false;
            }
            mUserIds.set(index, obj);
        } else {  // 特權用戶
            if (mOtherUserIds.get(uid) != null) {
                PackageManagerService.reportSettingsProblem(Log.ERROR, "Adding duplicate shared id: " + uid + " name=" + name);
                return false;
            }
            mOtherUserIds.put(uid, obj);
        }
        return true;
    }

4、PackageParse.parseBaseApplication(...)

解析四大組件之類的主要信息,存放在 Package 類的實例中

    /**
     * Parse the {@code application} XML tree at the current parse location in a
     * <em>base APK</em> manifest.
     * <p>
     * When adding new features, carefully consider if they should also be
     * supported by split APKs.
     */
    private boolean parseBaseApplication(Package owner, Resources res,
                                         XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
            throws XmlPullParserException, IOException {
        final ApplicationInfo ai = owner.applicationInfo;
        final String pkgName = owner.applicationInfo.packageName;

       ...
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            if (tagName.equals("activity")) {
                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
                        owner.baseHardwareAccelerated);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.activities.add(a);

            } else if (tagName.equals("receiver")) {
                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.receivers.add(a);

            } else if (tagName.equals("service")) {
                Service s = parseService(owner, res, parser, attrs, flags, outError);
                if (s == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.services.add(s);

            } else if (tagName.equals("provider")) {
                Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
                if (p == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.providers.add(p);

            } else if (tagName.equals("activity-alias")) {
                Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.activities.add(a);

            } else if (parser.getName().equals("meta-data")) {
                // note: application meta-data is stored off to the side, so it can
                // remain null in the primary copy (we like to avoid extra copies because
                // it can be large)
                if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData, outError)) == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

            } else if (tagName.equals("library")) {
                sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestLibrary);

                // Note: don't allow this value to be a reference to a resource
                // that may change.
                String lname = sa.getNonResourceString(com.android.internal.R.styleable.AndroidManifestLibrary_name);

                sa.recycle();

                if (lname != null) {
                    lname = lname.intern();
                    if (!ArrayUtils.contains(owner.libraryNames, lname)) {
                        owner.libraryNames = ArrayUtils.add(owner.libraryNames, lname);
                    }
                }

                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals("uses-library")) {
                sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesLibrary);

                // Note: don't allow this value to be a reference to a resource
                // that may change.
                String lname = sa.getNonResourceString(com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
                boolean req = sa.getBoolean(com.android.internal.R.styleable.AndroidManifestUsesLibrary_required, true);

                sa.recycle();

                if (lname != null) {
                    lname = lname.intern();
                    if (req) {
                        owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
                    } else {
                        owner.usesOptionalLibraries = ArrayUtils.add(owner.usesOptionalLibraries, lname);
                    }
                }

                XmlUtils.skipCurrentTag(parser);

            } else if (tagName.equals("uses-package")) {
                // Dependencies for app installers; we don't currently try to
                // enforce this.
                XmlUtils.skipCurrentTag(parser);

            } else {
               ...
        }

        return true;
    }

5、PackageManagerService.scanPackageDirtyLI(...)

主要完成六方面的工作:

private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
                                                     int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
        final File scanFile = new File(pkg.codePath);
        ..

        SharedUserSetting suid = null;
        PackageSetting pkgSetting = null;
        ..
        // writer
        synchronized (mPackages) {
            if (pkg.mSharedUserId != null) {
                // 獲取共享  ID
                suid = mSettings.getSharedUserLPw(pkg.mSharedUserId, 0, true);
                if (suid == null) {
                    throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                            "Creating application package " + pkg.packageName
                                    + " for shared user failed");
                }
               ...
            }

            ...

            // Just create the setting, don't add it yet. For already existing packages
            // the PkgSetting exists already and doesn't have to be created.
            pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
                    destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
                    pkg.applicationInfo.primaryCpuAbi,
                    pkg.applicationInfo.secondaryCpuAbi,
                    pkg.applicationInfo.flags, user, false);
            if (pkgSetting == null) {
                throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                        "Creating application package " + pkg.packageName + " failed");
            }
            ....

        // writer
        synchronized (mPackages) {
            // We don't expect installation to fail beyond this point

            // Add the new setting to mSettings
            mSettings.insertPackageSettingLPw(pkgSetting, pkg);
            // Add the new setting to mPackages
            // 將參數 pkg 所指向的一個 Package 對象保存在 mPackages 中
            mPackages.put(pkg.applicationInfo.packageName, pkg);

            int N = pkg.providers.size();
            int i;
            for (i=0; i<N; i++) {
                PackageParser.Provider p = pkg.providers.get(i);
                ...
                // 將 Content Provider 組件保存
                mProviders.addProvider(p);
               ..
            }

            N = pkg.services.size();
            r = null;
            for (i=0; i<N; i++) {
                PackageParser.Service s = pkg.services.get(i);
                ...
                // 將 Services 保存
                mServices.addService(s);
                ..
            }
           ...
            N = pkg.receivers.size();
            r = null;
            for (i=0; i<N; i++) {
                PackageParser.Activity a = pkg.receivers.get(i);
               ...
                // 保存 Receiver
                mReceivers.addActivity(a, "receiver");
                ..
            }
            ...
            N = pkg.activities.size();
            r = null;
            for (i=0; i<N; i++) {
                PackageParser.Activity a = pkg.activities.get(i);
                ..
                // 保存 Activity 
                mActivities.addActivity(a, "activity");
               ...
            }
              ...

           ..
        }

        return pkg;
    }


6、Settings.newUserIdLPw(...)

產生一個新的 Linux 用戶 ID

    // Returns -1 if we could not find an available UserId to assign
    private int newUserIdLPw(Object obj) {
        // Let's be stupidly inefficient for now...
        final int N = mUserIds.size();
        for (int i = mFirstAvailableUid; i < N; i++) {
            if (mUserIds.get(i) == null) {
                mUserIds.set(i, obj);
                return Process.FIRST_APPLICATION_UID + i;
            }
        }

        // None left?
        if (N > (Process.LAST_APPLICATION_UID-Process.FIRST_APPLICATION_UID)) {
            return -1;
        }

        mUserIds.add(obj);
        return Process.FIRST_APPLICATION_UID + N;
    }

7.PackageManageService.updataPermissionLPw(...)

更新應用程序的 Linux 用戶 ID 和 用戶組 ID


8.Settings.writeLPr()

通過 XmlSerializer 將應用信息寫進 /data/system/packges.xml 或者 /data/system/packages-backup.xml 文件中。



二、應用程序的顯示過程

Android 系統提供 Launcher 應用, Launcher 也是個 Activity, 用來顯示系統中已經安裝的應用程序;

Zygote 進程啓動 System 進程,而 System 進程在啓動過程中又會創建 ServerThread 線程來啓動系統中的關鍵服務,ServerThread 線程通過 ActivityManagerService 將 Launcher 啓動。


1、Launcher 的啓動過程

         2、從 Launcher 中啓動應用程序

1. LoaderTask

用來執行加載系統中已經安裝了的應用程序信息的操作;

由於加載系統中已經安裝了的應用程序的信息是一個比較耗時的操作。因此,將 LoaderTask 放到一個後臺線程中執行;

後臺線程是 HandlerThread, 具有消息循環的線程;


2、Launcher 運行時,可能會刪除應用程序。Launcher 需要經常執行加載系統中已經安裝了的應用程序的信息操作,以便可以實時地反映系統

當前已經安裝了的應用程序情況;

爲了避免重複地創建的線程來執行加載系統中已經安裝了的應用程序的信息操作,所以需要 HandlerThread 這樣帶有消息循環的線程。


3、AppsCustomizePageView 裏面使用了 GridView 顯示應用程序的圖標。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章