轉載請註明出處:http://blog.csdn.net/singwhatiwanna/article/details/19578947java
咱們知道,在android手機上安裝一個apk很簡單,只要打開apk文件,默認就會彈出安裝界面,而後點擊肯定,通過若干秒後,apk就安裝成功了,但是你知道apk的安裝過程是什麼嗎?你知道android系統在安裝一個apk的時候都幹了什麼嗎?在本文中,將一一解答這個問題。簡單來講,apk的安裝過程分兩步:第一步,將apk文件複製到程序目錄下(/data/app/);第二步,爲應用建立數據目錄(/data/data/package name/)、提取dex文件到指定目錄(/data/dalvik-cache/)、修改系統包管理信息。注意,本文的分析基於Android 4.3源碼。android
apk的安裝從PackageManager的installApk方法開始,因爲PackageManager所對應的binder服務爲PackageManagerService(PMS),因此,真正的安裝過程都在PackageManagerService中完成。PackageManagerService的installApk方法最終調用了installPackageWithVerificationAndEncryption方法,該方法的核心就是在最後發送了一個INIT_COPY的消息,這個消息的含義是完成apk的拷貝過程。web
public void installPackageWithVerificationAndEncryption(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); final int uid = Binder.getCallingUid(); if (isUserRestricted(UserHandle.getUserId(uid), UserManager.DISALLOW_INSTALL_APPS)) { try { observer.packageInstalled("", PackageManager.INSTALL_FAILED_USER_RESTRICTED); } catch (RemoteException re) { } return; } UserHandle user; if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) { user = UserHandle.ALL; } else { user = new UserHandle(UserHandle.getUserId(uid)); } final int filteredFlags; if (uid == Process.SHELL_UID || uid == 0) { if (DEBUG_INSTALL) { Slog.v(TAG, "Install from ADB"); } filteredFlags = flags | PackageManager.INSTALL_FROM_ADB; } else { filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB; } verificationParams.setInstallerUid(uid); final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName, verificationParams, encryptionParams, user); mHandler.sendMessage(msg); }
經過分析代碼能夠發現,真正實現apk拷貝的方法是InstallParams的handleStartCopy方法,InstallParams中有重試機制,拷貝若是失敗的話會重試,最多重試4次。在拷貝以前,還必須作一件事情,那就是綁定media container service,安裝過程當中一些狀態的檢查會用到這個服務,代碼以下所示:網絡
class PackageHandler extends Handler { private boolean mBound = false; final ArrayList<HandlerParams> mPendingInstalls = new ArrayList<HandlerParams>(); private boolean connectToService() { if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" + " DefaultContainerService"); Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mBound = true; return true; } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return false; } ... }
如今分析一下InstallParams的handleStartCopy方法,這個方法很長,代碼就不帖出來了,你們能夠本身去看看,這裏主要分析下它的工做流程:app
1. 檢查安裝位置標記位是否有衝突,若是有衝突,則安裝失敗,這裏的有衝突是指「一個apk同時要求被安裝到內部存儲和sd卡」ide
2. 調用MCS服務的getMinimalPackageInfo方法來獲得apk的推薦安裝位置,並檢查是否可以進行正常的安裝。在這一步,有可能拋出一些沒法安裝的狀態位:存儲空間不足、程序已經安裝、無效的apk文件等,這個時候安裝過程終止post
3. 到這一步,表示程序能夠正常安裝,同時MCS服務服務可能會根據須要調整安裝位置,在InstallParams的installLocationPolicy中完成ui
4. 文件的複製過程,PMS針對內部存儲和sd卡分別提供了一個類:FileInstallArgs和AsecInstallArgs,並分別調用兩者的copyApk方法來完成apk的複製過程this
通過了上面4步,待安裝apk已經被複制到了/data/app/目錄了。spa
上面,apk已經被複制到了/data/app/目錄,安裝的第一步已經完成,那麼系統是何時對apk進行dex提取和解析的呢,這還要從PMS提及,在PMS內部有一個AppDirObserver類,顧名思義,它的做用是應用目錄觀察者,它時刻觀察着應用目錄/data/app/,當目錄內部結構改變的時候(建立文件和刪除文件)它會作出相應行爲,下面看下它的代碼:
private final class AppDirObserver extends FileObserver { public AppDirObserver(String path, int mask, boolean isrom) { super(path, mask); mRootDir = path; mIsRom = isrom; } //在/data/app/目錄下添加或刪除apk的時候,此方法會被調用 public void onEvent(int event, String path) { String removedPackage = null; int removedAppId = -1; int[] removedUsers = null; String addedPackage = null; int addedAppId = -1; int[] addedUsers = null; // TODO post a message to the handler to obtain serial ordering synchronized (mInstallLock) { String fullPathStr = null; File fullPath = null; if (path != null) { fullPath = new File(mRootDir, path); fullPathStr = fullPath.getPath(); } if (DEBUG_APP_DIR_OBSERVER) Log.v(TAG, "File " + fullPathStr + " changed: " + Integer.toHexString(event)); if (!isPackageFilename(path)) { if (DEBUG_APP_DIR_OBSERVER) Log.v(TAG, "Ignoring change of non-package file: " + fullPathStr); return; } // Ignore packages that are being installed or // have just been installed. if (ignoreCodePath(fullPathStr)) { return; } PackageParser.Package p = null; PackageSetting ps = null; // reader synchronized (mPackages) { p = mAppDirs.get(fullPathStr); if (p != null) { ps = mSettings.mPackages.get(p.applicationInfo.packageName); if (ps != null) { removedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); } else { removedUsers = sUserManager.getUserIds(); } } addedUsers = sUserManager.getUserIds(); } //當apk被刪除的時候,每每意味着這個apk被卸載 if ((event&REMOVE_EVENTS) != 0) { if (ps != null) { if (DEBUG_REMOVE) Slog.d(TAG, "Package disappeared: " + ps); //removePackageLI方法完成卸載apk的主要功能 removePackageLI(ps, true); removedPackage = ps.name; removedAppId = ps.appId; } } //新添加了一個apk,每每意味着一個新的apk被安裝 if ((event&ADD_EVENTS) != 0) { if (p == null) { if (DEBUG_INSTALL) Slog.d(TAG, "New file appeared: " + fullPath); //scanPackageLI方法完成了apk安裝的第二個步驟 p = scanPackageLI(fullPath, (mIsRom ? PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR: 0) | PackageParser.PARSE_CHATTY | PackageParser.PARSE_MUST_BE_APK, SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME, System.currentTimeMillis(), UserHandle.ALL); if (p != null) { /* * TODO this seems dangerous as the package may have * changed since we last acquired the mPackages * lock. */ // writer synchronized (mPackages) { updatePermissionsLPw(p.packageName, p, p.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0); } addedPackage = p.applicationInfo.packageName; addedAppId = UserHandle.getAppId(p.applicationInfo.uid); } } } // reader synchronized (mPackages) { mSettings.writeLPr(); } } //下面兩個if語句塊你們應用不陌生吧,在咱們的應用中想監聽應用的安裝和卸載, //就是經過收聽ACTION_PACKAGE_ADDED和ACTION_PACKAGE_REMOVED這兩個廣播來實現的 if (removedPackage != null) { Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, removedAppId); extras.putBoolean(Intent.EXTRA_DATA_REMOVED, false); sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, extras, null, null, removedUsers); } if (addedPackage != null) { Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, addedAppId); sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, addedPackage, extras, null, null, addedUsers); } } private final String mRootDir; private final boolean mIsRom; }
下面,咱們主要分析一下scanPackageLI方法,仍是僅僅分析,不帖代碼,由於代碼太長了,帖出來無法看了,這個方法不只僅是完成apk包的掃描,還解析AndroidManifest.xml文件並提取出全部的intent-filter和permission信息,apk安裝的主要功能都由它來完成的,當apk包掃描完成後,系統會調用updatePermissionsLPw方法更新系統所具備的權限。
scanPackageLI方法有兩個,其第一個參數分別接受File和PackageParser.Package類型,第一個方法會從File中提取出package信息而後再調用第二個方法,下面分析第二個scanPackageLI方法,其完成的事情以下:
1. 若是包名是android,則會作一些特殊處理,這個包名爲android的應用是系統內部應用的,其餘應用的包名若是叫android則安裝會有問題,你們能夠試一下
2. 解析常見的use-feature、shared-userId、use-library標籤並保存到成員變量中
3. 進行簽名驗證,對應的方法是verifySignaturesLP,驗證失敗則應用沒法安裝
4. 建立應用程序目錄/data/data/包名,同時將apk中提取出dex文件並保存到/data/dalvik-cache,把apk當作zip解壓就能獲得dex文件
5. 解析AndroidManifest.xml文件,提取出所需信息,包括具備intent-filter的四大組件信息(Activity、Service、BroadcastReceiver、ContentProvider)和聲明的系統權限等
到此爲止,scanPackageLI方法結束了。而updatePermissionsLPw的做用是對系統中全部的權限進行更新,你們能夠查看下/system/etc/permissons目錄,下面定義了android系統中全部的權限,開發中最經常使用的權限定義在目錄下的platform.xml裏面,你們能夠打開看看,能夠看到常見的訪問網絡、讀寫外部存儲等權限等都是在這裏定義的。權限更新完畢之後,系統就會發送ACTION_PACKAGE_ADDED廣播,告知全部應用有新應用安裝了。另外,你們能夠查看下data/system/目錄,裏面有兩個文件packages.list和packages.xml,在packages.list裏面放的是手機上安裝的全部應用列表,而packages.xml中存放的是全部應用的設置應用,好比一個應用聲明瞭哪些系統權限就定義在這裏面。關於應用的卸載,咱們能夠想到是應用安裝過程的逆過程,大體要作的是:中止應用、刪除各類文件,更新系統設置、權限等,你們感興趣本身看一下,徹底是安裝過程的逆過程,這裏不介紹了。