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

<think>好的,我现在需要帮用户深入解析Android系统的启动流程。根据用户的问题,他想要一个详细的流程,包括各个阶段的核心逻辑和可能的伪代码框架。首先,我需要回忆一下Android启动流程的基本步骤,然后结合提供的引用内容来组织回答。 首先,用户提到的引用[1]、[2]、[3]都涉及Android系统启动的不同部分,包括init启动、Zygote启动、SystemServer启动和Launcher启动。我需要确保每个阶段都有清晰的解释,并且可能的话,加入伪代码示例来帮助理解。同时,用户要求使用中文回答,并且数学表达式要用$...$格式,不过这里可能用不上太多数学内容,但需要注意其他格式要求,比如独立公式用$$...$$,不过示例中的代码块用了Python,这里可能需要用类似的方式展示伪代码。 接下来,我需要按照系统启动的顺序来组织内容。通常Android启动流程是:Bootloader -> Kernel -> init进程 -> Zygote -> SystemServer -> Launcher。但用户提到的引用中主要关注的是init、Zygote、SystemServer和Launcher,所以可能不需要深入Bootloader和Kernel部分,除非必要。 首先,init进程是用户空间第一个进程,负责解析init.rc文件,启动守护进程和挂载文件系统。然后Zygote进程负责孵化应用进程,包括系统服务和应用。接着,SystemServer由Zygote启动,负责启动系统核心服务如AMS、PMS等。最后,Launcher作为桌面应用被启动,用户可见。 在解释每个阶段时,需要加入核心逻辑,例如init进程如何解析脚本,Zygote如何预加载资源,SystemServer如何启动服务。伪代码部分可能需要用类似代码块的结构,但不需要实际可运行的代码,而是逻辑上的框架。例如,init进程的伪代码可能包括解析rc文件、启动服务,Zygote的伪代码可能包括循环监听socket请求,fork进程等。 另外,用户提到要生成相关问题,所以在回答最后需要添加相关问题,可能涉及启动优化、各组件作用、与其他系统的对比等。 需要确保引用标记正确,例如在提到init启动时引用[^1],在源码解析部分引用[^2],相关资料引用[^3]。但要注意引用内容是否与描述部分对应,比如引用[1]提到init启动,引用[2]可能涉及源码流程,引用[3]可能提供更详细的资料。 现在需要组织这些内容,确保结构清晰,分阶段说明,每个阶段有核心逻辑和伪代码示例,同时引用正确。可能存在需要验证的地方,比如Zygote如何启动SystemServer,是否正确通过fork和specialize,以及Launcher是如何被启动的,是否是AMS启动的。 最后,检查是否符合格式要求:行内数学表达式(如果有的话)是否正确,独立公式是否单独成段,中文回答,生成相关问题,引用标识自然添加。</think>### Android系统启动流程深入解析 Android系统启动流程是设备从开机到用户界面就绪的核心过程,主要包括**init启动**、**Zygote启动**、**SystemServer启动**和**Launcher启动**四个阶段。以下是每个阶段的核心逻辑和伪代码框架: --- #### 1. **init启动**:用户空间第一个进程 init进程是Linux内核启动后创建的第一个用户空间进程,负责初始化系统环境: - **解析`init.rc`脚本**:定义系统服务、挂载文件系统、设置权限等。 - **启动关键守护进程**:例如`ueventd`(设备节点管理)、`servicemanager`(Binder通信管理)等。 - **触发`early-init`和`late-init`**:分阶段执行初始化动作。 ```plaintext // 伪代码框架示例 int main() { parse_init_rc(); // 解析init.rc start_ueventd(); // 启动设备管理守护进程 mount_filesystems(); // 挂载/system、/data等分区 start_core_services(); // 启动Zygote等关键服务 enter_event_loop(); // 进入事件循环,监听子进程状态 } ``` [^1] --- #### 2. **Zygote启动**:应用进程孵化器 Zygote是所有Android应用进程的父进程,通过预加载资源和类加速应用启动: - **启动Java虚拟机(JVM)**:初始化核心类库和资源。 - **监听Socket请求**:接收启动新应用的指令(例如通过`fork()`创建进程)。 - **启动SystemServer**:孵化第一个子进程`system_server`。 ```plaintext // 伪代码框架示例 void zygote_main() { preload_classes(); // 预加载常用类(如Activity、View) preload_resources(); // 预加载资源(如图标、主题) start_system_server(); // 启动SystemServer run_select_loop(); // 循环监听Socket请求,孵化新进程 } ``` [^2] --- #### 3. **SystemServer启动**:系统服务核心 SystemServer由Zygote孵化,负责启动和管理所有核心系统服务: - **启动`ActivityManagerService`(AMS)**:管理应用生命周期。 - **启动`PackageManagerService`(PMS)**:管理应用安装与权限。 - **启动其他服务**:如`WindowManagerService`、`PowerManagerService`等。 ```plaintext // 伪代码框架示例 void system_server_main() { start_bootstrap_services(); // 启动AMS、PMS等基础服务 start_core_services(); // 启动电池、传感器等服务 start_other_services(); // 启动网络、蓝牙等服务 start_launcher(); // 触发Launcher启动 } ``` [^3] --- #### 4. **Launcher启动**:用户界面入口 Launcher是系统桌面应用,由AMS通过`startHomeActivity()`触发: - **加载已安装应用图标**:通过PMS查询应用列表。 - **绑定桌面布局**:显示主屏幕、快捷方式和小组件。 - **响应交互事件**:用户点击应用图标时启动对应应用。 ```plaintext // 伪代码示例(AMS启动Launcher) void start_home_activity() { Intent intent = new Intent(ACTION_MAIN, CATEGORY_HOME); resolveActivity(intent); // 解析Launcher组件 startActivity(intent); // 启动Launcher进程 } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值