淺析調用android的content provider(一)

        在Android下,查詢聯繫人、通話記錄等,需要用到content provider。但是,調用content provider時,Android框架內部是如何做的呢?這一系列文章就是解決這個問題的,所採用的開發環境及源碼都是基於Android 1.6版本。
  • 概述
       總的來說此問題分爲兩個步驟: 
  1. 初始化content provider。這一階段主要是參照AndroidManifest.xml,初始化content provider。注意這裏只有當包含content provider的進程運行的時候,纔會對該進程內所有的content provider進行初始化。其它provider是按需初始化的(後續文章會介紹該問題)。  
  2. 調用content provider,進行數據庫操作。這個調用通常發生在用戶定義的Activity子類的相關接口內。調用時,首先會獲取對應的content provider對象(有可能是代理對象)。然後,再調用(直接調用或者通過IBinder接口)。
       本文主要探討第一個問題:初始化content provider。
  • 應用進程的管理模型

       Android框架內,應用程序Java代碼的入口爲ActivityThread.main。用來管理不同的應用的服務名稱爲activity。它的模型大致爲:


       左邊爲多個應用進程,每個進程中有個主線程ActivityThread.main,Looper.loop()是主線程的消息循環。所有的應用進程都是通過IBinder機制和ActivityManagerService進程進行交互的。應用進程爲了能調用activity服務進程的ActivityManagerService中的方法,必須通過ActivityManagerProxy類。activity服務進程則通過ApplicationThreadProxy類與服務進程的ApplicationThread通信。ApplicationThread的作用主要是將activity服務進程的調用轉換爲ActivityThread主線程中的消息,從而保證ActivityManagerService的調用是異步的。

       右邊的activity服務進程是所有應用的服務進程,用於管理應用進程。啓動新的應用進程時,會向zygote服務進程發送socket消息。zygote接收到消息後,則會啓動新的delvik虛擬機,然後運行ActivityThread.main,啓動新的應用。

  • PackageManagerService

       PackageManagerService也是一個服務,是用來管理手機內所有的apk包的。調用它的方式和調用ActivityManagerService是一樣的,通過IBinder。它的初始化入口爲PackageManagerService.main:

    public static final IPackageManager main(Context context, boolean factoryTest) {
        PackageManagerService m = new PackageManagerService(context, factoryTest);
        ServiceManager.addService("package", m);
        return m;
    }

       main方法主要是創建一個PackageManagerService,然後註冊到ServiceManager中,名稱爲"package”。PackageManagerService的構造函數中,會去查找系統目錄和應用目錄下的apk文件,以獲取應用的包相關的信息;比如:包名稱,包含的Acvity、Provider等。

    // ……
    scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode);
    // ……
    scanDirLI(mAppInstallDir, 0, scanMode);
    // ……

       scanDirLI函數中,對於每個package,使用函數scanPackageLI解析其中的信息(此處應該是讀取AndroidManifest.xml)。scanPackageLI檢查相關信息後,又會調用另一個scanPackageLI。這個函數內部會掃描到手機內所有的Provider信息:

    PackageParser.Provider p = pkg.providers.get(i);
    p.info.processName = fixProcessName(pkg.applicationInfo.processName,
            p.info.processName, pkg.applicationInfo.uid);
    mProvidersByComponent.put(new ComponentName(p.info.packageName,
            p.info.name), p);

       mProvidersByComponent保存了所有的provider信息,這部分數據源自於manifest。每個數據包含了PackageParser.Provider、包名稱和Provider的類名。

       到這裏,我們可以看到,PackageManagerService真的是用來管理手機的應用包的。通過它可以知道所有的系統可用資源。當然這些資源只是一些靜態信息。通過這些信息,可以創建應用進程、初始化相關的Android組件。

  • 應用進程的初始化
       先看一下ActivityThread.main的實現,它創建了一個ActivityThread對象,初始化之後,進入消息循環。

    public static final void main(String[] args) {
        // ......
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        Looper.loop();

        // ......
    }

       attach函數中,回去調用ActivityManagerService.attachApplication方法。

    mgr.attachApplication(mAppThread);

       此時,會進入ActivityManagerService的進程空間,進入方法attachApplicationLocked,它會去獲取和當前客戶端應用程序關聯的Provider信息。

    List providers = generateApplicationProvidersLocked(app);

       根據上面的信息,很容易知道此處是通過PackageManagerService獲取Provider信息的。參數app表明,只是取運行在該app內的Provider。根據Android的文檔,content provider必須在對應的AndroidManifest.xml中定義。默認情況下,是運行在安裝包名稱命名的進程裏面。你也可以在android:process屬性中制定所屬的進程名稱。另一個重要的屬性android:multiprocess則可以指定provider的初始化方式,是分散在調用端進程中,從而避免進程間通信;還是隻初始化在某個進程內,各個調用端只保留provider的代理。

       隨後,通過下面的方法調用,返回到應用程序的進程空間,參數中包含了上面獲得的providers。此處的thread實際上就是應用端的ApplicationThread對象。

	thread.bindApplication(processName, app.instrumentationInfo != null
			? app.instrumentationInfo : app.info, providers,
			app.instrumentationClass, app.instrumentationProfileFile,
			app.instrumentationArguments, app.instrumentationWatcher, testMode, 
			isRestrictedBackupMode, mConfiguration, getCommonServicesLocked());

       ApplicationThread.bindApplication會發送BIND_APPLICATION消息給主線程。主線程會調用ActivityThread.handleBindApplication方法。這個函數裏面主要分兩步:一是根據需要創建Application、ApplicationContext和ApplicationContentResolver對象。因爲,有可能多個apk運行在一個進程中,那麼它們內部的組件(Component)執行的上下文(context)是不一樣的。這幾個對象實際上就是provider執行的上下文。二是根據傳遞過來的provider信息,創建provider實例,並保存在ActivityThread.mProviderMap中。具體的實例化provider過程如下(下面的代碼位於ActivityThread.handleBindApplication中):

	List<ProviderInfo> providers = data.providers;
	if (providers != null) {
		installContentProviders(app, providers);
	}

       獲取ActivityManagerService傳遞過來的provider信息,並在本進程中初始化。具體的,installContentProviders方法中,會對每個provider調用installProvider方法:

    IContentProvider cp = installProvider(context, null, cpi, false);

       installProvider方法中,主要是實例化Provider,並保存到mProviderMap中:

	final java.lang.ClassLoader cl = c.getClassLoader();
	localProvider = (ContentProvider)cl.
		loadClass(info.name).newInstance();
	provider = localProvider.getIContentProvider();
	// ......
	// Cache the pointer for the remote provider.
	String names[] = PATTERN_SEMICOLON.split(info.authority);
	for (int i=0; i<names.length; i++) {
		ProviderRecord pr = new ProviderRecord(names[i], provider,
				localProvider);
		try {
			provider.asBinder().linkToDeath(pr, 0);
			mProviderMap.put(names[i], pr);
		} catch (RemoteException e) {
			return null;
		}
	}
       上面的installContentProviders方法執行完成之後,會調用ActivityManagerService.publishContentProviders方法,將provider註冊到ActivityManagerService中,方便其它應用進程獲取。這裏面有兩個參數,一個是ApplicationThread對象,另一個是provider實例信息。

	try {
		ActivityManagerNative.getDefault().publishContentProviders(
			getApplicationThread(), results);
	} catch (RemoteException ex) {
	}

       ActivityManagerService.publishContentProviders的實現也很簡單,主要是將provider信息保存到ActivityManagerService.mProvidersByName中。具體參見源碼。
  • 總結
       本文主要介紹了Android系統內provider的初始化,Android系統默認是會初始化一些provider的,比如:ContactsProvider。它們的初始化和本文介紹的流程應該差不多,主要是在應用進程初始化時獲取provider的信息,然後實例化provider,最後將實例化的provider保存到ActivityManagerService中,供其它應用進程使用。需要說明的一點是,應用進程內可以運行多個apk中的組件。

       下一篇文章會介紹調用provider的流程。

發佈了86 篇原創文章 · 獲贊 10 · 訪問量 43萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章