Android启动流程分析(7)-apk扫描
2024-04-09 20:10:18  阅读数 1134
前言

前面已经从开机启动聊到了跟应用相关的system_server进程和zygote进程的启动流程,也知道了在开启一个应用进程的时候怎么通过socket通信让zygote孵化应用进程和应用进程执行ActivityThread.main()方法后,也初步分析了startActivity方法后到执行Activity的onCreate()的过程

我们都知道Android的安装文件.apk就是一个各种资源的压缩文件,我们在开发中使用Activity组件是必须得在manifest清单文件中注册对应的组件信息,那么PKMS(PackageManagerService)就是去解析对应的manifest清单文件,对apk中的四大组件信息提前做一个缓存,这个过程是发生在system_server进程的过程中,也就是解析应用的信息发生在开机的过程中,这样做也是为了提高后面应用和页面的启动速度

马上进入具体的流程源码分析,想象一下,如果不提前解析apk文件里面的manifest信息,那么在每次打开一个页面或者启动一个服务的时候,都需要经历apk的解压,manifest文件的解析遍历,无疑是一件非常重复的且耗时的事情

启动入口

在system_server启动引导服务的流程中,就会去启动pkms服务

//SystemServer.java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
   try {
            mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        } finally {
      }
}

//PackageManagerService.java
public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
  
            //调用PKMS的构造方法
                PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);
  
            //将PKMS服务注册到servicemanger中,并且区分有native服务
            ServiceManager.addService("package", m);
        final PackageManagerNative pmn = m.new PackageManagerNative();
        ServiceManager.addService("package_native", pmn);
        return m;
}

可以看出pkms在创建完成后,会向servicemanager中注册一个名为“package”的服务,我们在应用开发中,在代码中会遇到想要获取到当前应用版本号的功能,就可以通过pkms去获取

//举个栗子
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //调用getPackageManger可以获取系统提供的pkms服务
        val version = packageManager.getPackageInfo(packageName,0).versionName
    }
}

//ContextImpl.java
    @Override
    public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }
                //调用到ActivityThread的getPackageManager
        final IPackageManager pm = ActivityThread.getPackageManager();
        final IPermissionManager permissionManager = ActivityThread.getPermissionManager();
        if (pm != null && permissionManager != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm, permissionManager));
        }

        return null;
    }

//ActivityThread.java
    @UnsupportedAppUsage
    public static IPackageManager getPackageManager() {
        if (sPackageManager != null) {
            return sPackageManager;
        }
        //可以看到,实际获取的pkms服务就是之前注册在servicemanger中名字为“package”的服务的binder对象
        final IBinder b = ServiceManager.getService("package");
        //通过asInterface可以看出返回的对象是一个binder通信中的Proxy代理对象
        sPackageManager = IPackageManager.Stub.asInterface(b);
        return sPackageManager;
    }

结合了获取代码,可以看出平时在应用开发中使用的packagemanger就是今天分析并被注册在servicemanager中的PMKS

PKMS构造函数

PKMS最主要的业务功能就集中在构造函数中,而整个构造函数有1000多行的代码,涉及的内容很多,包括系统分辨率信息,installer安装对象引用,权限管理,系统应用和用户应用的扫描解析,dex文件优化等很多东西

PKMS对应用开发来说,粗略的定义就是一个管理服务,负责安装应用,卸载应用,查询应用相关信息的服务,接下来就对跟应用信息相关的部分进行一下分析

