【读书笔记】 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 显示应用程序的图标。




发布了58 篇原创文章 · 获赞 20 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/yxhuang2008/article/details/51783394
今日推荐