編輯:關於Android編程
最近有同學問我關於Manifest何時被系統解析的問題,正好也分析到這一塊了,索性這一章就講解一下android系統何時解析Manifest吧,這裡的Manifest指的是android安裝文件apk中的androidManifest.xml文件是何時被解析的。
大家應該都知道,Android系統啟動之後,我們就可以在一個應用中打開另一個從未打開過的應用,或者是在一個應用中發送廣播,如果另外一個應用設置了這個廣播的接收器,那麼這個應用進程就會被啟動並接收該廣播並作出相應的處理,這樣的例子很多,我們可以猜測到Android系統在啟動的時候就會抓取到了系統中所有安裝的應用信息(應該是解析apk文件的Manifest信息),即在Android系統的啟動過程中就已經解析了系統中安裝應用的androidManifest.xml文件並保存起來了,那麼這個過程具體是如何的呢?
其實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.mani方法中,解析完剛剛的幾個系統目錄之後系統會調用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());
}
怎麼樣?很熟悉吧,這裡的Installer其實調用的就是我們平時運行android項目很熟悉的install命令,原來android系統安裝apk文件底層都是調用的adb命令。
總結:
android系統啟動之後會解析固定目錄下的apk文件,並執行解析,持久化apk信息,重新安裝等操作;
解析Manifest流程:Zygote進程 –> SystemServer進程 –> PackgeManagerService服務 –> scanDirLI方法 –> scanPackageLI方法 –> PackageParser.parserPackage方法;
解析完成Manifest之後會將apk的Manifest信息保存在Settings對象中並持久化,然後執行重新安裝的操作;
本文實例為大家分享了Android仿微信二維碼和條形碼的具體代碼,供大家參考,具體內容如下package your.QRCode.namespace;import jav
在前不久的谷歌2015 I/O大會上,發布了Android新版本M,貌似從這個版本開始Android不在以數字命名版本了。在這次的I/O大會上谷歌對Andro
華為榮耀X2手機系統版本更新到B013版以後,鎖屏和密碼中增加了一項智能解鎖功能,聽起來都很高大上的功能,比指紋解鎖還NB,那這個智能解鎖功能怎麼用呢?下面
(一)概述雖然,已經學過了Android繪圖的內容,但是總是覺得很模糊,今天就好好梳理下思路吧!純粹就是一個讀書筆記,整理下自己以前不知道的內容,好了開始:(本節主要介紹