PMS(PackageManagerService)原理简单介绍,启动过程源码简单解析

Posted wodongx123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PMS(PackageManagerService)原理简单介绍,启动过程源码简单解析相关的知识,希望对你有一定的参考价值。

文章目录

前言

先想一个最直接的问题:
我们在写android项目的时候,为什么要去写AndroidMenifest.xml这个清单文件?除了IDE强制你写以外,还有没有别的什么理由?

假如我们的项目很大,有上千个类,那么手机系统想要从项目里面遍历把启动类找出来是非常耗时的,所以我们通过将Android一些重要的资源(四大组件,Application还有权限)声明在AndroidManifest.xml里面,这样就省的系统一个个去找这些重要资源的时间。

顺便一提,在Windows或者别的系统里面不存在这样的问题,因为每个应用都会有个很明显的exe文件,只要通过运行exe(或者对应的快捷方式)文件就可以拉起整个应用,而Android系统中不同应用是根据包来区分的,而我们用的时候又没办法像Windows系统一样每个文件夹去翻,所以才会有这样的机制。

1. PMS

刚刚说了AndroidManifest.xml的作用是为了让手机系统明确自己应用中重要资源的位置,接下来就是重点了,具体是让手机系统中的哪个模块明确呢?这里就是我们今天的主角PMS登场了。

PMS(Package Manager Service):用于定位手机系统中所有的APK文件。

这个东西是伴随着Android系统而运行的,也就是说,当你的手机开启的时候,PMS就开始运行了。

工作原理简介
在手机系统开机的时候,PMS就会将你手机里面的存着不所有APP的目录遍历一遍,将其中的apk文件加载到内存里面,并且解析apk里面的androidMenifst.xml,以便于在用户点击应用的时候,快速的根据androidMenifest.xml中声明的内容来快速拉起APP(虽然实际上还是要消耗一定时间)。

todo:APK介绍和两个存放APK的地址

2. 源码和关键方法

PMS启动的关键流程时序图如下,在下面还会贴出关键代码,启动流程涉及到三个重要的类:

  • SystemServer:系统服务类,由zygote进程所启动的一个新的进程,我们常说的PMS,AMS和WMS都在SystemServer里面启动。
  • PackageManagerService:今天的主角,主要功能上文已经说过了。
  • PackageParser:用于解析APK和AndroidManifest.xml的工具类。

从宏观的看完这个服务启动图之后,还需要从具体代码重新入手,把里面的关键方法都再看一遍,顺便整理一下里面的一些细节。

我会只截取有意义的代码部分,并且调整不同方法的顺序,让我们看的时候可以直接从上往下按顺序看。

如果某个方法中,我有进行省略代码的行为,我会注释表明,反之就是没有省略代码。
如果因为省略代码而看到了意义不明的参数,请忽略他,他并不会影响源码的阅读。

SystemServer

public final class SystemServer 
	/**
     * The main entry point from zygote.
     */
    public static void main(String[] args) 
        new SystemServer().run();
    

	private void run() 
		// 省略无关代码
		TimingsTraceAndSlog t = new TimingsTraceAndSlog(); // 记录各种操作的耗时用的,这里不用管
		startBootstrapServices(t); // AMS,PMS等都在这里启动
        startCoreServices(t);
        startOtherServices(t);
	
	
	private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) 
		// 省略无关代码,主要看启动PMS服务的这行
		mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
	

  1. 这是一个有静态main方法的类,这就意味着他可以是一个单独的进程,事实上SystemServer在开机的时候可以单独启动一个进程来操作的。
  2. 可以看到在run方法里面,启动了很多的服务,我们常说的AMS,PMS,WMS等都是在这里启动的(所以这些服务都是系统开机的时候启动的)

PackageManagerService

