日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

PMS解析AndroidManifest.xml文件的过程

發布時間:2023/12/20 Android 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PMS解析AndroidManifest.xml文件的过程 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、前言

???????前段時間在看當下主流的安卓插件化技術原理的時候,發現目前插件化技術對于四大組件的處理基本都是通過代理來實現的(動態注冊的廣播接收器除外)。簡單說就是需要先在主包里預埋一個注冊在AndroidManifest.xml中的組件,再由這個組件來連接SystemServer和插件中的業務邏輯。

???????這里難免就會有一些疑問,系統從AndroidManifest.xml文件解析出來的四大組件的信息存放在哪里?能否不利用預埋+代理的技術,直接通過修改中間的存儲容器來實現四大組件的插件化?
???????要想解答這些疑惑,我們就得知道系統是如何從AndroidManifest.xml中解析出四大組件的?以及解析后的組件信息又是如何保存的?

???????系統中負責解析AndroidManifest.xml文件的系統服務是PackageManagerService(以下簡稱PMS)。PMS中關于AndroidManifest.xml文件的處理邏輯入口主要有兩處,一個是SystemServer進程啟動時,另一個則是應用安裝/更新時。兩個入口對于xml的解析流程大體上是一致的,本篇文章只分析SystemServer進程啟動時,PMS對AndroidManifest.xml的解析流程。

二、相關知識

2.1 方法后綴LI、LIF、LPr、LPw的含義

??????PMS中有很多方法的名字后綴都是LI/LIF/LPr/LPw,因此我們需要先簡單了解下這些后綴的含義是什么。這些后綴涉及到PMS兩個很重要的鎖mPackages和mInstallLock以及一個mFrozenPackages變量。相關后綴的含義見下表。詳見:https://blog.csdn.net/u013553529/article/details/61962439。

三、調用流程

SystemServer的啟動屬于開機流程中的一步,入口如下:

  • SystemServer.main()
/*** The main entry point from zygote.* zygote進程會調用該方法*/public static void main(String[] args) {new SystemServer().run();}

??????main()方法中new了一個SystemServer對象,并調用了它的run()方法

  • SystemServer.run()
