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相关的代码,在构造方法中有这样几段代码。可以发现在构造方法中,解析了系统中几个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文件的*。看一下scanDirLI方法的具体实现就什么卵都知道了:

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对象中。可以看到平时在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阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容