android系統什么時候解析mainfest(很枯燥乏味兒)

最近面試聊到很多啟動過程的事情:

Android系統的啟動過程中就已經解析了系統中安裝應用的androidManifest.xml文件并保存起來了?

android系統啟動之后會解析固定目錄下的apk文件,并執行解析,持久化apk信息,重新安裝等操作;

解析Manifest流程:Zygote進程 --> SystemServer進程 --> PackgeManagerService服務 --> scanDirLI方法 --> scanPackageLI方法 --> PackageParser.parserPackage方法;

解析完成Manifest之后會將apk的Manifest信息保存在Settings對象中并持久化,然后執行重新安裝的操作;

android系統啟動過程中解析Manifest的流程是通過PackageManagerService服務來實現的。這里我們重點分析一下PackageManagerService服務是如何解析Manifest

首先看一下在SystemServer進程啟動過程中是如何啟動PackageManagerService服務的:

private void startBootstrapServices() {

...

mPackageManagerService = PackageManagerService.main(mSystemContext, installer,

mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);

mFirstBoot = mPackageManagerService.isFirstBoot();

mPackageManager = mSystemContext.getPackageManager();

...

}

**Android系統的啟動過程中就解析系統中安裝應用的androidManifest.xml文件并保存起來了?**

android系統啟動之后會解析固定目錄下的apk文件,并執行解析,持久化apk信息,重新安裝等操作;

*解析Manifest流程:Zygote進程 --> SystemServer進程 --> PackgeManagerService服務 --> scanDirLI方法 --> scanPackageLI方法 --> PackageParser.parserPackage方法;*

解析完成Manifest之后會將apk的Manifest信息保存在Settings對象中并持久化,然后執行重新安裝的操作;

android系統啟動過程中解析Manifest的流程是通過PackageManagerService服務來實現的。那么,PackageManagerService服務是如何解析Manifest?

*首先看一下在SystemServer進程啟動過程中是如何啟動PackageManagerService服務的:*

private void startBootstrapServices() {

...

mPackageManagerService = PackageManagerService.main(mSystemContext, installer,

mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);

mFirstBoot = mPackageManagerService.isFirstBoot();

mPackageManager = mSystemContext.getPackageManager();

...

}

在SystemServer進程啟動過程中會調用SystemServer類的startBootstrapServices方法(主要用于啟動ActivityManagerService服務和PackageManagerService服務),然后會在這個方法中會調用PackageManagerService.main靜態方法,這個方法主要是用來初始化PackageManagerService服務并執行相關邏輯的。來看一下main方法的具體邏輯:

public static PackageManagerService main(Context context, Installer installer,

boolean factoryTest, boolean onlyCore) {

PackageManagerService m = new PackageManagerService(context, installer,

factoryTest, onlyCore);

ServiceManager.addService("package", m);

return m;

}

發現main方法的實現邏輯主要是創建了一個PackageManagerService對象,并將這個對象添加到ServierManager中為其他組件提供服務。好吧,看來*PackageManagerService的初始化操作主要是在PackageManagerService的構造方法中*了,來看一下其構造方法的實現邏輯:

File dataDir = Environment.getDataDirectory();

mAppDataDir = new File(dataDir, "data");

mAppInstallDir = new File(dataDir, "app");

mAppLib32InstallDir = new File(dataDir, "app-lib");

mAsecInternalPath = new File(dataDir, "app-asec").getPath();

mUserAppDataDir = new File(dataDir, "user");

mDrmAppPrivateInstallDir = new File(dataDir, "app-private");

PackageManagerService的構造方法代碼量比較大,貼出主要和解析Manifest相關的代碼,在構造方法中有這樣幾段代碼??梢园l現在構造方法中,解析了系統中幾個apk的安裝目錄,這幾個目錄就是系統中安裝apk的目錄,android系統會默認解析這幾個目錄下apk文件,也就是說*如果android手機在其他的目錄下存在apk文件系統是不會默認解析的,反過來說,如果把apk文件移動到這幾個目錄下,那么重新啟動操作系統,該apk文件就會被系統解析并執行相關的邏輯操作,具體做什么操作呢*?看下面的實現。