public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender 
    
	/** 存储已安装应用程序的目录 */
    private static final File sAppInstallDir = new File(Environment.getDataDirectory(), "app");

	/** 在这个静态方法中,会实例化一个新的PMS */
	public static PackageManagerService main(Context context, Installer installer,
	            boolean factoryTest, boolean onlyCore) 
        // 省略无关代码
		PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);
	

	/** PMS的构造方法 */
	public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) 
		// 省略无关代码,顺便一提这个构造方法算上注释有2000+行了
		if (!mOnlyCore) 
			scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,
                        packageParser, executorService);
		
	

	/** 用Trace对初始化的过程做个耗时记录,*/
	private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags,
            long currentTime, PackageParser2 packageParser, ExecutorService executorService) 
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
        try 
            scanDirLI(scanDir, parseFlags, scanFlags, currentTime, packageParser, executorService);
         finally 
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        
    

	/** */
	private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,
            PackageParser2 packageParser, ExecutorService executorService) 
        // 省略无关代码
        final File[] files = scanDir.listFiles(); // 获得所有的文件列表
        ParallelPackageParser parallelPackageParser =new ParallelPackageParser(packageParser, executorService); // 创建一个用于处理apk文件的对象

 		for (File file : files) 
	        final boolean isPackage = (isApkFile(file) || file.isDirectory())
	                && !PackageInstallerService.isStageName(file.getName());
	        if (!isPackage) 
	        	// 不是APK文件就置之不理
	            continue;
	        
	        parallelPackageParser.submit(file, parseFlags); // 解析每个APK文件
    	
    

  1. 可以看到,PMS是用最基本的File类的API,对着整个APP的安装目录扫了一圈,并且将其中的APK文件抓去解析。
  2. 在源码过程中,可以看到多次Trace类的出镜,他的主要作用就是记录某段时间内系统的运行情况,一般用于做性能优化(没有数据的话谷歌工程师也不会知道哪里需要优化,这个就是记数据的)。

ParallelPackageParser

class ParallelPackageParser 
	private static final int QUEUE_CAPACITY = 30;
	// 线程池的最大线程数量
    private static final int MAX_THREADS = 4;

	private final BlockingQueue<ParseResult> mQueue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
	
	/** 线程池,用于处理并发任务 */
	private final ExecutorService mExecutorService;

	private final PackageParser mPackageParser;
	
	/**
     * 提交用于解析的文件
     */
    public void submit(File scanFile, int parseFlags) 
        mExecutorService.submit(() -> 
            ParseResult pr = new ParseResult();
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parallel parsePackage [" + scanFile + "]");
            try 
                pr.scanFile = scanFile;
                pr.parsedPackage = parsePackage(scanFile, parseFlags);  // 解析APK
             catch (Throwable e) 
                pr.throwable = e;
             finally 
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            
            try 
                mQueue.put(pr);
             catch (InterruptedException e) 
                Thread.currentThread().interrupt();
                mInterruptedInThread = Thread.currentThread().getName();
            
        );
    

	 @VisibleForTesting
    protected ParsedPackage parsePackage(File scanFile, int parseFlags)
            throws PackageParser.PackageParserException 
        return mPackageParser.parsePackage(scanFile, parseFlags, true);
    

  1. 他是个多线程任务,通过并行处理来同时对多个APK文件来进行解析
  2. 顺便一提,在android的早期版本6.0,这部分是没有多线程的,是在高版本才改为并行处理。

PackageParser

核心的解析类,内容会比较多