//PackageMnagerService.java
//Injector从名字可以看出是用了依赖注入的方式,将PKMS中需要使用的类的依赖进来
//onlyCore参数是否加载核心服务 factoryTest一般是false,非工厂模式,代码中都有赋值判断
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
        mContext = injector.getContext();
        mFactoryTest = factoryTest;
        mOnlyCore = onlyCore;
                //分辨率的配置
        mMetrics = new DisplayMetrics(); 
            //installer对象
        mInstaller = injector.getInstaller();
            //权限管理服务
        mPermissionManager = injector.getPermissionManagerServiceInternal();
            //setting对象
        mSettings = injector.getSettings();
            //向mSettings添加了一些列的shareUserId
        mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
                //...类似的会添加system/phone/log/nfc/bluetooth/shell/se/network等8种shareUserId
  
            //构造PackageDexOptimizer,用来对dex文件进行优化
        mPackageDexOptimizer = new PackageDexOptimizer(mInstaller, mInstallLock, mContext,
                "*dexopt*");
        mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, mInstaller, mInstallLock);
            //ART虚拟机管理服务
        mArtManagerService = new ArtManagerService(mContext, this, mInstaller, mInstallLock);
            //获取默认的分辨率
        getDefaultDisplayMetrics(mInjector.getDisplayManager(), mMetrics);
        //SystemConfig对象的获取
            SystemConfig systemConfig = SystemConfig.getInstance();
  
            //用于后续扫描系统应用的文件路径
        mDirsToScanAsSystem = new ArrayList<>();
        mDirsToScanAsSystem.addAll(SYSTEM_PARTITIONS);
        mDirsToScanAsSystem.addAll(scanPartitions);
        
            synchronized (mInstallLock) {
        // writer
        synchronized (mLock) {
            //创建handler线程,用来处理安装应用的相关消息
            mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true);
            mHandlerThread.start();
          
            //读取并解析/data/system路径下的相关文件
            mFirstBoot = !mSettings.readLPw(mInjector.getUserManagerInternal().getUsers(
                    /* excludePartial= */ true,
                    /* excludeDying= */ false,
                    /* excludePreCreated= */ false));
            //清理一些不存在路径的软件包
            final int packageSettingCount = mSettings.mPackages.size();
            for (int i = packageSettingCount - 1; i >= 0; i--) {
                PackageSetting ps = mSettings.mPackages.valueAt(i);
                if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())
                        && mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
                    mSettings.mPackages.removeAt(i);
                    mSettings.enableSystemPackageLPw(ps.name);
                }
            }
                        //在不是第一次启动的情况下,拷贝预编译的DEX文件
            if (!mOnlyCore && mFirstBoot) {
                requestCopyPreoptedFiles();
            }
          
            //准备解析缓存的package
            mCacheDir = preparePackageParserCache();
            //设置标志位,让扫描安装的时候不去更改文件路径
            int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
            //包解析器,随后的扫描解析需要用到
            PackageParser2 packageParser = new PackageParser2(mSeparateProcesses, mOnlyCore,
mMetrics, mCacheDir, mPackageParserCallback);
          
            for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
                final ScanPartition partition = mDirsToScanAsSystem.get(i);
                if (partition.getOverlayFolder() == null) {
                    continue;
                }
                scanDirTracedLI(partition.getOverlayFolder(), systemParseFlags,
                        systemScanFlags | partition.scanFlag, 0,
                        packageParser, executorService);
            }
          
            scanDirTracedLI(frameworkDir, systemParseFlags,
                    systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
                    packageParser, executorService);
          
            for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
                final ScanPartition partition = mDirsToScanAsSystem.get(i);
                if (partition.getPrivAppFolder() != null) {
                    scanDirTracedLI(partition.getPrivAppFolder(), systemParseFlags,
                            systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
                            packageParser, executorService);
                }
                scanDirTracedLI(partition.getAppFolder(), systemParseFlags,
                        systemScanFlags | partition.scanFlag, 0,
                        packageParser, executorService);
            }
          
            //除了系统的文件路径,还扫描了用户安装的apk文件路径
//private static final File sAppInstallDir =new File(Environment.getDataDirectory(), "app");
            if (!mOnlyCore) {
                scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
                        packageParser, executorService);
            }
            }
        }
}

//调用scanDirTracedLI方法中,在不断的循环去扫描,这部分就是不同的文件路径,从上面的赋值可以发现是SYSTEM_PARTITIONS这个参数,而这个参数就是不同的apk的路径
public class PackagePartitions {
      private static final ArrayList<SystemPartition> SYSTEM_PARTITIONS =
            new ArrayList<>(Arrays.asList(
                    new SystemPartition(Environment.getRootDirectory(), PARTITION_SYSTEM,
                            true /* containsPrivApp */, false /* containsOverlay */),
                    new SystemPartition(Environment.getVendorDirectory(), PARTITION_VENDOR,
                            true /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getOdmDirectory(), PARTITION_ODM,
                            true /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getOemDirectory(), PARTITION_OEM,
                            false /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getProductDirectory(), PARTITION_PRODUCT,
                            true /* containsPrivApp */, true /* containsOverlay */),
                    new SystemPartition(Environment.getSystemExtDirectory(), PARTITION_SYSTEM_EXT,
                            true /* containsPrivApp */, true /* containsOverlay */)));
}

