Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android內核解讀-應用的安裝過程

Android內核解讀-應用的安裝過程

編輯:關於Android編程

 

前言

我們知道,在android手機上安裝一個apk很簡單,只要打開apk文件,默認就會彈出安裝界面,然後點擊確定,經過若干秒後,apk就安裝成功了,可是你知道apk的安裝過程是什麼嗎?你知道android系統在安裝一個apk的時候都干了什麼嗎?在本文中,將一一解答這個問題。簡單來說,apk的安裝過程分兩步:第一步,將apk文件復制到程序目錄下(/data/app/);第二步,為應用創建數據目錄(/data/data/package name/)、提取dex文件到指定目錄(/data/dalvik-cache/)、修改系統包管理信息。注意,本文的分析基於Android 4.3源碼。

apk的復制過程

apk的安裝從PackageManager的installApk方法開始,由於PackageManager所對應的binder服務為PackageManagerService(PMS),所以,真正的安裝過程都在PackageManagerService中完成。PackageManagerService的installApk方法最終調用了installPackageWithVerificationAndEncryption方法,該方法的核心就是在最後發送了一個INIT_COPY的消息,這個消息的含義是完成apk的拷貝過程。

    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 mPendingInstalls =
		new ArrayList();

	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方法,這個方法很長,代碼就不帖出來了,大家可以自己去看看,這裡主要分析下它的工作流程:

1. 檢查安裝位置標記位是否有沖突,如果有沖突,則安裝失敗,這裡的有沖突是指“一個apk同時要求被安裝到內部存儲和sd卡”

2. 調用MCS服務的getMinimalPackageInfo方法來得到apk的推薦安裝位置,並檢查是否能夠進行正常的安裝。在這一步,有可能拋出一些無法安裝的狀態位:存儲空間不足、程序已經安裝、無效的apk文件等,這個時候安裝過程終止

3. 到這一步,表示程序可以正常安裝,同時MCS服務服務可能會根據需要調整安裝位置,在InstallParams的installLocationPolicy中完成

4. 文件的復制過程,PMS針對內部存儲和sd卡分別提供了一個類:FileInstallArgs和AsecInstallArgs,並分別調用二者的copyApk方法來完成apk的復制過程

經過了上面4步,待安裝apk已經被復制到了/data/app/目錄了。

apk的解析過程

上面,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中存放的是所有應用的設置應用,比如一個應用聲明了哪些系統權限就定義在這裡面。關於應用的卸載,我們可以想到是應用安裝過程的逆過程,大致要做的是:停止應用、刪除各種文件,更新系統設置、權限等,大家感興趣自己看一下,完全是安裝過程的逆過程,這裡不介紹了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved