概述:
android和你通常理解的程序路徑是不一樣的脱拼,android應用安裝完畢后,會存儲在/data/app或者/system/app目錄中,當程序運行時坦辟,所需要的layout文件,drawable文件等等需要從該目錄中的原文件中進行讀取声离。首先加載resource.asc芒炼,然后根據(jù)id值尋找相應的資源,而lib目錄等特殊文件會存放 /data/data/你的應用包名此路徑下术徊。
- 安裝分類
- 幾個相關目錄
- APK安裝流程
- APK解析流程
- 調(diào)用PMS
安裝分類
根據(jù)觸發(fā)條件可分為三類:
分類 | 說明 |
---|---|
系統(tǒng)應用安裝 | 沒有安裝界面本刽,在開機時自動完成 |
網(wǎng)絡下載應用安裝 | 沒有安裝界面,在應用市場完成 |
ADB命令安裝 | 沒有安裝界面赠涮,通過命令直接安裝 |
外部設備安裝 | 有安裝界面子寓,通過SD卡等外部設備安裝,由packageInstaller處理安裝邏輯 |
幾個相關目錄
以下是比較關鍵的四個目錄:
(1)system/app : 系統(tǒng)自帶的應用程序笋除,獲得root權限才能刪除
(2)data/app : 用戶程序安裝目錄斜友,安裝時會把apk文件復制到此目錄下
(3)data/data : 存放應用程序的數(shù)據(jù)
(4)data/dalvik-cache : 將apk中的dex文件安裝到該目錄下(dex文件是dalvik虛擬機的可執(zhí)行文件,大小約為原始apk的四分之一)
APK安裝流程
如下是比較關鍵的四個目錄:
(1)拷貝apk到指定的目錄
默認情況下垃它,用戶安裝的apk首先會拷貝到/data/app下鲜屏,用戶有訪問/data/app目錄的權限,但系統(tǒng)出廠的apk文件會被放到/system分區(qū)下国拇,包括/system/app洛史,/system/vendor/app,以及/system/priv-app等酱吝。該分區(qū)需要root權限的用戶才能訪問也殖。
(2)解壓apk、拷貝文件务热、創(chuàng)建應用的數(shù)據(jù)目錄
為了加快APP的啟動速度忆嗜,apk在安裝的時候己儒,會首先將APP的可執(zhí)行文件(dex)拷貝到/data/dalvik-cache目錄下,緩存起來霎褐。再在/data/data/目錄下創(chuàng)建應用程序的數(shù)據(jù)目錄(以應用包名命令)址愿,用來存放應用的數(shù)據(jù)庫、cache冻璃、二進制的so動態(tài)庫等响谓。資源文件還在原來的APK文件里,并沒有拷貝過來
(3)解析apk的AndroidManifest.xml文件
在安裝apk的過程中省艳,會解析apk的AndroidManifest.xml文件娘纷,將apk的權限、應用包名跋炕、apk的安裝位置赖晶、版本、userID等重要信息保存在/data/system/packages.xml文件中辐烂。這些操作都是在PackageManagerService中完成的遏插,并將解析出的四大組件信息注冊到PackageManagerService中。
(4)顯示icon圖標
應用程序經(jīng)過PMS中的邏輯處理后纠修,相當于已經(jīng)注冊好了胳嘲,如果想要在Android桌面上看到icon圖標,則需要Launcher將系統(tǒng)中已經(jīng)安裝的程序展現(xiàn)在桌面上扣草。
APK解析流程
解析AndroidManifest.xml文件
APK文件里包含了一個配置文件AndroidManifest.xml了牛,Android應用程序的解析過程就是解析這個xml文件的過程。APK解析是從PackageManagerService的scanPackageLI開始的辰妙,而該方法內(nèi)部又調(diào)用的是scanPackageDirtyLI()方法鹰祸,我們來看一下這個方法的實現(xiàn)。
public class PackageManagerService extends IPackageManager.Stub {
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
//...
// writer
synchronized (mPackages) {
// 驗證已注冊的ContentProvider是否有其他同名密浑,做沖突檢測蛙婴。
if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
final int N = pkg.providers.size();
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
if (p.info.authority != null) {
String names[] = p.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
if (mProvidersByAuthority.containsKey(names[j])) {
PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
final String otherPackageName =
((other != null && other.getComponentName() != null) ?
other.getComponentName().getPackageName() : "?");
throw new PackageManagerException(
INSTALL_FAILED_CONFLICTING_PROVIDER,
"Can't install because provider name " + names[j]
+ " (in package " + pkg.applicationInfo.packageName
+ ") is already used by " + otherPackageName);
}
}
}
}
}
}
if (mPlatformPackage == pkg) {
//...
} else {
// This is a normal package, need to make its data directory.
dataPath = getDataPathForPackage(pkg.packageName, 0);
if (dataPath.exists()) {
//...
} else {
//invoke installer to do the actual installation
//這里創(chuàng)建了應用數(shù)據(jù)目錄,用于存放用戶數(shù)據(jù)
int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.seinfo);
//...
}
}
// We also need to dexopt any apps that are dependent on this library. Note that
// if these fail, we should abort the install since installing the library will
// result in some apps being broken.
if (clientLibPkgs != null) {
if ((scanFlags & SCAN_NO_DEX) == 0) {
for (int i = 0; i < clientLibPkgs.size(); i++) {
PackageParser.Package clientPkg = clientLibPkgs.get(i);
if (performDexOptLI(clientPkg, null /* instruction sets */, forceDex,
(scanFlags & SCAN_DEFER_DEX) != 0, false) == DEX_OPT_FAILED) {
throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
"scanPackageLI failed to dexopt clientLibPkgs");
}
}
}
}
// writer
synchronized (mPackages) {
//...
// 以下對四大組件進行注冊
int N = pkg.providers.size();
StringBuilder r = null;
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
p.info.processName = fixProcessName(pkg.applicationInfo.processName,
p.info.processName, pkg.applicationInfo.uid);
//注冊Content Provider
mProviders.addProvider(p);
//...
}
//...
}
}
//...
}
}
scanPackageDirtyLI是一個上千行的函數(shù)尔破,它主要完成的工作如下所示:
(1)調(diào)用PackageParser的parsePackage()方法解析AndroidMainfest.xml文件敬锐,主要包括四大組件、權限信息呆瞻、用戶ID台夺,其他use-feature、shared-userId痴脾、use-library等 信息颤介,并保存到PackageManagerService相應的成員變量中。
(2)調(diào)用簽名驗證方法verifySignaturesLP()進行簽名驗證,驗證失敗的無法進行安裝滚朵。
(3)調(diào)用createDataDirsDirtyLI()方法創(chuàng)建應用目錄/data/data/package冤灾,同時將APK中提取的DEX文件保存到/data/dalvik-cache中。
(4)調(diào)用performDexOptLI()方法執(zhí)行dexopt操作辕近。
解析 APK 文件
Apk的解析是PackageParser的parsePackage()函數(shù)來完成的韵吨,我們來看看它的實現(xiàn)。
public class PackageParser {
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);
}
}
private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
//...
//初始化AssetManager
final AssetManager assets = new AssetManager();
try {
//...
//解析Base APk移宅,解析AndroidManifest.xml
final Package pkg = parseBaseApk(baseApk, assets, flags);
if (pkg == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse base APK: " + baseApk);
}
//如果splitName不為空归粉,則循環(huán)解析Split Apk
if (!ArrayUtils.isEmpty(lite.splitNames)) {
final int num = lite.splitNames.length;
pkg.splitNames = lite.splitNames;
pkg.splitCodePaths = lite.splitCodePaths;
pkg.splitRevisionCodes = lite.splitRevisionCodes;
pkg.splitFlags = new int[num];
pkg.splitPrivateFlags = new int[num];
for (int i = 0; i < num; i++) {
//解析
parseSplitApk(pkg, i, assets, flags);
}
}
pkg.setCodePath(packageDir.getAbsolutePath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
}
注:Split APK是Google為解決65535上限以及APK越來越大的問題而提出的一種方案,它可以將一個龐大的APK按照屏幕密度漏峰、ABI等形式拆分成多個獨立的APK糠悼,這些APK共享相同的data、cache目錄浅乔。 共享相同的進程倔喂,共享相同的包名。它們還可以使用各自的資源靖苇,并且繼承了Base APK里的資源席噩。
該方法調(diào)用parseBaseApk()去解析AndroidManifest.xml,AndroidManifest.xml也是xml文件贤壁,當然也使用XmlResourceParser來解析班挖。這個解析相應標簽并保存到PackageManagerService對應的成員變量中去。
調(diào)用PMS流程
在應用中,我們通常是調(diào)用Context的getPackageManager()方法返回PackageManager對象芯砸,這里返回的實際是ApplicationPackageManager對象,這個對象創(chuàng)建時使用IPackageManager對象作為參數(shù),IPackageManager是PackageManagerService實現(xiàn)的AIDL接口,通過binder來獲取PackageManagerService對象。
因此ApplicationPackageManager是PackageManagerService的代理對象给梅,ApplicationPackageManager繼承自PackageManager假丧,PackageManager中定義了可以操作PackageManagerService的接口,在PackageManager中,實現(xiàn)對apk安裝的方法是installExistingPackage和installPackageWithVerificationAndEncryption(前者是通過包名去升級應用,后者是安裝新的應用)
尊重作者动羽,尊重原創(chuàng)包帚,參考文章:
https://blog.csdn.net/wy391920778/article/details/79881142
https://blog.csdn.net/qq_25804863/article/details/48565569