//相关的Environment中的定义
    private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
    private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
    private static final File DIR_ANDROID_EXPAND = getDirectory(ENV_ANDROID_EXPAND, "/mnt/expand");
    private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage");
    private static final File DIR_DOWNLOAD_CACHE = getDirectory(ENV_DOWNLOAD_CACHE, "/cache");
    private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
    private static final File DIR_ODM_ROOT = getDirectory(ENV_ODM_ROOT, "/odm");
    private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
    private static final File DIR_PRODUCT_ROOT = getDirectory(ENV_PRODUCT_ROOT, "/product");
    private static final File DIR_SYSTEM_EXT_ROOT = getDirectory(ENV_SYSTEM_EXT_ROOT,
            "/system_ext");
    private static final File DIR_APEX_ROOT = getDirectory(ENV_APEX_ROOT,
            "/apex");
扫描路径下是系统的相关apk文件.png

整个PKMS的构造函数的内容非常多,摘取了一些相关对象的赋值,比如安装器对象,权限管理对象,dex优化等,本文主要分析我们的apk文件的相关解析,其他的仅仅在这段展示一下,毕竟这玩意1000多行的构造函数代码,内容太多了

可以看到扫描文件路径的入口函数是一样的,就是传入了不同的文件路径,类似的有

/system/priv-app、/system/app、/vendor/priv- app、/vendor/app、/odm/priv-app、/odm/app、/oem/app、/oem/priv-app等不同的路径,而在手机文件结构中,这些路径下就是各个不同的apk文件

而且还扫描了/data/app文件路径,而这个路径就是用来保存平时我们开发的应用的apk文件,PKMS会管理应用的的安装,而安装的实际上就是将需要安装的apk文件拷贝到/data/app路径下,并且已base.apk重新命名安装的文件,这部分后面分析PKMS应用的安装会去分析

data-app目录下就是用户安装的baseApk文件.png

安装应用后卸载了安装包,可以从/data/app目录文件中找到对应应用的apk安装包
那么就直接进入到具体的扫描部分

//PackageManagerService.java
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
            long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
        try {
          //继续调用
            scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
        }
}
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
            PackageParser2 packageParser, ExecutorService executorService) {
        
        //ParallelPackageParser对象是一个队列,里面有我们手机里的所有系统apk
      ParallelPackageParser parallelPackageParser =
             new ParallelPackageParser(packageParser, executorService);
  
        int fileCount = 0;
        for (File file : files) {
            //是APK文件或者是目录,用来过滤非apk文件
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                continue;
            }
            //将对应的apk文件保存在parallelPackageParser中
            parallelPackageParser.submit(file, parseFlags);
            fileCount++;
        }
  
                //上面变量保存了apk文件,这会循环处理每一个apk文件
          for (; fileCount > 0; fileCount--) {
            //先从ParallelPackageParser的队列中取出apk
            ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
            Throwable throwable = parseResult.throwable;
            int errorCode = PackageManager.INSTALL_SUCCEEDED;

            if (throwable == null) {
                try {
                    //继续向后执行
                    addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
                            currentTime, null);
                } catch (PackageManagerException e) {}
            } else {}

            if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0 && errorCode != INSTALL_SUCCEEDED) {
                mApexManager.reportErrorWithApkInApex(scanDir.getAbsolutePath());
            }

            // 如果是非系统文件,并且解析失败,就会被清理删除
            if ((scanFlags & SCAN_AS_SYSTEM) == 0
                    && errorCode != PackageManager.INSTALL_SUCCEEDED) {
                removeCodePathLI(parseResult.scanFile);
            }
        }
}

//追踪文件被添加到parallelPackageParser数组中的过程
//ParallelPackageParser.java
//Android源码中很多这样命名的对象,这个对象是在构造函数中传入的
private final PackageParser2 mPackageParser;

public void submit(File scanFile, int parseFlags) {
    mExecutorService.submit(() -> {
        //保存解析结果的包装对象
        ParseResult pr = new ParseResult();
        try {
            pr.scanFile = scanFile;
            //继续调用
            pr.parsedPackage = parsePackage(scanFile, parseFlags);
        }
        try {
            //将扫描路径中的文件放入队列中
            mQueue.put(pr);
        }
    });
}

//PackageParser2的解析方法
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
        throws PackageParser.PackageParserException {
    return mPackageParser.parsePackage(scanFile, parseFlags, true);
}