/ overlay packages if they reside in VENDOR_OVERLAY_DIR.

File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);

scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

// Find base frameworks (resource packages without code).

scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR

| PackageParser.PARSE_IS_PRIVILEGED,

scanFlags | SCAN_NO_DEX, 0);

// Collected privileged system packages.

final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");

scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR

| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

// Collect ordinary system packages.

final File systemAppDir = new File(Environment.getRootDirectory(), "app");

scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

// Collect all vendor packages.

File vendorAppDir = new File("/vendor/app");

try {

vendorAppDir = vendorAppDir.getCanonicalFile();

} catch (IOException e) {

// failed to look up canonical path, continue with original one

}

scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

// Collect all OEM packages.

final File oemAppDir = new File(Environment.getOemDirectory(), "app");

scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM

| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

在剛剛的PackageManagerService.main方法中,解析完剛剛的幾個系統目錄之后系統會調用scanDirLI方法,那么這個方法主要是做什么用的呢?*這個方法主要就是用于解析上面幾個目錄下的apk文件的*??匆幌聅canDirLI方法的具體實現就什么卵都知道了:

private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {

final File[] files = dir.listFiles();

if (ArrayUtils.isEmpty(files)) {

Log.d(TAG, "No files in app dir " + dir);

return;

}

if (DEBUG_PACKAGE_SCANNING) {

Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags

+ " flags=0x" + Integer.toHexString(parseFlags));

}

for (File file : files) {

final boolean isPackage = (isApkFile(file) || file.isDirectory())

&& !PackageInstallerService.isStageName(file.getName());

if (!isPackage) {

// Ignore entries which are not packages

continue;

}

try {

scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,

scanFlags, currentTime, null);

} catch (PackageManagerException e) {

Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());

// Delete invalid userdata apps

if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&

e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {

logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);

if (file.isDirectory()) {

mInstaller.rmPackageDir(file.getAbsolutePath());

} else {

file.delete();

}

}

}

}

}

可以放下其首先會遍歷該目錄下的所有文件,并判斷是否是apk文件,如果是apk文件則調用scanPackageLI方法,scanPackageLI方法的名字很明顯,就是用于解析這個apk文件的。

*繼續看一下scanPakcageLI方法的實現:*

private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,

long currentTime, UserHandle user) throws PackageManagerException {

if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);

parseFlags |= mDefParseFlags;

PackageParser pp = new PackageParser();

pp.setSeparateProcesses(mSeparateProcesses);

pp.setOnlyCoreApps(mOnlyCore);

pp.setDisplayMetrics(mMetrics);

if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {

parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;

}

final PackageParser.Package pkg;

try {

pkg = pp.parsePackage(scanFile, parseFlags);

} catch (PackageParserException e) {

throw PackageManagerException.from(e);

}

...

}

好吧,這個方法也比較復雜,這里列出重點相關的代碼,可以發現:在這個方法中創建了一個PackagerParser對象,并調用了parsePackage方法,*這個方法其實就是解析Manifest的主要方法*,可以看一下其具體的實現:

public Package parsePackage(File packageFile, int flags) throws PackageParserException {

if (packageFile.isDirectory()) {

return parseClusterPackage(packageFile, flags);

} else {

return parseMonolithicPackage(packageFile, flags);

}

}

- 列表項目

可以發現,若解析的File對象是一個文件夾則執行調用parseClusterPackage方法,否則調用執行parseMonolithicPackage方法,很明顯的因為解析的是apk文件(在上一方法中循環遍歷得到了apk文件,這里的File對象就代表了一個個的apk文件信息),所以這里會執行parseMonolithicPackage方法,*然后再來看一下parseMonolithicPackage方法*:

public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {

if (mOnlyCoreApps) {

final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);

if (!lite.coreApp) {

throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,

"Not a coreApp: " + apkFile);

}

}