public class PackageParser 

	public Package parsePackage(File packageFile, int flags, boolean useCaches)
	        throws PackageParserException 
	    if (packageFile.isDirectory()) 
	    	// 这次不看文件是目录的情况
	        return parseClusterPackage(packageFile, flags);
	     else 
	        return parseMonolithicPackage(packageFile, flags);
	    
	

	public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException 
		// 省略无关代码
		final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
		return package
	

	private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
            throws PackageParserException 
        // 省略无关代码
        final String apkPath = apkFile.getAbsolutePath();
        XmlResourceParser parser = null;
        // 创建一个用于解析xml文件的实例,就是
        parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); 
        final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);  //解析androidMenifest.xml
        if (pkg == null) 
                throw new PackageParserException(mParseError,
                        apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
            
        return pkg;
    

	private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
            String[] outError) throws XmlPullParserException, IOException 
        // 省略无关代码	
        
        // 这里只是获取APK的版本号等信息
       	TypedArray sa = res.obtainAttributes(parser,
               com.android.internal.R.styleable.AndroidManifest);
        pkg.mVersionCode = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
        pkg.mVersionCodeMajor = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
        pkg.applicationInfo.setVersionCode(pkg.getLongVersionCode());
        pkg.baseRevisionCode = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
        pkg.mVersionName = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifest_versionName, 0);
        if (pkg.mVersionName != null) 
            pkg.mVersionName = pkg.mVersionName.intern();
        

		return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
    

	private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
            XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
            IOException 
        // 省略无关代码

		// 遍历androidManifest中的所有的标签
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) 
        	String tagName = parser.getName();
        	// 标签是application标签的情况
        	if (tagName.equals(TAG_APPLICATION))  
    		    if (!parseBaseApplication(pkg, res, parser, flags, outError)) 
                    return null;
                
            // 标签是permission的情况
        	 else if (tagName.equals(TAG_PERMISSION)) 
                if (!parsePermission(pkg, res, parser, outError)) 
                    return null;
                
            
       	
    

	private boolean parseBaseApplication(Package owner, Resources res,
            XmlResourceParser parser, int flags, String[] outError) 
            throws XmlPullParserException, IOException 

		// 再去找Application标签里面的标签,Activity等标签都在里面
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth))  
            String tagName = parser.getName();
            if (tagName.equals("activity")) 
            	Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
                        owner.baseHardwareAccelerate);
                owner.activities.add(a);
             else if (tagName.equals("receiver") 
            	Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
                        true, false);
                owner.receivers.add(a);
             else if (tagName.equals("service")) 
            	Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
            	owner.services.add(s);
             else if (tagName.equals("provider")) 
            	Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
            	owner.providers.add(p);
            
            
        return true;
    

  1. 基本上就是将xml里面的标签全部保存下来
  2. 要注意的是,这里的Activity,Service和Provider不是真的四大组件,只是一个用于记录组件信息的缩略类。
/** 这个类不仅是个缩略类,也是PackageParser的内部类,主要用于记录组件的信息 */
public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable 
        @UnsupportedAppUsage
        public final ActivityInfo info;
        private boolean mHasMaxAspectRatio;
        private boolean mHasMinAspectRatio;

3. 细节总结

  1. SystemService是一个单独的进程,AMS,PMS等服务都由它管理
  2. PMS用多线程去解析我们的安装文件目录
  3. 解析的过程本质就是将androidMenifest.xml中的标签取下来保存到一个信息类里面,并不会对数据做实际的校验(所以会有可能出现androidMenifest.xml和实际的项目对不上的情况)。

4. 时序图startuml代码

@startuml

participant SystemServer 
participant PackageManagerService
participant ParallelPackageParser
participant PackageParser

SystemServer -> SystemServer : main
SystemServer -> SystemServer : run

activate SystemServer
	SystemServer -> SystemServer : startBootstrapServices
	activate SystemServer
	SystemServer -> PackageManagerService : main
	activate PackageManagerService
	PackageManagerService -> PackageManagerService : new PackageManagerService
	PackageManagerService -> PackageManagerService : ScanDirTracedLI 
	PackageManagerService -> PackageManagerService : ScanDirLI
	loop 遍历文件夹内所有的APK文件
		PackageManagerService --> ParallelPackageParser : submit
		activate ParallelPackageParser
			ParallelPackageParser -> PackageParser : parsePackage
			activate PackageParser
			PackageParser -> PackageParser : parseMonolithicPackage
			PackageParser -> PackageParser : parseBaseApk(这里参数传的是路径)
			PackageParser -> PackageParser : parseBaseApk(这里参数传的是androidMenifest)
			PackageParser -> PackageParser : parseBaseApkCommon(这里解析androidMenifest中定义的各种资源)
			PackageParser -> PackageParser : parseBaseApplication(解析application标签内的子标签)
			alt 标签名为Activity或者receiver
				PackageParser -> PackageParser : parseActivity
			else 标签名为Service
				PackageParser -> PackageParser : parseService
			else 标签名为Provider
				PackageParser -> PackageParser : parseProvider
			end 
		deactivate ParallelPackageParser
	end
	deactivate PackageManagerService
	deactivate SystemServer
deactivate SystemServer

@enduml

参考材料

码牛学院VIP24-2020.8.24-PMS服务启动原理详解,从开机到APP启动PMS服务处理机制与流程-david

以上是关于PMS(PackageManagerService)原理简单介绍,启动过程源码简单解析的主要内容,如果未能解决你的问题,请参考以下文章

PMS(PackageManagerService)原理简单介绍,启动过程源码简单解析

APK安装(一)—— PMS原理分析

PMS启动 APK 安装流程详解

从APK安装过程来认识PMS

从APK安装过程来认识PMS

从APK安装过程来认识PMS