//PackageParser2.java
//之前的Android版本,解析后的数据对象是Package,Android11源码中是ParsedPackage
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
        throws PackageParserException {
    //缓存安装的解析结果
    if (useCaches && mCacher != null) {
        ParsedPackage parsed = mCacher.getCachedResult(packageFile, flags);
        if (parsed != null) {
            return parsed;
        }
    }

    ParseInput input = mSharedResult.get().reset();
    //将解析方法封装到了ParsingPackageUtils工具类中
    ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
    //取解析结果
    ParsedPackage parsed = (ParsedPackage) result.getResult().hideAsParsed();
    //更新缓存
    if (mCacher != null) {
        mCacher.cacheResult(packageFile, flags, parsed);
    }
    return parsed;
}

//ParsingPackageUtils.java
//这一部分的逻辑就跟之前版本源码的逻辑接上了
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,
        int flags)
        throws PackageParserException {
    if (packageFile.isDirectory()) {
      //如果传入的文件是个目录,执行这个方法
        return parseClusterPackage(input, packageFile, flags);
    } else {
      //如果不是目录就是apk文件,就调用这个方法
        return parseMonolithicPackage(input, packageFile, flags);
    }
}

//解析给定的apk文件
private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
        int flags) throws PackageParserException {
    final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
  
    try {
      //重要看到方法名中有apk字符的命名了,对应用的解析的关键要到了
        ParseResult<ParsingPackage> result = parseBaseApk(input,
                apkFile,
                apkFile.getCanonicalPath(),
                assetLoader.getBaseAssetManager(), flags);
      
        return input.success(result.getResult()
                .setUse32BitAbi(lite.use32bitAbi));
    }
}

经过一系列的方法调用,将存放apk文件的或者目录进行传递,找到了用来解析apk的关键方法,而用户安装的apk文件为什么在/data/app路径下,就是PKMS中对于应用安装部分的逻辑,不在这部分分析中

可以看出由于需要扫描的apk路径比较多,而且随着用户安装的应用数量级的上升,每一个文件都需要执行apk的解析,所以Android手机开机的耗时有很大一部分都集中在这里,接下来就看一下具体的解析过程,就能帮助理解到为什么顶着开机慢的体验也要去做提前解析加载工作了

//ParsingPackageUtils.java
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
        String codePath, AssetManager assets, int flags) {
    //apk文件的路径
    final String apkPath = apkFile.getAbsolutePath();
  
    //这部分处理的是如果apk文件在/mnt/expand/目录下的情况,会截取路径上的UUID设在给pkg做标识
    String volumeUuid = null;
    if (apkPath.startsWith(PackageParser.MNT_EXPAND)) {
        final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length());
        volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end);
    }


    //这部分是去获取一个XML资源解析对象,我们知道manifest文件就是一个.xml格式的文件
    try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
            PackageParser.ANDROID_MANIFEST_FILENAME)) {
        final Resources res = new Resources(assets, mDisplayMetrics, null);
                //继续调用parseBaseApk的重载方法
        ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
                parser, flags);
                
        final ParsingPackage pkg = result.getResult();
        
        pkg.setVolumeUuid(volumeUuid);
        return input.success(pkg);
    }
}

这个方法处理了如果文路径在/mnt/expand/,这种外部sd卡的加密存储方式,现在相对较少了,毕竟机器基本都是128g起步了,应用数据大小远大于应用包占的大小了

//ParsingPackageUtils.java
//继续调用的重载方法
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,
        String codePath, Resources res, XmlResourceParser parser, int flags)
        throws XmlPullParserException, IOException, PackageParserException {
    //拿到AndroidManifest.xml资源
    final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
    try {
        //进一步解析manifest文件
        final ParseResult<ParsingPackage> result =
                parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
        return input.success(pkg);
    } finally {
        manifestArray.recycle();
    }
}

//通过xml中定义的标签去解析
private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,
        TypedArray sa, Resources res, XmlResourceParser parser, int flags)
        throws XmlPullParserException, IOException {
    boolean foundApp = false;
    final int depth = parser.getDepth();
    int type;
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG
            || parser.getDepth() > depth)) {
        if (type != XmlPullParser.START_TAG) {
            continue;
        }
                //从manifest中拿到标签名
        String tagName = parser.getName();
        final ParseResult result;

        if (PackageParser.TAG_APPLICATION.equals(tagName)) {
          //如果从manifest中读取的tag是“application”
          //这部分也是应用开发中配置<activity>等标签的父节点,更应用相关的基本都写在<application>标签中
            if (foundApp) {
              //默认值是false
            } else {
                foundApp = true;
                //执行application标签节点的解析,内含Android的四大组件
                result = parseBaseApplication(input, pkg, res, parser, flags);
            }
        } else {
            //如果不是application标签,那么就可能是基础的manifest标签,permission标签等
            result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);
        }
    }
    return input.success(pkg);
}