final AssetManager assets = new AssetManager();

try {

final Package pkg = parseBaseApk(apkFile, assets, flags);

pkg.codePath = apkFile.getAbsolutePath();

return pkg;

} finally {

IoUtils.closeQuietly(assets);

}

}

可以看出,這里又調用了parseBaseApk方法:

private Package parseBaseApk(File apkFile, AssetManager assets, int flags)

...

final Package pkg = parseBaseApk(res, parser, flags, outError);

...

}

可以看出,這個parseBaseApk方法調用了其重載的parseBaseApk方法:

while ((type = parser.next()) != XmlPullParser.END_DOCUMENT

&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {

if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {

continue;

}

String tagName = parser.getName();

if (tagName.equals("application")) {

if (foundApp) {

if (RIGID_PARSER) {

outError[0] = " has more than one ";

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return null;

} else {

Slog.w(TAG, " has more than one ");

XmlUtils.skipCurrentTag(parser);

continue;

}

}

foundApp = true;

if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {

return null;

}

} else if (tagName.equals("overlay")) {

pkg.mTrustedOverlay = trustedOverlay;

sa = res.obtainAttributes(attrs,

com.android.internal.R.styleable.AndroidManifestResourceOverlay);

pkg.mOverlayTarget = sa.getString(

com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);

pkg.mOverlayPriority = sa.getInt(

com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,

-1);

sa.recycle();

if (pkg.mOverlayTarget == null) {

outError[0] = " does not specify a target package";

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return null;

}

if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {

outError[0] = " priority must be between 0 and 9999";

mParseError =

PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return null;

}

XmlUtils.skipCurrentTag(parser);

} else if (tagName.equals("key-sets")) {

if (!parseKeySets(pkg, res, parser, attrs, outError)) {

return null;

}

} else if (tagName.equals("permission-group")) {

if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {

return null;

}

} else if (tagName.equals("permission")) {

if (parsePermission(pkg, res, parser, attrs, outError) == null) {

return null;

}

} else if (tagName.equals("permission-tree")) {

if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {

return null;

}

} else if (tagName.equals("uses-permission")) {

if (!parseUsesPermission(pkg, res, parser, attrs)) {

return null;

}

} else if (tagName.equals("uses-permission-sdk-m")

|| tagName.equals("uses-permission-sdk-23")) {

if (!parseUsesPermission(pkg, res, parser, attrs)) {

return null;

}

} else if (tagName.equals("uses-configuration")) {

ConfigurationInfo cPref = new ConfigurationInfo();

sa = res.obtainAttributes(attrs,

com.android.internal.R.styleable.AndroidManifestUsesConfiguration);

cPref.reqTouchScreen = sa.getInt(

com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,

Configuration.TOUCHSCREEN_UNDEFINED);

cPref.reqKeyboardType = sa.getInt(

com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,

Configuration.KEYBOARD_UNDEFINED);

if (sa.getBoolean(

com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,

false)) {

cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;

}

cPref.reqNavigation = sa.getInt(

com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,

Configuration.NAVIGATION_UNDEFINED);

if (sa.getBoolean(

com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,

false)) {

cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;

}

sa.recycle();

pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);

XmlUtils.skipCurrentTag(parser);

} else if (tagName.equals("uses-feature")) {

FeatureInfo fi = parseUsesFeature(res, attrs);

pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);

if (fi.name == null) {

ConfigurationInfo cPref = new ConfigurationInfo();

cPref.reqGlEsVersion = fi.reqGlEsVersion;

pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);

}

