源码学习《5》PMS 的启动 和 AndroidManifast.xml 解析流程

本文深入探讨Android应用启动过程,解析Manifast.xml加载与四大组件的创建机制。从PackageManagerService入手,详细介绍系统如何扫描并解析APK文件,以及如何识别与创建Activity、Service等组件。

通过前两篇文章学习我们大致了解了,apk 资源的加载 和 XML 标签创建对象的过程,今天就来学习一下 Android app启动后Manifast中的 标签是怎么被识别的,activity / service等四大组件对象的创建 android 8.0。

  1. Manifast.xml的加载和解析?
  2. Activity 等四大组件的包装对象的创建?

其实Manifast是在PackageManagerService中被解析的,所以一切的开始就要从PackageManagerService下手,首先我们需要了解PackageManagerService创建。我们知道在系统启动之后会fork systemserver 去创建系统服务包括PackageManagerService。systemServer启动之后会调用 启动引导服务 : startBootstrapServices() 方法

 mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);

创建PackageManagerService对象,所以转到 main方法

  public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
      
        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        m.enableSystemUserPackages();
        ServiceManager.addService("package", m);
        
        return m;
    }

可以看到在这里 new 一个 PackageManagerService 对象并且把对象传到 ServiceManager 中去保存,共其他类使用。然后就触发了 PackageManagerService 的构造方法,在构造方法中封装了,系统加载 apk 的路径

            File dataDir = Environment.getDataDirectory();
            mAppInstallDir = new File(dataDir, "app");
            mAppLib32InstallDir = new File(dataDir, "app-lib");
            mAsecInternalPath = new File(dataDir, "app-asec").getPath();
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private");

系统启动的时候就会去 搜索 这几个路径下的文件,查找 apk。

// Collect ordinary system packages.
            final File systemAppDir = new File(Environment.getRootDirectory(), "app");
            scanDirTracedLI(systemAppDir, mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

// Collected privileged system packages.
            final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
            scanDirTracedLI(privilegedAppDir, mDefParseFlags
                    | PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR
                    | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
.............................................

不同路径的apk都会被加载,

 private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
       
        // 创建apk的解析器
        ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
                mParallelPackageParserCallback);

        // Submit files for parsing in parallel
        int fileCount = 0;
        for (File file : files) {
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                // Ignore entries which are not packages
                continue;
            }
            // 把所有 apk 文件传入线程池中,在线程中持行 解析操作
            parallelPackageParser.submit(file, parseFlags);
            fileCount++;
        }

        // Process results one by one
        for (; fileCount > 0; fileCount--) {
            // 阻塞队列等待解析结果
            ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
            
            if (throwable == null) {
               
                try {
                    if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
                        // 持久化apk信息并且安装apk
                        scanPackageLI(parseResult.pkg, parseResult.scanFile, parseFlags, scanFlags,
                                currentTime, null);
                    }
}

在这里把所有apk 文件都传到线程池中解析,并把解析结果放入阻塞队列中保存,等待被一个一个拿出来到scanPackageLI()去持久化apk信息并且安装apk。

public void submit(File scanFile, int parseFlags) {
        mService.submit(() -> {
            ParseResult pr = new ParseResult();
           
            try {
                PackageParser pp = new PackageParser();
                pp.setSeparateProcesses(mSeparateProcesses);
                pp.setOnlyCoreApps(mOnlyCore);
                pp.setDisplayMetrics(mMetrics);
                pp.setCacheDir(mCacheDir);
                pp.setCallback(mPackageParserCallback);
                pr.scanFile = scanFile;
                // 解析 xml tag
                pr.pkg = parsePackage(pp, scanFile, parseFlags);
            } catch (Throwable e) {
                pr.throwable = e;
            }
            try {
                // 把结果 放到阻塞队列中
                mQueue.put(pr);
            } catch (InterruptedException e) {
            }
        });
    }

这里解析 apk 信息到package中,最终会调到

 private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
            throws PackageParserException {
        final String apkPath = apkFile.getAbsolutePath();

        mParseError = PackageManager.INSTALL_SUCCEEDED;
        mArchiveSourcePath = apkFile.getAbsolutePath();

        final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);

        Resources res = null;
        XmlResourceParser parser = null;
        try {
            res = new Resources(assets, mMetrics, null);
            // 解析 Manifast.xml 文件
            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

            final String[] outError = new String[1];
            // 封装 package 包
            final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
            
            }

            return pkg;
    }

最终走到 parseBaseApkCommon () 方法中解析 application / permission 等属性

 // 是 当前 application 标签
if (tagName.equals(TAG_APPLICATION)) {
                if (foundApp) {
                    if (RIGID_PARSER) {
                        outError[0] = "<manifest> has more than one <application>";
                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                        return null;
                    } else {
                        Slog.w(TAG, "<manifest> has more than one <application>");
                        XmlUtils.skipCurrentTag(parser);
                        continue;
                    }
                }

                foundApp = true;
                // 解析 application 内部的标签,例如 activity等四大组件
                if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
                    return null;
                }

在这里解析application 和 其中的 activity 等四大组件。其中在parseBaseApplication()中创建activity对象

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

                owner.activities.add(a);

            } else if (tagName.equals("receiver")) {

            } else if (tagName.equals("service")) {

            } else if (tagName.equals("provider")) {

在这里我们看到了 activity 对象,这个acitvity不是我们start 的activity,这是个activity info 信息,保存在manifast中获取出来的activity的配置信息。

  Activity a = new Activity(cachedArgs.mActivityArgs, new ActivityInfo());
        if (outError[0] != null) {
            sa.recycle();
            return null;
        }

 

### AndroidManifest.xml 解析信息存储位置 在Android系统中,`AndroidManifest.xml`文件的解析信息主要由系统的包管理服务(Package Manager Service, PMS)处理并保存。当安装一个新的应用程序PMS会读取该应用的`AndroidManifest.xml`文件,并将其内容转换成二进制形式,以便更高效地访问查询这些信息。 #### 1. 编译期编译为Binary XML 为了提高效率,在构建过程中,`AndroidManifest.xml`会被AAPT工具(Android Asset Packaging Tool)转化为一种紧凑高效的二进制XML格式。这种格式不仅减小了文件大小,还加快了解析速度[^2]。 #### 2. 安装期间缓存至Package Data Directory 在应用程序首次被安装到设备上之后,其清单文件的信息会被提取出来并通过PMS进一步加工后存储于特定的数据目录下(`/data/system/packages.xml`)。这里记录了所有已安装的应用程序的相关配置细节以及权限声明等重要参数[^3]。 #### 3. 运行期间内存映射 每当启动某个组件(如Activity、Service)之前,PMS都会先检查对应的类是否已经在运行环境中加载过;如果没有,则重新从磁盘上的二进制版本中获取必要的定义,并创建相应的对象实例。此过程涉及到将部分或全部的`AndroidManifest.xml`数据映射到进程地址空间内,从而允许快速查找所需的元数据[^4]。 ```java // 获取当前上下文中指定名称的ComponentName Intent intent = new Intent(); intent.setClassName("com.example.app", "com.example.app.MainActivity"); ComponentName componentName = intent.getComponent(); // 使用PackageManager来检索有关给定组件的具体信息 PackageManager packageManager = context.getPackageManager(); ActivityInfo activityInfo = packageManager.getActivityInfo(componentName, PackageManager.GET_META_DATA); ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WangRain1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值