private void run() {try {// 省略部分代碼,只列出關鍵部分的代碼// The system server should never make non-oneway callsBinder.setWarnOnBlocking(true);// Initialize native services.System.loadLibrary("android_servers");// Create the system service manager.// SystemServiceManager是整個Service的大管家mSystemServiceManager = new SystemServiceManager(mSystemContext);mSystemServiceManager.setStartInfo(mRuntimeRestart,mRuntimeStartElapsedTime, mRuntimeStartUptime);LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);} finally {t.traceEnd(); // InitBeforeStartServices}// Start services.try {t.traceBegin("StartServices");// start critical services, include PMS/AMSstartBootstrapServices(t);// start essential servicesstartCoreServices(t);// start other servicestartOtherServices(t);} catch (Throwable ex) {Slog.e("System", "******************************************");Slog.e("System", "************ Failure starting system services", ex);throw ex;} finally {t.traceEnd(); // StartServices}}

??????run()方法中主要做了一些系統啟動時的初始化工作,其中很關鍵的一步就是啟動各項service服務。PMS就是在startBootstrapServices()中初始化的。

  • SystemServer.startBootstrapServices(@NonNull TimingsTraceAndSlog t)
/*** Starts the small tangle of critical services that are needed to get the system off the* ground. These services have complex mutual dependencies which is why we initialize them all* in one place here. Unless your service is also entwined in these dependencies, it should be* initialized in one of the other functions.*/private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {// 省略部分代碼t.traceBegin("StartPackageManagerService");try {Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");mPackageManagerService = PackageManagerService.main(mSystemContext, installer,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);} finally {Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");}// Now that the package manager has started, register the dex load reporter to capture any// dex files loaded by system server.// These dex files will be optimized by the BackgroundDexOptService.SystemServerDexLoadReporter.configureSystemServerDexReporter(mPackageManagerService);mFirstBoot = mPackageManagerService.isFirstBoot();mPackageManager = mSystemContext.getPackageManager();// 省略部分代碼}

??????startBootstrapServices()方法中調用了PMS的main()方法來初始化PMS服務

  • PackageManagerService.main()
public static PackageManagerService main(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {// 省略部分代碼// new一個Java層的PMS對象PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);// 省略部分代碼// 將new出來的PMS對象注冊到ServiceManager中,之后我們便能夠通過Context對象的getPackageManager()來獲取PM對象了,這部分源碼在Context.getPackageManager()/ContextImpl.getPackageManager()中m.installWhitelistedSystemPackages();ServiceManager.addService("package", m);final PackageManagerNative pmn = m.new PackageManagerNative();ServiceManager.addService("package_native", pmn);return m;}

??????這里我們主要關注PMS的實例化過程,PMS的構造函數如下:

  • PackageManagerService.PackageManagerService()
/** Directory where installed applications are stored */private static final File sAppInstallDir =new File(Environment.getDataDirectory(), "app");public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {// 省略部分代碼if (!mOnlyCore) {EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,SystemClock.uptimeMillis());scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0,packageParser, executorService);}// 省略部分代碼}

??????PMS的構造函數中通過scanDirTracedLI()來掃描已安裝的三方App apk信息,路徑為/data/app/

  • PackageManagerService.scanDirTracedLI()
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);}}
  • PackageManagerService.scanDirLI()
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime,PackageParser2 packageParser, ExecutorService executorService) {ParallelPackageParser parallelPackageParser =new ParallelPackageParser(packageParser, executorService);// Submit files for parsing in parallelint fileCount = 0;for (File file : files) {final boolean isPackage = (isApkFile(file) || file.isDirectory())&& !PackageInstallerService.isStageName(file.getName());if (!isPackage) {// Ignore entries which are not packagescontinue;}// 繼續往下調用了parallelPackageParser.submit()parallelPackageParser.submit(file, parseFlags);fileCount++;}}
  • ParallelPackageParser.submit(File scanFile, int parseFlags)
/*** Submits the file for parsing* @param scanFile file to scan* @param parseFlags parse flags*/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;// 繼續往下調用了parserPackage()pr.parsedPackage = parsePackage(scanFile, parseFlags);} catch (Throwable e) {pr.throwable = e;} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}try {mQueue.put(pr);} catch (InterruptedException e) {Thread.currentThread().interrupt();// Propagate result to callers of take().// This is helpful to prevent main thread from getting stuck waiting on// ParallelPackageParser to finish in case of interruptionmInterruptedInThread = Thread.currentThread().getName();}});}
  • ParallelPackageParser.parsePackage(File scanFile, int parseFlags)
protected ParsedPackage parsePackage(File scanFile, int parseFlags)throws PackageParser.PackageParserException {return mPackageParser.parsePackage(scanFile, parseFlags, true);}
  • PackageParser2.parsePackage(File packageFile, int flags, boolean useCaches)
/*** TODO(b/135203078): Document new package parsing*/@AnyThreadpublic 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;}}long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;ParseInput input = mSharedResult.get().reset();// 繼續往下調用了parsePackage()ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);// 省略部分代碼return parsed;}
  • ParsingPackageUtils.parsePackage(ParseInput input, File packageFile, int flags)
public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile,int flags)throws PackageParserException {if (packageFile.isDirectory()) {// 如果給定的路徑是文件夾,則循環解析其中的所有apk文件return parseClusterPackage(input, packageFile, flags);} else {// 如果給定的路徑是文件,則調用parseMonolithicPackage()解析文件return parseMonolithicPackage(input, packageFile, flags);} }

注: Android5.0引入了Split APK機制,這是為了解決65536上限以及APK安裝包越來越大等問題。Split APK機制可以將一個APK,拆分成多個獨立APK。
在引入了Split APK機制后,APK有兩種分類:

Single APK:安裝文件為一個完整的APK,即base APK。Android稱其為Monolithic。

Mutiple APK:安裝文件在一個文件目錄中,其內部有多個被拆分的APK,這些APK由一個 base APK和一個或多個split APK組成。Android稱其為Cluster。

??????我們直接看解析Single APK文件的邏輯

  • ParsingPackageUtils.parseMonolithicPackage(ParseInput input, File apkFile, int flags)
/*** Parse the given APK file, treating it as as a single monolithic package.* <p>* Note that this <em>does not</em> perform signature verification; that* must be done separately in {@link #getSigningDetails(ParsingPackageRead, boolean)}.*/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);if (result.isError()) {return input.error(result);}return input.success(result.getResult().setUse32BitAbi(lite.use32bitAbi));} catch (IOException e) {return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,"Failed to get path: " + apkFile, e);} finally {IoUtils.closeQuietly(assetLoader);}}
  • parsingPackageUtils.parseBaseApk(ParseInput input, File apkFile, String codePath, AssetManager assets, int flags)
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,String codePath, AssetManager assets, int flags) {final String apkPath = apkFile.getAbsolutePath();// 省略部分代碼// 打開apk的AndroidManifest.xml文件try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,PackageParser.ANDROID_MANIFEST_FILENAME)) {final Resources res = new Resources(assets, mDisplayMetrics, null);// 開始解析AndroidManifest.xml的信息ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,parser, flags);// 省略部分代碼ApkAssets apkAssets = assets.getApkAssets()[0];if (apkAssets.definesOverlayable()) {SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();int size = packageNames.size();for (int index = 0; index < size; index++) {String packageName = packageNames.get(index);Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);if (overlayableToActor != null && !overlayableToActor.isEmpty()) {for (String overlayable : overlayableToActor.keySet()) {pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));}}}}pkg.setVolumeUuid(volumeUuid);if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) {pkg.setSigningDetails(getSigningDetails(pkg, false));} else {pkg.setSigningDetails(SigningDetails.UNKNOWN);}return input.success(pkg);} catch (Exception e) {return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,"Failed to read manifest from " + apkPath, e);}}
  • parsingPackageUtils.parseBaseApk(ParseInput input, String apkPath, String codePath, Resources res, XmlResourceParser parser, int flags)
/*** Parse the manifest of a <em>base APK</em>. When adding new features you* need to consider whether they should be supported by split APKs and child* packages.** @param apkPath The package apk file path* @param res The resources from which to resolve values* @param parser The manifest parser* @param flags Flags how to parse* @return Parsed package or null on error.*/private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, String apkPath,String codePath, Resources res, XmlResourceParser parser, int flags)throws XmlPullParserException, IOException, PackageParserException {// 省略部分代碼final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);try {final boolean isCoreApp =parser.getAttributeBooleanValue(null, "coreApp", false);final ParsingPackage pkg = mCallback.startParsingPackage(pkgName, apkPath, codePath, manifestArray, isCoreApp);// 繼續往下調用了parseBaseApkTags()final ParseResult<ParsingPackage> result =parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);if (result.isError()) {return result;}return input.success(pkg);} finally {manifestArray.recycle();}}
  • parsingPackageUtils.parseBaseApkTags(ParseInput input, ParsingPackage pkg, TypedArray sa, Resources res, XmlResourceParser parser, int flags)
private ParseResult<ParsingPackage> parseBaseApkTags(ParseInput input, ParsingPackage pkg,TypedArray sa, Resources res, XmlResourceParser parser, int flags)throws XmlPullParserException, IOException {//省略部分代碼while ((type = parser.next()) != XmlPullParser.END_DOCUMENT&& (type != XmlPullParser.END_TAG|| parser.getDepth() > depth)) {if (type != XmlPullParser.START_TAG) {continue;}String tagName = parser.getName();final ParseResult result;if (PackageParser.TAG_APPLICATION.equals(tagName)) {// 找到<applicaton>標簽,四大組件的聲明都在里面if (foundApp) {if (PackageParser.RIGID_PARSER) {result = input.error("<manifest> has more than one <application>");} else {Slog.w(TAG, "<manifest> has more than one <application>");result = input.success(null);}} else {foundApp = true;// 關鍵在這里result = parseBaseApplication(input, pkg, res, parser, flags);}} else {// 解析除<application>外的其他標簽result = parseBaseApkTag(tagName, input, pkg, res, parser, flags);}if (result.isError()) {return input.error(result);}}// 省略部分代碼return input.success(pkg);}
  • parsingPackageUtils.parseBaseApplication(ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)throws XmlPullParserException, IOException {// 省略部分代碼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;String tagName = parser.getName();boolean isActivity = false;switch (tagName) {case "activity":isActivity = true;// fall-throughcase "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(mSeparateProcesses, pkg, res, parser,flags, PackageParser.sUseRoundIcon, input);if (serviceResult.isSuccess()) {ParsedService service = serviceResult.getResult();hasServiceOrder |= (service.getOrder() != 0);pkg.addService(service);}result = serviceResult;break;case "provider":// Provider的解析ParseResult<ParsedProvider> providerResult =ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,flags, PackageParser.sUseRoundIcon, input);if (providerResult.isSuccess()) {// 注冊provider信息pkg.addProvider(providerResult.getResult());}result = providerResult;break;case "activity-alias":activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,parser, PackageParser.sUseRoundIcon, input);if (activityResult.isSuccess()) {ParsedActivity activity = activityResult.getResult();hasActivityOrder |= (activity.getOrder() != 0);pkg.addActivity(activity);}result = activityResult;break;default:result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);break;}if (result.isError()) {return input.error(result);}}if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) {// Add a hidden app detail activity to normal apps which forwards user to App Details// page.// 如果App沒有入口Activity,就將其入口設置為App詳情頁ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);if (a.isError()) {return input.error(a);}pkg.addActivity(a.getResult());}return input.success(pkg);}

??????parseBaseApplication()方法中PMS根據tag來區分四大組件,并調用相應的方法做進一步的解析。最終PMS會通過ParsingPackage的addXXX()方法將解析到的組件信息添加到對應的List中。ParsingPackage是一個接口,對應的實現類為ParsingPackageImpl

  • ParsingPackageImpl.addXXX()
@NonNullprotected List<ParsedActivity> activities = emptyList();@NonNullprotected List<ParsedActivity> receivers = emptyList();@NonNullprotected List<ParsedService> services = emptyList();@NonNullprotected List<ParsedProvider> providers = emptyList();@Overridepublic ParsingPackageImpl addActivity(ParsedActivity parsedActivity) {this.activities = CollectionUtils.add(this.activities, parsedActivity);addMimeGroupsFromComponent(parsedActivity);return this;}@Overridepublic ParsingPackageImpl addReceiver(ParsedActivity parsedReceiver) {this.receivers = CollectionUtils.add(this.receivers, parsedReceiver);addMimeGroupsFromComponent(parsedReceiver);return this;}@Overridepublic ParsingPackageImpl addService(ParsedService parsedService) {this.services = CollectionUtils.add(this.services, parsedService);addMimeGroupsFromComponent(parsedService);return this;}@Overridepublic ParsingPackageImpl addProvider(ParsedProvider parsedProvider) {this.providers = CollectionUtils.add(this.providers, parsedProvider);addMimeGroupsFromComponent(parsedProvider);return this;}

??????至此,四大組件的信息就都被解析并且保存下來了。

三、小結

??????整個流程看下來我們會發現,PMS對四大組件信息的處理采用的是實時解析AndroidManifest.xml文件的方式,中間并沒有多余的轉存邏輯。因此,想要通過修改中間容器的方式來實現四大組件的插件化看起來是不太可行的。只能老老實實走預埋+代理的邏輯。
??????但是,實時解析的方式雖然安全準確,但是速度慢呀。當你手機里安裝的App越來越多,設備的開機速度就會變得越來越慢。因此,新版本上安卓系統為了提升設備的啟動速度,在PMS解析AndroidManifest.xml文件的流程中引入了相應的緩存邏輯來優化解析速度。

總結

以上是生活随笔為你收集整理的PMS解析AndroidManifest.xml文件的过程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 中文字幕久久熟女蜜桃 | 久久精品亚洲无码 | 国产精品4p| 无码视频一区二区三区 | 国产免费av一区二区三区 | 色乱码一区二区三区网站 | 国产麻豆网 | 激情视频在线观看免费 | 久久日本精品字幕区二区 | 亚洲黄色a| 欧美日韩激情在线一区二区三区 | 免费看欧美一级特黄a大片 国产免费的av | 三级av毛片| av在线网页 | caopor在线视频| 色妻av| 亚洲4区| 狠狠操女人 | 初尝黑人巨炮波多野结衣 | 五月av综合av国产av | 四川丰满少妇被弄到高潮 | 一级看片 | √资源天堂中文在线视频 | 深爱综合网 | 成人涩涩软件 | 国产在线不卡 | 国产精品后入内射日本在线观看 | 精品二区在线 | 欧美午夜精品久久久久免费视 | 台湾150部性三级 | 日韩中文字幕在线 | 国产毛片一区二区 | 欧美日韩在线免费播放 | 亚洲国产精品久久精品怡红院 | 久久国产劲爆∧v内射 | 91麻豆蜜桃一区二区三区 | 久久久成人av | 97黄色网 | 在线看的免费网站 | 亚洲4p | 国产色综合天天综合网 | 中文天堂网 | 好男人网站| 久久影院中文字幕 | 久久涩视频 | 精品三级在线观看 | 第一宅男av导航入口 | 日韩免费福利视频 | 先锋影音资源av | 亚洲xxxxxx | 欧美视频第一区 | 国产在线一区二区三区四区 | 美女大逼 | 欧美99热| 国产福利免费观看 | 一区小视频 | 97人妻精品一区二区三区免 | 欧美精品在欧美一区二区少妇 | 天天射影院 | 国产免费自拍 | 91蝌蚪网 | 国产日韩欧美不卡 | 欧美大片免费在线观看 | 久久久久五月 | 丰满肉嫩西川结衣av | 最新av在线| 亚洲综合视频在线 | 国产亚洲欧美一区二区 | 国产五月天婷婷 | 亚洲色图在线观看 | 日韩av在线直播 | 91视频免费观看网站 | 内谢少妇xxxxx8老少交视频 | av视| 黄视频网站免费看 | 欧美与黑人午夜性猛交久久久 | 精品无码久久久久 | 成人av一区二区在线观看 | 日韩精品999 | 91成人免费版 | av无码一区二区三区 | 亚洲一区二区在线免费 | av中出| 日本黄网站色大片免费观看 | 成人在线视频免费观看 | 黄色录像大片 | 日本一区二区视频免费 | 国产男女猛烈无遮挡 | 亚洲视频不卡 | 亚洲欧美视频一区 | 日韩激情视频 | 亚洲在线国产 | 国内自拍第三页 | 波多野结衣国产在线 | 亚洲第一偷拍 | 日韩一区二区在线免费观看 | 国产一区二区综合 | 久久国产精品二区 | 麻豆一区二区99久久久久 |