XmlUtils.skipCurrentTag(parser);

} else if (tagName.equals("feature-group")) {

FeatureGroupInfo group = new FeatureGroupInfo();

ArrayList features = null;

final int innerDepth = parser.getDepth();

while ((type = parser.next()) != XmlPullParser.END_DOCUMENT

&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {

if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {

continue;

}

final String innerTagName = parser.getName();

if (innerTagName.equals("uses-feature")) {

FeatureInfo featureInfo = parseUsesFeature(res, attrs);

// FeatureGroups are stricter and mandate that

// any declared are mandatory.

featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;

features = ArrayUtils.add(features, featureInfo);

} else {

Slog.w(TAG, "Unknown element under : " + innerTagName +

" at " + mArchiveSourcePath + " " +

parser.getPositionDescription());

}

XmlUtils.skipCurrentTag(parser);

}

if (features != null) {

group.features = new FeatureInfo[features.size()];

group.features = features.toArray(group.features);

}

pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);

} else if (tagName.equals("uses-sdk")) {

if (SDK_VERSION > 0) {

sa = res.obtainAttributes(attrs,

com.android.internal.R.styleable.AndroidManifestUsesSdk);

int minVers = 0;

String minCode = null;

int targetVers = 0;

String targetCode = null;

TypedValue val = sa.peekValue(

com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);

if (val != null) {

if (val.type == TypedValue.TYPE_STRING && val.string != null) {

targetCode = minCode = val.string.toString();

} else {

// If it's not a string, it's an integer.

targetVers = minVers = val.data;

}

}

val = sa.peekValue(

com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);

if (val != null) {

if (val.type == TypedValue.TYPE_STRING && val.string != null) {

targetCode = minCode = val.string.toString();

} else {

// If it's not a string, it's an integer.

targetVers = val.data;

}

}

sa.recycle();

if (minCode != null) {

boolean allowedCodename = false;

for (String codename : SDK_CODENAMES) {

if (minCode.equals(codename)) {

allowedCodename = true;

break;

}

}

if (!allowedCodename) {

if (SDK_CODENAMES.length > 0) {

outError[0] = "Requires development platform " + minCode

+ " (current platform is any of "

+ Arrays.toString(SDK_CODENAMES) + ")";

} else {

outError[0] = "Requires development platform " + minCode

+ " but this is a release platform.";

}

mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;

return null;

}

} else if (minVers > SDK_VERSION) {

outError[0] = "Requires newer sdk version #" + minVers

+ " (current version is #" + SDK_VERSION + ")";

mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;

return null;

}

if (targetCode != null) {

boolean allowedCodename = false;

for (String codename : SDK_CODENAMES) {

if (targetCode.equals(codename)) {

allowedCodename = true;

break;

}

}

if (!allowedCodename) {

if (SDK_CODENAMES.length > 0) {

outError[0] = "Requires development platform " + targetCode

+ " (current platform is any of "

+ Arrays.toString(SDK_CODENAMES) + ")";

} else {

outError[0] = "Requires development platform " + targetCode

+ " but this is a release platform.";

}

mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;

return null;

}

// If the code matches, it definitely targets this SDK.

pkg.applicationInfo.targetSdkVersion

= android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;

} else {

pkg.applicationInfo.targetSdkVersion = targetVers;

}

}

XmlUtils.skipCurrentTag(parser);

} else if (tagName.equals("supports-screens")) {

sa = res.obtainAttributes(attrs,

com.android.internal.R.styleable.AndroidManifestSupportsScreens);

pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(

com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,

0);

pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger(

com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,

0);

pkg.applicationInfo.largestWidthLimitDp = sa.getInteger(

com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,

0);

// This is a trick to get a boolean and still able to detect

// if a value was actually set.

supportsSmallScreens = sa.getInteger(

com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,

supportsSmallScreens);

supportsNormalScreens = sa.getInteger(

com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,

supportsNormalScreens);

supportsLargeScreens = sa.getInteger(

com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,

supportsLargeScreens);

supportsXLargeScreens = sa.getInteger(

com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens,

supportsXLargeScreens);

resizeable = sa.getInteger(

com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,

resizeable);

anyDensity = sa.getInteger(

com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,

anyDensity);

sa.recycle();

XmlUtils.skipCurrentTag(parser);

} else if (tagName.equals("protected-broadcast")) {

sa = res.obtainAttributes(attrs,

com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);

// Note: don't allow this value to be a reference to a resource

// that may change.

String name = sa.getNonResourceString(

com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);

sa.recycle();

if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {

if (pkg.protectedBroadcasts == null) {

pkg.protectedBroadcasts = new ArrayList();

}

if (!pkg.protectedBroadcasts.contains(name)) {

pkg.protectedBroadcasts.add(name.intern());

}

}

XmlUtils.skipCurrentTag(parser);

} else if (tagName.equals("instrumentation")) {

if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {

return null;

}

} else if (tagName.equals("original-package")) {

sa = res.obtainAttributes(attrs,

com.android.internal.R.styleable.AndroidManifestOriginalPackage);

String orig =sa.getNonConfigurationString(

com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);

if (!pkg.packageName.equals(orig)) {

if (pkg.mOriginalPackages == null) {

pkg.mOriginalPackages = new ArrayList();

pkg.mRealPackage = pkg.packageName;

}

pkg.mOriginalPackages.add(orig);

}

sa.recycle();

XmlUtils.skipCurrentTag(parser);

} else if (tagName.equals("adopt-permissions")) {

sa = res.obtainAttributes(attrs,

com.android.internal.R.styleable.AndroidManifestOriginalPackage);

String name = sa.getNonConfigurationString(

com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);

sa.recycle();

if (name != null) {

if (pkg.mAdoptPermissions == null) {

pkg.mAdoptPermissions = new ArrayList();

}

pkg.mAdoptPermissions.add(name);

}

XmlUtils.skipCurrentTag(parser);

} else if (tagName.equals("uses-gl-texture")) {

// Just skip this tag

XmlUtils.skipCurrentTag(parser);

continue;

} else if (tagName.equals("compatible-screens")) {

// Just skip this tag

XmlUtils.skipCurrentTag(parser);

continue;

} else if (tagName.equals("supports-input")) {

XmlUtils.skipCurrentTag(parser);

continue;

} else if (tagName.equals("eat-comment")) {

// Just skip this tag

XmlUtils.skipCurrentTag(parser);

continue;

} else if (RIGID_PARSER) {

outError[0] = "Bad element under : "

+ parser.getName();

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return null;

} else {

Slog.w(TAG, "Unknown element under : " + parser.getName()

+ " at " + mArchiveSourcePath + " "

+ parser.getPositionDescription());

XmlUtils.skipCurrentTag(parser);

continue;

}

}