AndroidManifest.xml文件中定义的标签可能会包含application,permission,uses-permission等,而开发中定义的四大组件的注册都是写在application标签下,那么继续追下去

//ParsingPackageUtils.java
private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
        ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
        throws XmlPullParserException, IOException {
    final String pkgName = pkg.getPackageName();
    int targetSdk = pkg.getTargetSdkVersion();
    TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
    //...
  
  
    boolean hasActivityOrder = false;
    boolean hasReceiverOrder = false;
    boolean hasServiceOrder = false;
    final int depth = parser.getDepth();
    int type;
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG
            || parser.getDepth() > depth)) {
        if (type != XmlPullParser.START_TAG) {
            continue;
        }

        final ParseResult result;
        //获取“application”标签下的子标签名,并将解析结果保存到ParsingPackage中
        String tagName = parser.getName();
        boolean isActivity = false;
        switch (tagName) {
            //activity标签的解析跟receiver走的相同的流程
            case "activity":
                isActivity = true;
            case "receiver":
                ParseResult<ParsedActivity> activityResult =
                        ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
                                res, parser, flags, PackageParser.sUseRoundIcon, input);

                if (activityResult.isSuccess()) {
                    ParsedActivity activity = activityResult.getResult();
                    if (isActivity) {
                        hasActivityOrder |= (activity.getOrder() != 0);
                        pkg.addActivity(activity);
                    } else {
                        hasReceiverOrder |= (activity.getOrder() != 0);
                        pkg.addReceiver(activity);
                    }
                }

                result = activityResult;
                break;
            case "service":
                ParseResult<ParsedService> serviceResult =ParsedServiceUtils.parseService();
                                pkg.addService(service);
                    break;
            case "provider":
           ParseResult<ParsedProvider> providerResult =ParsedProviderUtils.parseProvider();
                 pkg.addProvider(providerResult.getResult());
                break;
            case "activity-alias":
                activityResult = ParsedActivityUtils.parseActivityAlias();
                pkg.addActivity(activity);
                break;
            default:
                result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
                break;
        }

    }
}

这里就看到了我们应用开发中常见的四大组件的注册的解析,在手机开机的过程中,就会对手机中安装的应用进行扫描,并解析每一个apk文件中的AndroidManifest.xml文件,得到这个清单文件中的标签内容,并且将标签内容都添加到ParsingPackage对象中,不全部展开分析,看一下activity相关的解析

既然最终数据持有的对象是ParsingPackage,先看一下对应的数据结构,省略一些列传参的构造过程的追踪,最终的实现类是ParsingPackageImpl.java

//manifest解析后,用来保存四大组件数据引用的包装对象
public class ParsingPackageImpl implements ParsingPackage, Parcelable {
    protected List<String> protectedBroadcasts = emptyList();
    protected List<ParsedActivity> activities = emptyList();
    protected List<ParsedActivity> receivers = emptyList();
    protected List<ParsedService> services = emptyList();
    protected List<ParsedProvider> providers = emptyList();
    private List<ParsedAttribution> attributions = emptyList();
    protected List<ParsedPermission> permissions = emptyList();
    protected List<ParsedPermissionGroup> permissionGroups = emptyList();
}

记得之前的Android源码中,将activity标签解析后的数据类命名也是Activity,现在的版本命名更加的好理解了,从这个包装类可以看出,一个apk文件解析后,会对四大组件的标签等xml内的数据处理成数据类,activity和receiver是同一个数据类

