當系統啟動之後,就會註冊各種系統服務,如WindowManagerService、ActivityManagerService等,其中就有PackageManagerService。PMS啟動之後,就會掃描已安裝的apk目錄,並解析apk下的androidmanifest.xml檔案得到app的資訊,而在androidmanifest.xml中又包含了activity、service等元件的註冊資訊,當PMS解析完成之後,整個app的資訊樹就被構建出來了。
那麼PMS是如何工作的呢,我們來一起分析一下。
PMS的建立
通常情況下,我們可以通過以下方式獲取到PackageManager:
PackageManager packageManager = getPackageManager();
複製程式碼
點進去可以發現PackageManager也是一個抽象類,而mBase就是Context:
@Override
public PackageManager getPackageManager() {
return mBase.getPackageManager();
}
複製程式碼
所以我們直接看Context的實現類ContextImpl:
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
複製程式碼
跳轉到ActivityThread:
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
複製程式碼
可以發現同樣和WMS一樣都是通過ServiceManager.getService("package")來獲得IBinder物件,並最終轉化為PackageManager。
那麼PMS是如何新增到ServiceManager中的呢?
這個時候我們需要去看SystemServer.java
,這個類會建立系統的服務:
private PowerManagerService mPowerManagerService;
private ActivityManagerService mActivityManagerService;
private WebViewUpdateService mWebViewUpdateService;
private DisplayManagerService mDisplayManagerService;
private PackageManagerService mPackageManagerService;
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
// PackageManagerService.java
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
// Disable any carrier apps. We do this very early in boot to prevent the apps from being
// disabled after already being started.
CarrierAppUtils.disableCarrierAppsUntilPrivileged(context.getOpPackageName(), m,
UserHandle.USER_SYSTEM);
ServiceManager.addService("package", m);
return m;
}
複製程式碼
可以發現,在SystemServer中PMS最終通過PackageManagerService.main
函式建立出來,並通過ServiceManager.addService("package", m)新增到ServiceManager中。
PMS的工作原理
PMS的主要工作就是解析已安裝的apk目錄,並解析AndroidManifest.xml檔案,最終解析出整個apk的資訊樹。
首先我們先看PMS的建構函式:
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// 獲取/data目錄
File dataDir = Environment.getDataDirectory();
// 獲取/data/data目錄,也就是第三方軟體目錄
mAppInstallDir = new File(dataDir, "app");
// 獲取framework資源、系統app資源
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
// 載入framework資源
scanDirTracedLI(frameworkDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// 載入系統應用
scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
// 載入第三方應用
scanDirLI(mEphemeralInstallDir, mDefParseFlags
| PackageParser.PARSE_IS_EPHEMERAL,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
}
複製程式碼
從上面的程式碼可以看到,PMS不僅要載入已經安裝好的apk,還需要載入framework的相關西苑,當載入好了之後才開始掃描指定目錄下的apk檔案並解析。 接下來我們繼續看scanDirLI:
private void scanDirLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// 如果不是package檔案,continue
continue;
}
try {
// 解析apk檔案
scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
}
}
}
private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
try {
return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
// 建立包解析器
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
}
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
// 解析apk
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}
複製程式碼
通過一系列的呼叫最終發現解析apk是通過PackageParser也就是包解析器,對apk檔案進行parsePackage方法的解析:
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
// 解析單個apk
return parseMonolithicPackage(packageFile, flags);
}
}
@Deprecated
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (mOnlyCoreApps) {
if (!lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + apkFile);
}
}
// 構建AssetManager
final AssetManager assets = new AssetManager();
try {
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.setCodePath(apkFile.getAbsolutePath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
// 構建資源
Resources res = null;
XmlResourceParser parser = null;
try {
res = new Resources(assets, mMetrics, null);
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
// 獲取AndroidManifest.xml解析器
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];
// 解析
final Package pkg = parseBaseApk(res, parser, flags, outError);
pkg.setVolumeUuid(volumeUuid);
pkg.setApplicationVolumeUuid(volumeUuid);
pkg.setBaseCodePath(apkPath);
pkg.setSignatures(null);
return pkg;
} catch (PackageParserException e) {
throw e;
} catch (Exception e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
}
}
複製程式碼
可以發現呼叫parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME)
獲取AndroidManifest.xml解析器
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
複製程式碼
從這我們可以得出一個結論如果一個apk包沒有AndroidManifest.xml檔案,那麼我們在開啟的時候就會報錯,包括lib檔案。
我們繼續追蹤,現在到了parseBaseApk方法:
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
// 解析apk的包名
Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
pkgName = packageSplit.first;
splitName = packageSplit.second;
// 構建package物件
final Package pkg = new Package(pkgName);
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
// 獲取apk的versioncode、versionname等資訊
pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.baseRevisionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null) {
pkg.mVersionName = pkg.mVersionName.intern();
}
pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
sa.recycle();
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}
複製程式碼
上面這個方法我們通過解析AndroidManifest.xml獲取到了apk包的相關資訊。
最終return呼叫的parseBaseApkCommon
方法才是解析xml檔案的函式。
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
mParseInstrumentationArgs = null;
mParseActivityArgs = null;
mParseServiceArgs = null;
mParseProviderArgs = null;
parseBaseApplication(pkg, res, parser, flags, outError);
}
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, 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, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
}
}
複製程式碼
由於程式碼太長,只擷取了一點典型程式碼,通過上面我們終於發現了activity、receiver等標籤,可以發現就是普通的xml解析,解析出來之後並返回一個Activity的例項,在加入到activities列表中。
public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
public final ArrayList<Service> services = new ArrayList<Service>(0);
複製程式碼
最終通過解析所有的標籤,把所有的節點資訊都儲存到對應的list中。 到這一步的時候,整個apk的資訊樹就已經被建立好了,整個apk的應用名、包名、圖示、activity、service等資訊都儲存到了系統中。