在這個parseBaseApk方法中有一個while循環,該循環主要就是用于解析AndroidManifest.xml文件中的節點信息。在開始解析application節點的時候,同時調用了parseBaseApplication方法*,該方法解析了application節點下的activity,service,broadcast,contentprovier等(四大組件)組件的定義信息*:

while ((type = parser.next()) != XmlPullParser.END_DOCUMENT

&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {

if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {

continue;

}

String tagName = parser.getName();

if (tagName.equals("activity")) {

Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,

owner.baseHardwareAccelerated);

if (a == null) {

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return false;

}

owner.activities.add(a);

} else if (tagName.equals("receiver")) {

Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);

if (a == null) {

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return false;

}

owner.receivers.add(a);

} else if (tagName.equals("service")) {

Service s = parseService(owner, res, parser, attrs, flags, outError);

if (s == null) {

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return false;

}

owner.services.add(s);

} else if (tagName.equals("provider")) {

Provider p = parseProvider(owner, res, parser, attrs, flags, outError);

if (p == null) {

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return false;

}

owner.providers.add(p);

} else if (tagName.equals("activity-alias")) {

Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);

if (a == null) {

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return false;

}

owner.activities.add(a);

} else if (parser.getName().equals("meta-data")) {

// note: application meta-data is stored off to the side, so it can

// remain null in the primary copy (we like to avoid extra copies because

// it can be large)

if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,

outError)) == null) {

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return false;

}

} else if (tagName.equals("library")) {

sa = res.obtainAttributes(attrs,

com.android.internal.R.styleable.AndroidManifestLibrary);

// Note: don't allow this value to be a reference to a resource

// that may change.

String lname = sa.getNonResourceString(

com.android.internal.R.styleable.AndroidManifestLibrary_name);

sa.recycle();

if (lname != null) {

lname = lname.intern();

if (!ArrayUtils.contains(owner.libraryNames, lname)) {

owner.libraryNames = ArrayUtils.add(owner.libraryNames, lname);

}

}

XmlUtils.skipCurrentTag(parser);

} else if (tagName.equals("uses-library")) {

sa = res.obtainAttributes(attrs,

com.android.internal.R.styleable.AndroidManifestUsesLibrary);

// Note: don't allow this value to be a reference to a resource

// that may change.

String lname = sa.getNonResourceString(

com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);

boolean req = sa.getBoolean(

com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,

true);

sa.recycle();

if (lname != null) {

lname = lname.intern();

if (req) {

owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);

} else {

owner.usesOptionalLibraries = ArrayUtils.add(

owner.usesOptionalLibraries, lname);

}

}