//ParsedAcivityUtils.java
//继续回到activity标签的解析部分
public static ParseResult<ParsedActivity> parseActivityOrReceiver(String[] separateProcesses,
        ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags,
        boolean useRoundIcon, ParseInput input){
    final String packageName = pkg.getPackageName();
    //解析后的Activity标签的数据类
    final ParsedActivity
            activity = new ParsedActivity();
    //跟receiver的区分标志
    boolean receiver = "receiver".equals(parser.getName());
    String tag = "<" + parser.getName() + ">";
    TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
    try {
        activity.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
        activity.flags |= //一套位运算获取flag操作...;

        if (!receiver) {
            activity.flags |= //...;
            activity.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode, ActivityInfo.COLOR_MODE_DEFAULT);
            activity.documentLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_documentLaunchMode, ActivityInfo.DOCUMENT_LAUNCH_NONE);
            //启动模式SINGLE_TASK,SINGLE_TOP等
            activity.launchMode = sa.getInt(R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE);
                        //页面横竖屏配置
            int screenOrientation = sa.getInt(R.styleable.AndroidManifestActivity_screenOrientation, SCREEN_ORIENTATION_UNSPECIFIED);
            int resizeMode = getActivityResizeMode(pkg, sa, screenOrientation);
            activity.screenOrientation = screenOrientation;
            activity.resizeMode = resizeMode;
        } else {
            activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
            activity.configChanges = 0;
            activity.flags |= flag(ActivityInfo.FLAG_SINGLE_USER, R.styleable.AndroidManifestActivity_singleUser, sa);
        }
        //task任务栈栈名
        String taskAffinity = sa.getNonConfigurationString(
                R.styleable.AndroidManifestActivity_taskAffinity,
                Configuration.NATIVE_CONFIG_VERSION);
        activity.taskAffinity = affinityNameResult.getResult();

        boolean visibleToEphemeral = sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
        if (visibleToEphemeral) {
            activity.flags |= ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP;
            pkg.setVisibleToInstantApps(true);
        }
                
        //activity还有子标签,继续调用去解析子标签
        return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
                false /*isAlias*/, visibleToEphemeral, input,
                R.styleable.AndroidManifestActivity_parentActivityName,
                R.styleable.AndroidManifestActivity_permission,
                R.styleable.AndroidManifestActivity_exported
        );
    } finally {
        sa.recycle();
    }
}

//继续处理,找到activity设置的inten-filter属性的解析
private static ParseResult<ParsedActivity> parseActivityOrAlias(){
    String parentActivityName = array.getNonConfigurationString(parentActivityNameAttr, Configuration.NATIVE_CONFIG_VERSION);
    final int depth = parser.getDepth();
    int type;
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG
            || parser.getDepth() > depth)) {
        if (type != XmlPullParser.START_TAG) {
            continue;
        }

        final ParseResult result;
        //这个位置开始解析了activity的intent-filter标签,并且以ParsedIntentInfo的数据类保存在activity中
        if (parser.getName().equals("intent-filter")) {
            ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
                    !isReceiver, visibleToEphemeral, resources, parser, input);
            if (intentResult.isSuccess()) {
                ParsedIntentInfo intent = intentResult.getResult();
                if (intent != null) {
                    activity.order = Math.max(intent.getOrder(), activity.order);
                    activity.addIntent(intent);
                }
            }
            result = intentResult;
        } 
    }
}

到这一步,PKMS对apk文件进行扫描的流程差不多走了一遍,对整体流程有了一个认知,也知道了为什么应用开发的时候,在写Activity页面的时候需要在manifest文件中进行注册,如果不注册,pkms就没有对应的activity的信息,也就不能进行下一步的通过类名反射实例化Activity对象,和具体的activity启动的参数的判断。

梳理一下大概流程

1->PKMS构造函数中调用扫描文件目录的方法scanDirLI()

2->判断文件是否是apk,不是apk会继续往下寻找,最终找到apk文件

3->使用XML解析器,对apk文件中的manifest文件进行解析,并将其中的不同标签用不同的数据类进行封装,最终保存在一个ParsingPackage对象中

4->每一个apk就会对应一个ParsingPackage数据对象,统一由PKMS管理,在后续应用启动的时候,就会和PKMS通信去拿取对应的activity信息

总结

PKMS构造函数的代码非常的庞大,看源码也很费劲,跟着源码走一遍可以提高一下自己阅读源码找关键方法步骤的方法,如果有时间大家都可以跟着源码看一下

PKMS在我看来主要做的就是APK的扫描解析拿到不同应用的信息,然后对应用信息进行管理,然后还管理了应用的安装和卸载等工作,安装过程就能解答为什么data/app文件夹中的apk文件的命名统一是bask.apk,PKMS更像是一个的应用数据的管家,如果有需要去获取应用的各个信息,可以从getPackageManager中去查看有没有相关的方法暴露

查看源码,也可以反向的去全面的知道Activity可以支持的属性标签有哪一些,毕竟从数据类的属性列表可以很直观的看出来