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);
- 这是一个有静态main方法的类,这就意味着他可以是一个单独的进程,事实上SystemServer在开机的时候可以单独启动一个进程来操作的。
- 可以看到在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文件
- 可以看到,PMS是用最基本的File类的API,对着整个APP的安装目录扫了一圈,并且将其中的APK文件抓去解析。
- 在源码过程中,可以看到多次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);
- 他是个多线程任务,通过并行处理来同时对多个APK文件来进行解析
- 顺便一提,在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;
- 基本上就是将xml里面的标签全部保存下来
- 要注意的是,这里的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. 细节总结
- SystemService是一个单独的进程,AMS,PMS等服务都由它管理
- PMS用多线程去解析我们的安装文件目录
- 解析的过程本质就是将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)原理简单介绍,启动过程源码简单解析的主要内容,如果未能解决你的问题,请参考以下文章