XmlUtils.skipCurrentTag(parser);

} else if (tagName.equals("uses-package")) {

// Dependencies for app installers; we don't currently try to

// enforce this.

XmlUtils.skipCurrentTag(parser);

} else {

if (!RIGID_PARSER) {

Slog.w(TAG, "Unknown element under : " + tagName

+ " at " + mArchiveSourcePath + " "

+ parser.getPositionDescription());

XmlUtils.skipCurrentTag(parser);

continue;

} else {

outError[0] = "Bad element under : " + tagName;

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return false;

}

}

}

這樣,經過這里循環遍歷,**整個androidManifest的節點信息就被解析并保存在了Package對象中??梢钥吹狡綍r在Manifest中定義的各種節點,其實都是在這里有所體現**。當androidManifest.xml文件被解析完成之后,會調用剛剛介紹的scanPackageLI的重載方法,將解析完成的Package對象信息保存的Setting對象中,這個對象用于保存app的安裝信息,具體實現是在方法:

private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,

long currentTime, UserHandle user) throws PackageManagerException

當解析完成manifest文件之后會調用其重載方法:

// Note that we invoke the following method only if we are about to unpack an application

PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags

| SCAN_UPDATE_SIGNATURE, currentTime, user);

這樣,解析的manifest文件信息就會被保存到Settings中,并持久化,然后執行安裝apk的操作,可以看一下該重載方法的具體實現:

private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,

int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {

boolean success = false;

try {

final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,

currentTime, user);

success = true;

return res;

} finally {

if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {

removeDataDirsLI(pkg.volumeUuid, pkg.packageName);

}

}

}

*可以發現其內部調用了scanPackageDirtyLI方法,這個方法就是實際實現持久化manifest信息并安裝APK操作的:*

private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,

int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {

...

// And now re-install the app.

ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,

pkg.applicationInfo.seinfo);

...

}

可以發現其內部調用了createDataDirLI,該方法主要實現安裝apk的操作。

private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {

int[] users = sUserManager.getUserIds();

int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);

if (res < 0) {

return res;

}

for (int user : users) {

if (user != 0) {

res = mInstaller.createUserData(volumeUuid, packageName,

UserHandle.getUid(user, uid), user, seinfo);

if (res < 0) {

return res;

}

}

}

return res;

}

**查看該方法的實現:**

public int install(String uuid, String name, int uid, int gid, String seinfo) {

StringBuilder builder = new StringBuilder("install");

builder.append(' ');

builder.append(escapeNull(uuid));

builder.append(' ');

builder.append(name);

builder.append(' ');

builder.append(uid);

builder.append(' ');

builder.append(gid);

builder.append(' ');

builder.append(seinfo != null ? seinfo : "!");

return mInstaller.execute(builder.toString());

到這里,實現安裝apk的操作就是為了去執行install的這一句話。

**以上就是學習插件化時,整理的一份跟讀筆記。這個可以說是安卓啟動過程重要的部分,安卓小伙伴了解插件化這個必不可少。**

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容