posts - 101,  comments - 29,  trackbacks - 0

                  通過前面的學習,我們知道在Android系統中,Content Provider可以為不同的應用程序訪問相同的數據提供統一的入口。Content Provider一般是運行在獨立的進程中的,每一個Content Provider在系統中只有一個實例存在,其它應用程序首先要找到這個實例,然后才能訪問它的數據。那么,系統中的Content Provider實例是由誰來負責啟動的呢?本文將回答這個問題。

                  Content Provider和應用程序組件Activity、Service一樣,需要在AndroidManifest.xml文件中配置之后才能使用。系統在安裝包含Content Provider的應用程序的時候,會把這些Content Provider的描述信息保存起來,其中最重要的就是Content Provider的Authority信息,Android應用程序的安裝過程具體可以參考Android應用程序安裝過程源代碼分析一文。注意,安裝應用程序的時候,并不會把相應的Content Provider加載到內存中來,系統采取的是懶加載的機制,等到第一次要使用這個Content Provider的時候,系統才會把它加載到內存中來,下次再要使用這個Content Provider的時候,就可以直接返回了。

                  本文以前面一篇文章Android應用程序組件Content Provider應用實例中的例子來詳細分析Content Provider的啟動過程。Android應用程序組件Content Provider應用實例這篇文章介紹的應用程序Article中,第一次使用ArticlesProvider這個Content Provider的地方是ArticlesAdapter類的getArticleCount函數,因為MainActivity要在ListView中顯示文章信息列表時, 首先要知道ArticlesProvider中的文章信息的數量。從ArticlesAdapter類的getArticleCount函數調用開始,一直到ArticlesProvider類的onCreate函數被調用,就是ArticlesProvider的完整啟動過程,下面我們就先看看這個過程的序列圖,然后再詳細分析每一個步驟:


                  Step 1. ArticlesAdapter.getArticleCount

                  這個函數定義在前面一篇文章Android應用程序組件Content Provider應用實例介紹的應用程序Artilce源代碼工程目錄下,在文件為packages/experimental/Article/src/shy/luo/article/ArticlesAdapter.java中:

          public class ArticlesAdapter {
          	......
          
          	private ContentResolver resolver = null;
          
          	public ArticlesAdapter(Context context) {
          		resolver = context.getContentResolver();
          	}
          
          	......
          
          	public int getArticleCount() {
          		int count = 0;
          
          		try {
          			IContentProvider provider = resolver.acquireProvider(Articles.CONTENT_URI);
          			Bundle bundle = provider.call(Articles.METHOD_GET_ITEM_COUNT, null, null);
          			count = bundle.getInt(Articles.KEY_ITEM_COUNT, 0);
          		} catch(RemoteException e) {
          			e.printStackTrace();
          		}
          
          		return count;
          	}
          
          	......
          }
                   這個函數通過應用程序上下文的ContentResolver接口resolver的acquireProvider函數來獲得與Articles.CONTENT_URI對應的Content Provider對象的IContentProvider接口。常量Articles.CONTENT_URI是在應用程序ArticlesProvider中定義的,它的值為“content://shy.luo.providers.articles/item”,對應的Content Provider就是ArticlesProvider了。

                   Step 2. ContentResolver.acqireProvider

                   這個函數定義在frameworks/base/core/java/android/content/ContentResolver.java文件中:

          public abstract class ContentResolver {
          	......
          
          	public final IContentProvider acquireProvider(Uri uri) {
          		if (!SCHEME_CONTENT.equals(uri.getScheme())) {
          			return null;
          		}
          		String auth = uri.getAuthority();
          		if (auth != null) {
          			return acquireProvider(mContext, uri.getAuthority());
          		}
          		return null;
          	}
          
          	......
          }
                  函數首先驗證參數uri的scheme是否正確,即是否是以content://開頭,然后取出它的authority部分,最后調用另外一個成員函數acquireProvider執行獲取ContentProvider接口的操作。在我們這個情景中,參數uri的authority的內容便是“shy.luo.providers.articles”了。

                  從ContentResolver類的定義我們可以看出,它是一個抽象類,兩個參數版本的acquireProvider函數是由它的子類來實現的。回到Step 1中,這個ContentResolver接口是通過應用程序上下文Context對象的getContentResolver函數來獲得的,而應用程序上下文Context是由ContextImpl類來實現的,它定義在frameworks/base/core/java/android/app/ContextImpl.java文件中:

          class ContextImpl extends Context {
          	......
          
          	private ApplicationContentResolver mContentResolver;
          
          	......
          
          	final void init(LoadedApk packageInfo,
          			IBinder activityToken, ActivityThread mainThread,
          			Resources container) {
          		......
          
          		mContentResolver = new ApplicationContentResolver(this, mainThread);
          
          		......
          	}
          
          	......
          
          	@Override
          	public ContentResolver getContentResolver() {
          		return mContentResolver;
          	}
          
          	......
          }
                   ContextImpl類的init函數是在應用程序啟動的時候調用的,具體可以參考Android應用程序啟動過程源代碼分析一文中的Step 34。

                   因此,在上面的ContentResolver類的acquireProvider函數里面接下來要調用的ApplicationContentResolver類的acquireProvider函數。

                   Step 3. ApplicationContentResolve.acquireProvider

                   這個函數定義在frameworks/base/core/java/android/app/ContextImpl.java文件中:

          class ContextImpl extends Context {
          	......
          
          	private static final class ApplicationContentResolver extends ContentResolver {
          		......
          
          		@Override
          		protected IContentProvider acquireProvider(Context context, String name) {
          			return mMainThread.acquireProvider(context, name);
          		}
          
          		......
          
          		private final ActivityThread mMainThread;
          	}
          
          	......
          }
                   它調用ActivityThread類的acquireProvider函數進一步執行獲取Content Provider接口的操作。

                   Step 4. ActivityThread.acquireProvider

                   這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

          public final class ActivityThread {
          	......
          
          	public final IContentProvider acquireProvider(Context c, String name) {
          		IContentProvider provider = getProvider(c, name);
          		if(provider == null)
          			return null;
          		......
          		return provider;
          	}
          
          	......
          }
                   它又是調用了另外一個成員函數getProvider來進一步執行獲取Content Provider接口的操作。

                   Step 5. ActivityThread.getProvider

                   這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

          public final class ActivityThread {
          	......
          
          	private final IContentProvider getExistingProvider(Context context, String name) {
          		synchronized(mProviderMap) {
          			final ProviderClientRecord pr = mProviderMap.get(name);
          			if (pr != null) {
          				return pr.mProvider;
          			}
          			return null;
          		}
          	}
          
          	......
          
          	private final IContentProvider getProvider(Context context, String name) {
          		IContentProvider existing = getExistingProvider(context, name);
          		if (existing != null) {
          			return existing;
          		}
          
          		IActivityManager.ContentProviderHolder holder = null;
          		try {
          			holder = ActivityManagerNative.getDefault().getContentProvider(
          				getApplicationThread(), name);
          		} catch (RemoteException ex) {
          		}
          
          		IContentProvider prov = installProvider(context, holder.provider,
          			holder.info, true);
          
          		......
          
          		return prov;
          	}
          
          	......
          }

                   這個函數首先會通過getExistingProvider函數來檢查本地是否已經存在這個要獲取的ContentProvider接口,如果存在,就直接返回了。本地已經存在的ContextProvider接口保存在ActivityThread類的mProviderMap成員變量中,以ContentProvider對應的URI的authority為鍵值保存。在我們這個情景中,因為是第一次調用ArticlesProvider接口,因此,這時候通過getExistingProvider函數得到的IContentProvider接口為null,于是下面就會調用ActivityManagerService服務的getContentProvider接口來獲取一個ContentProviderHolder對象holder,這個對象就包含了我們所要獲取的ArticlesProvider接口,在將這個接口返回給調用者之后,還會調用installProvider函數來把這個接口保存在本地中,以便下次要使用這個ContentProvider接口時,直接就可以通過getExistingProvider函數獲取了。

                  我們先進入到ActivityManagerService服務的getContentProvider函數中看看它是如何獲取我們所需要的ArticlesProvider接口的,然后再返回來看看installProvider函數的實現。

                  Step 6. ActivityManagerService.getContentProvider

                  這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

          public final class ActivityManagerService extends ActivityManagerNative
          		implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
          	......
          
          	public final ContentProviderHolder getContentProvider(
          			IApplicationThread caller, String name) {
          		......
          
          		return getContentProviderImpl(caller, name);
          	}
          
          	......
          }
                  它調用getContentProviderImpl函數來進一步執行操作。

                  Step 7. ActivityManagerService.getContentProviderImpl

                  這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

          public final class ActivityManagerService extends ActivityManagerNative
          		implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
          	......
          
          	private final ContentProviderHolder getContentProviderImpl(
          			IApplicationThread caller, String name) {
          		ContentProviderRecord cpr;
          		ProviderInfo cpi = null;
          
          		synchronized(this) {
          			ProcessRecord r = null;
          			if (caller != null) {
          				r = getRecordForAppLocked(caller);
          				......
          			}
          
          			// First check if this content provider has been published...
          			cpr = mProvidersByName.get(name);
          			if (cpr != null) {
          				......
          			} else {
          				try {
          					cpi = AppGlobals.getPackageManager().
          						resolveContentProvider(name,
          						STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
          				} catch (RemoteException ex) {
          				}
          				......
          			}
          
          			cpr = mProvidersByClass.get(cpi.name);
          			final boolean firstClass = cpr == null;
          			if (firstClass) {
          				try {
          					ApplicationInfo ai =
          						AppGlobals.getPackageManager().
          						getApplicationInfo(
          						cpi.applicationInfo.packageName,
          						STOCK_PM_FLAGS);
          					......
          					cpr = new ContentProviderRecord(cpi, ai);
          				} catch (RemoteException ex) {
          					// pm is in same process, this will never happen.
          				}
          			}
          
          			if (r != null && cpr.canRunHere(r)) {
          				// If this is a multiprocess provider, then just return its
          				// info and allow the caller to instantiate it.  Only do
          				// this if the provider is the same user as the caller's
          				// process, or can run as root (so can be in any process).
          				return cpr;
          			}
          
          			......
          
          			// This is single process, and our app is now connecting to it.
          			// See if we are already in the process of launching this
          			// provider.
          			final int N = mLaunchingProviders.size();
          			int i;
          			for (i=0; i<N; i++) {
          				if (mLaunchingProviders.get(i) == cpr) {
          					break;
          				}
          			}
          
          			// If the provider is not already being launched, then get it
          			// started.
          			if (i >= N) {
          				final long origId = Binder.clearCallingIdentity();
          				ProcessRecord proc = startProcessLocked(cpi.processName,
          					cpr.appInfo, false, 0, "content provider",
          					new ComponentName(cpi.applicationInfo.packageName,
          					cpi.name), false);
          				......
          				mLaunchingProviders.add(cpr);
          				......
          			}
          
          			// Make sure the provider is published (the same provider class
          			// may be published under multiple names).
          			if (firstClass) {
          				mProvidersByClass.put(cpi.name, cpr);
          			}
          			cpr.launchingApp = proc;
          			mProvidersByName.put(name, cpr);
          
          			......
          		}
          
          		// Wait for the provider to be published...
          		synchronized (cpr) {
          			while (cpr.provider == null) {
          				......
          				try {
          					cpr.wait();
          				} catch (InterruptedException ex) {
          				}
          			}
          		}
          
          		return cpr;
          	}
          	
          	......
          }
                  這個函數比較長,我們一步一步地分析。
                  函數首先是獲取調用者的進程記錄塊信息:

          ProcessRecord r = null;
          if (caller != null) {
          	r = getRecordForAppLocked(caller);
          	......
          }
                  在我們這個情景中,要獲取的就是應用程序Article的進程記錄塊信息了,后面會用到。

                  在ActivityManagerService中,有兩個成員變量是用來保存系統中的Content Provider信息的,一個是mProvidersByName,一個是mProvidersByClass,前者是以Content Provider的authoriry值為鍵值來保存的,后者是以Content Provider的類名為鍵值來保存的。一個Content Provider可以有多個authority,而只有一個類來和它對應,因此,這里要用兩個Map來保存,這里為了方便根據不同條件來快速查找而設計的。下面的代碼就是用來檢查要獲取的Content Provider是否已經加存在的了:

          // First check if this content provider has been published...
          cpr = mProvidersByName.get(name);
          if (cpr != null) {
          	......
          } else {
          	try {
          		cpi = AppGlobals.getPackageManager().
          			resolveContentProvider(name,
          			STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
          	} catch (RemoteException ex) {
          	}
          	......
          }
          
          cpr = mProvidersByClass.get(cpi.name);
          final boolean firstClass = cpr == null;
          if (firstClass) {
          	try {
          		ApplicationInfo ai =
          			AppGlobals.getPackageManager().
          			getApplicationInfo(
          			cpi.applicationInfo.packageName,
          			STOCK_PM_FLAGS);
          		......
          		cpr = new ContentProviderRecord(cpi, ai);
          	} catch (RemoteException ex) {
          		// pm is in same process, this will never happen.
          	}
          }
                  在我們這個情景中,由于是第一次調用ArticlesProvider接口,因此,在mProvidersByName和mProvidersByClass兩個Map中都不存在ArticlesProvider的相關信息,因此,這里會通過AppGlobals.getPackageManager函數來獲得PackageManagerService服務接口,然后分別通過它的resolveContentProvider和getApplicationInfo函數來分別獲取ArticlesProvider應用程序的相關信息,分別保存在cpi和cpr這兩個本地變量中。這些信息都是在安裝應用程序的過程中保存下來的,具體可以參考Android應用程序安裝過程源代碼分析一文。

                  接下去這個代碼判斷當前要獲取的Content Provider是否允許在客戶進程中加載,即查看一個這個Content Provider否配置了multiprocess屬性為true,如果允許在客戶進程中加載,就直接返回了這個Content Provider的信息了:

          if (r != null && cpr.canRunHere(r)) {
          	// If this is a multiprocess provider, then just return its
          	// info and allow the caller to instantiate it.  Only do
          	// this if the provider is the same user as the caller's
          	// process, or can run as root (so can be in any process).
          	return cpr;
          }
                  在我們這個情景中,要獲取的ArticlesProvider設置了要在獨立的進程中運行,因此,繼續往下執行:

          // This is single process, and our app is now connecting to it.
          // See if we are already in the process of launching this
          // provider.
          final int N = mLaunchingProviders.size();
          int i;
          for (i=0; i<N; i++) {
          	if (mLaunchingProviders.get(i) == cpr) {
          		break;
          	}
          }
                  系統中所有正在加載的Content Provider都保存在mLaunchingProviders成員變量中。在加載相應的Content Provider之前,首先要判斷一下它是可否正在被其它應用程序加載,如果是的話,就不用重復加載了。在我們這個情景中,沒有其它應用程序也正在加載ArticlesProvider這個Content Provider,繼續往前執行:

          // If the provider is not already being launched, then get it
          // started.
          if (i >= N) {
          	final long origId = Binder.clearCallingIdentity();
          	ProcessRecord proc = startProcessLocked(cpi.processName,
          		cpr.appInfo, false, 0, "content provider",
          		new ComponentName(cpi.applicationInfo.packageName,
          		cpi.name), false);
          	......
          	mLaunchingProviders.add(cpr);
          	......
          }
                  這里的條件i >= N為true,就表明沒有其它應用程序正在加載這個Content Provider,因此,就要調用startProcessLocked函數來啟動一個新的進程來加載這個Content Provider對應的類了,然后把這個正在加載的信息增加到mLaunchingProviders中去。我們先接著分析這個函數,然后再來看在新進程中加載Content Provider的過程,繼續往下執行:

          // Make sure the provider is published (the same provider class
          // may be published under multiple names).
          if (firstClass) {
          	mProvidersByClass.put(cpi.name, cpr);
          }
          cpr.launchingApp = proc;
          mProvidersByName.put(name, cpr);
                  這段代碼把這個Content Provider的信息分別保存到mProvidersByName和mProviderByCalss兩個Map中去,以方便后續查詢。

                  因為我們需要獲取的Content Provider是在新的進程中加載的,而getContentProviderImpl這個函數是在系統進程中執行的,它必須要等到要獲取的Content Provider是在新的進程中加載完成后才能返回,這樣就涉及到進程同步的問題了。這里使用的同步方法是不斷地去檢查變量cpr的provider域是否被設置了。當要獲取的Content Provider在新的進程加載完成之后,它會通過Binder進程間通信機制調用到系統進程中,把這個cpr變量的provider域設置為已經加載好的Content Provider接口,這時候,函數getContentProviderImpl就可以返回了。下面的代碼就是用來等待要獲取的Content Provider是在新的進程中加載完成的:

          // Wait for the provider to be published...
          synchronized (cpr) {
          	while (cpr.provider == null) {
          		......
          		try {
          			cpr.wait();
          		} catch (InterruptedException ex) {
          		}
          	}
          }
                  下面我們再分析在新進程中加載ArticlesProvider這個Content Provider的過程。

                   Step 8. ActivityManagerService.startProcessLocked

                   Step 9. Process.start

                   Step 10. ActivityThread.main

                   Step 11. ActivityThread.attach

                   Step 12. ActivityManagerService.attachApplication

                   這五步是標準的Android應用程序啟動步驟,具體可以參考Android應用程序啟動過程源代碼分析一文中的Step 23到Step 27,或者Android系統在新進程中啟動自定義服務過程(startService)的原理分析一文中的Step 4到Step 9,這里就不再詳細描述了。

                   Step 13. ActivityManagerService.attachApplicationLocked

                   這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

          public final class ActivityManagerService extends ActivityManagerNative
          		implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
          	......
          
          	private final boolean attachApplicationLocked(IApplicationThread thread,
          			int pid) {
          		// Find the application record that is being attached...  either via
          		// the pid if we are running in multiple processes, or just pull the
          		// next app record if we are emulating process with anonymous threads.
          		ProcessRecord app;
          		if (pid != MY_PID && pid >= 0) {
          			synchronized (mPidsSelfLocked) {
          				app = mPidsSelfLocked.get(pid);
          			}
          		} else if (mStartingProcesses.size() > 0) {
          			......
          		} else {
          			......
          		}
          
          		......
          
          		app.thread = thread;
          		app.curAdj = app.setAdj = -100;
          		app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
          		app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
          		app.forcingToForeground = null;
          		app.foregroundServices = false;
          		app.debugging = false;
          
          		......
          
          		boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
          		List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
          
          		try {
          			......
          
          			thread.bindApplication(processName, app.instrumentationInfo != null
          				? app.instrumentationInfo : app.info, providers,
          				app.instrumentationClass, app.instrumentationProfileFile,
          				app.instrumentationArguments, app.instrumentationWatcher, testMode,
          				isRestrictedBackupMode || !normalMode,
          				mConfiguration, getCommonServicesLocked());
          
          			......
          		} catch (Exception e) {
          			......
          		}
          
          		......
          		
          		return true;
          	}
          
          	......
          
          	private final List generateApplicationProvidersLocked(ProcessRecord app) {
          		List providers = null;
          		try {
          			providers = AppGlobals.getPackageManager().
          				queryContentProviders(app.processName, app.info.uid,
          				STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS);
          		} catch (RemoteException ex) {
          		}
          		if (providers != null) {
          			final int N = providers.size();
          			for (int i=0; i<N; i++) {
          				ProviderInfo cpi =
          					(ProviderInfo)providers.get(i);
          				ContentProviderRecord cpr = mProvidersByClass.get(cpi.name);
          				if (cpr == null) {
          					cpr = new ContentProviderRecord(cpi, app.info);
          					mProvidersByClass.put(cpi.name, cpr);
          				}
          				app.pubProviders.put(cpi.name, cpr);
          				app.addPackage(cpi.applicationInfo.packageName);
          				ensurePackageDexOpt(cpi.applicationInfo.packageName);
          			}
          		}
          		return providers;
          	}
          
          	......
          }
                  這個函數首先是根據傳進來的進程ID找到相應的進程記錄塊,注意,這個進程ID是應用程序ArticlesProvider的ID,然后對這個進程記錄塊做一些初傾始化的工作。再接下來通過調用generateApplicationProvidersLocked獲得需要在這個過程中加載的Content Provider列表,在我們這個情景中,就只有ArticlesProvider這個Content Provider了。最后調用從參數傳進來的IApplicationThread對象thread的bindApplication函數來執行一些應用程序初始化工作。從Android應用程序啟動過程源代碼分析一文中我們知道,在Android系統中,每一個應用程序進程都加載了一個ActivityThread實例,在這個ActivityThread實例里面,有一個成員變量mAppThread,它是一個Binder對象,類型為ApplicationThread,實現了IApplicationThread接口,它是專門用來和ActivityManagerService服務進行通信的。因此,調用下面語句:

          thread.bindApplication(processName, app.instrumentationInfo != null
          	? app.instrumentationInfo : app.info, providers,
          	app.instrumentationClass, app.instrumentationProfileFile,
          	app.instrumentationArguments, app.instrumentationWatcher, testMode,
          	isRestrictedBackupMode || !normalMode,
          	mConfiguration, getCommonServicesLocked());
                  就會進入到應用程序ArticlesProvider進程中的ApplicationThread對象的bindApplication函數中去。在我們這個情景場,這個函數調用中最重要的參數便是第三個參數providers了,它是我們要處理的對象。

                  Step 14. ApplicationThread.bindApplication

                  這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

          public final class ActivityThread {
          	......
          
          	private final class ApplicationThread extends ApplicationThreadNative {
          		......
          
          		public final void bindApplication(String processName,
          				ApplicationInfo appInfo, List<ProviderInfo> providers,
          				ComponentName instrumentationName, String profileFile,
          				Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
          				int debugMode, boolean isRestrictedBackupMode, Configuration config,
          				Map<String, IBinder> services) {
          			if (services != null) {
          				// Setup the service cache in the ServiceManager
          				ServiceManager.initServiceCache(services);
          			}
          
          			AppBindData data = new AppBindData();
          			data.processName = processName;
          			data.appInfo = appInfo;
          			data.providers = providers;
          			data.instrumentationName = instrumentationName;
          			data.profileFile = profileFile;
          			data.instrumentationArgs = instrumentationArgs;
          			data.instrumentationWatcher = instrumentationWatcher;
          			data.debugMode = debugMode;
          			data.restrictedBackupMode = isRestrictedBackupMode;
          			data.config = config;
          			queueOrSendMessage(H.BIND_APPLICATION, data);
          		}
          
          		......
          	}
          
          	......
          }
                   這個函數把相關的信息都封裝成一個AppBindData對象,然后以一個消息的形式發送到主線程的消息隊列中去等等待處理。這個消息最終是是在ActivityThread類的handleBindApplication函數中進行處理的。

                   Step 15. ActivityThread.handleBindApplication

                   這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

          public final class ActivityThread {
          	......
          	
          	private final void handleBindApplication(AppBindData data) {
          		......
          
          		List<ProviderInfo> providers = data.providers;
          		if (providers != null) {
          			installContentProviders(app, providers);
          			......
          		}
          
          		......
          	}
          
          	......
          }
                   這個函數的內容比較多,我們忽略了其它無關的部分,只關注和Content Provider有關的邏輯,這里主要就是調用installContentProviders函數來在本地安裝Content Providers信息。

                   Step 16. ActivityThread.installContentProviders

                   這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

          public final class ActivityThread {
          	......
          	
          	private final void installContentProviders(
          			Context context, List<ProviderInfo> providers) {
          		final ArrayList<IActivityManager.ContentProviderHolder> results =
          			new ArrayList<IActivityManager.ContentProviderHolder>();
          
          		Iterator<ProviderInfo> i = providers.iterator();
          		while (i.hasNext()) {
          			ProviderInfo cpi = i.next();
          			StringBuilder buf = new StringBuilder(128);
          			buf.append("Pub ");
          			buf.append(cpi.authority);
          			buf.append(": ");
          			buf.append(cpi.name);
          			Log.i(TAG, buf.toString());
          			IContentProvider cp = installProvider(context, null, cpi, false);
          			if (cp != null) {
          				IActivityManager.ContentProviderHolder cph =
          					new IActivityManager.ContentProviderHolder(cpi);
          				cph.provider = cp;
          				results.add(cph);
          				// Don't ever unload this provider from the process.
          				synchronized(mProviderMap) {
          					mProviderRefCountMap.put(cp.asBinder(), new ProviderRefCount(10000));
          				}
          			}
          		}
          
          		try {
          			ActivityManagerNative.getDefault().publishContentProviders(
          				getApplicationThread(), results);
          		} catch (RemoteException ex) {
          		}
          	}
          
          	......
          }
                   這個函數主要是做了兩件事情,一是調用installProvider來在本地安裝每一個Content Proivder的信息,并且為每一個Content Provider創建一個ContentProviderHolder對象來保存相關的信息。ContentProviderHolder對象是一個Binder對象,是用來把Content Provider的信息傳遞給ActivityManagerService服務的。當這些Content Provider都處理好了以后,還要調用ActivityManagerService服務的publishContentProviders函數來通知ActivityManagerService服務,這個進程中所要加載的Content Provider,都已經準備完畢了,而ActivityManagerService服務的publishContentProviders函數的作用就是用來喚醒在前面Step 7等待的線程的了。我們先來看installProvider的實現,然后再來看ActivityManagerService服務的publishContentProviders函數的實現。

                  Step 17. ActivityThread.installProvider

                  這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

          public final class ActivityThread {
          	......
          	
          	private final IContentProvider installProvider(Context context,
          			IContentProvider provider, ProviderInfo info, boolean noisy) {
          		ContentProvider localProvider = null;
          		if (provider == null) {
          			......
          
          			Context c = null;
          			ApplicationInfo ai = info.applicationInfo;
          			if (context.getPackageName().equals(ai.packageName)) {
          				c = context;
          			} else if (mInitialApplication != null &&
          				mInitialApplication.getPackageName().equals(ai.packageName)) {
          					c = mInitialApplication;
          			} else {
          				try {
          					c = context.createPackageContext(ai.packageName,
          						Context.CONTEXT_INCLUDE_CODE);
          				} catch (PackageManager.NameNotFoundException e) {
          				}
          			}
          
          			......
          
          			try {
          				final java.lang.ClassLoader cl = c.getClassLoader();
          				localProvider = (ContentProvider)cl.
          					loadClass(info.name).newInstance();
          				provider = localProvider.getIContentProvider();
          				......
          
          				// XXX Need to create the correct context for this provider.
          				localProvider.attachInfo(c, info);
          			} catch (java.lang.Exception e) {
          				......
          			}
          
          		} else if (localLOGV) {
          			......
          		}
          
          		synchronized (mProviderMap) {
          			// Cache the pointer for the remote provider.
          			String names[] = PATTERN_SEMICOLON.split(info.authority);
          			for (int i=0; i<names.length; i++) {
          				ProviderClientRecord pr = new ProviderClientRecord(names[i], provider,
          					localProvider);
          				try {
          					provider.asBinder().linkToDeath(pr, 0);
          					mProviderMap.put(names[i], pr);
          				} catch (RemoteException e) {
          					return null;
          				}
          			}
          			if (localProvider != null) {
          				mLocalProviders.put(provider.asBinder(),
          					new ProviderClientRecord(null, provider, localProvider));
          			}
          		}
          
          		return provider;
          	}
          
          	......
          }
                   這個函數的作用主要就是在應用程序進程中把相應的Content Provider類加載進來了,在我們這個種情景中,就是要在ArticlesProvider這個應用程序中把ArticlesProvider這個Content Provider類加載到內存中來了:

          final java.lang.ClassLoader cl = c.getClassLoader();
          localProvider = (ContentProvider)cl.
          	loadClass(info.name).newInstance();
          
                   接著通過調用localProvider的getIContentProvider函數來獲得一個Binder對象,這個Binder對象返回給installContentProviders函數之后,就會傳到ActivityManagerService中去,后續其它應用程序就是通過獲得這個Binder對象來和相應的Content Provider進行通信的了。我們先看一下這個函數的實現,然后再回到installProvider函數中繼續分析。

                   Step 18. ContentProvider.getIContentProvider

                   這個函數定義在frameworks/base/core/java/android/content/ContentProvider.java文件中:

          public abstract class ContentProvider implements ComponentCallbacks {
          	......
          
          	private Transport mTransport = new Transport();
          
          	......
          
          	class Transport extends ContentProviderNative {
          		......
          	}
          
          	public IContentProvider getIContentProvider() {
          		return mTransport;
          	}
          
          	......
          }
                  從這里我們可以看出,ContentProvider類和Transport類的關系就類似于ActivityThread和ApplicationThread的關系,其它應用程序不是直接調用ContentProvider接口來訪問它的數據,而是通過調用它的內部對象mTransport來間接調用ContentProvider的接口,這一點我們在下一篇文章中分析調用Content Provider接口來獲取共享數據時將會看到。
                  回到前面的installProvider函數中,它接下來調用下面接口來初始化剛剛加載好的Content Provider:

          // XXX Need to create the correct context for this provider.
          localProvider.attachInfo(c, info);
                  同樣,我們先進入到ContentProvider類的attachInfo函數去看看它的實現,然后再回到installProvider函數來。

                  Step 19. ContentProvider.attachInfo

                  這個函數定義在frameworks/base/core/java/android/content/ContentProvider.java文件中:

          public abstract class ContentProvider implements ComponentCallbacks {
          	......
          
          	public void attachInfo(Context context, ProviderInfo info) {
          		/*
          		* Only allow it to be set once, so after the content service gives
          		* this to us clients can't change it.
          		*/
          		if (mContext == null) {
          			mContext = context;
          			mMyUid = Process.myUid();
          			if (info != null) {
          				setReadPermission(info.readPermission);
          				setWritePermission(info.writePermission);
          				setPathPermissions(info.pathPermissions);
          				mExported = info.exported;
          			}
          			ContentProvider.this.onCreate();
          		}
          	}
          
          	......
          }
                  這個函數很簡單,主要就是根據這個Content Provider的信息info來設置相應的讀寫權限,然后調用它的子類的onCreate函數來讓子類執行一些初始化的工作。在我們這個情景中,這個子類就是ArticlesProvide應用程序中的ArticlesProvider類了。

                  Step 20. ArticlesProvider.onCreate

                  這個函數定義在前面一篇文章Android應用程序組件Content Provider應用實例介紹的應用程序ArtilcesProvider源代碼工程目錄下,在文件為packages/experimental/ArticlesProvider/src/shy/luo/providers/articles/ArticlesProvider.java中:

          public class ArticlesProvider extends ContentProvider {
          	......
          
          	@Override
          	public boolean onCreate() {
          		Context context = getContext();
          		resolver = context.getContentResolver();
          		dbHelper = new DBHelper(context, DB_NAME, null, DB_VERSION);
          
          		return true;
          	}
          
          	......
          }
                  這個函數主要執行一些簡單的工作,例如,獲得應用程序上下文的ContentResolver接口和創建數據庫操作輔助對象,具體可以參考前面一篇文章Android應用程序組件Content Provider應用實例

                  回到前面Step 17中的installProvider函數中,它接下來就是把這些在本地中加載的Content Provider信息保存下來了,以方便后面查詢和使用:

          synchronized (mProviderMap) {
              // Cache the pointer for the remote provider.
              String names[] = PATTERN_SEMICOLON.split(info.authority);
              for (int i=0; i<names.length; i++) {
          	ProviderClientRecord pr = new ProviderClientRecord(names[i], provider,
          		localProvider);
          	try {
          		provider.asBinder().linkToDeath(pr, 0);
          		mProviderMap.put(names[i], pr);
          	} catch (RemoteException e) {
          		return null;
          	}
              }
              if (localProvider != null) {
          	mLocalProviders.put(provider.asBinder(),
          		new ProviderClientRecord(null, provider, localProvider));
              }
          }
                  和ActivityMangerService類似,在ActivityThread中,以Content Provider的authority為鍵值來把這個Content Provider的信息保存在mProviderMap成員變量中,因為一個Content Provider可以對應多個authority,因此這里用一個for循環來處理,同時又以這個Content Provider對應的Binder對象provider來鍵值來把這個Content Provider的信息保存在mLocalProviders成員變量中,表明這是一個在本地加載的Content Provider。

                  函數installProvider執行完成以后,返回到Step 16中的instalContentProviders函數中,執行下面語句:

          try {
          	ActivityManagerNative.getDefault().publishContentProviders(
          		getApplicationThread(), results);
          } catch (RemoteException ex) {
          }
                  前面已經提到,這個函數調用的作用就是通知ActivityMangerService,需要在這個進程中加載的Content Provider已經完加載完成了,參數results就包含了這些已經加載好的Content Provider接口。

                  Step 21. ActivityMangerService.publishContentProviders

                  這個函數定義在frameworks/base/services/java/com/android/server/am/ActivityManagerService.java文件中:

          public final class ActivityManagerService extends ActivityManagerNative
          		implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
          	......
          
          	public final void publishContentProviders(IApplicationThread caller,
          			List<ContentProviderHolder> providers) {
          		......
          
          		synchronized(this) {
          			final ProcessRecord r = getRecordForAppLocked(caller);
          			......
          
          			final int N = providers.size();
          			for (int i=0; i<N; i++) {
          				ContentProviderHolder src = providers.get(i);
          				if (src == null || src.info == null || src.provider == null) {
          					continue;
          				}
          				ContentProviderRecord dst = r.pubProviders.get(src.info.name);
          				if (dst != null) {
          					mProvidersByClass.put(dst.info.name, dst);
          					String names[] = dst.info.authority.split(";");
          					for (int j = 0; j < names.length; j++) {
          						mProvidersByName.put(names[j], dst);
          					}
          
          					int NL = mLaunchingProviders.size();
          					int j;
          					for (j=0; j<NL; j++) {
          						if (mLaunchingProviders.get(j) == dst) {
          							mLaunchingProviders.remove(j);
          							j--;
          							NL--;
          						}
          					}
          					synchronized (dst) {
          						dst.provider = src.provider;
          						dst.app = r;
          						dst.notifyAll();
          					}
          					......
          				}
          			}
          		}
          	}
          
          	......
          }
                   在我們這個情景中,只有一個Content Provider,因此,這里的N等待1。在中間的for循環里面,最重要的是下面這個語句:

          ContentProviderRecord dst = r.pubProviders.get(src.info.name);
                   從這里得到的ContentProviderRecord對象dst,就是在前面Step 7中創建的ContentProviderRecord對象cpr了。在for循環中,首先是把這個Content Provider信息保存好在mProvidersByClass和mProvidersByName中:

          mProvidersByClass.put(dst.info.name, dst);
          String names[] = dst.info.authority.split(";");
          for (int j = 0; j < names.length; j++) {
          	mProvidersByName.put(names[j], dst);
          }
                  前面已經說過,這兩個Map中,一個是以類名為鍵值保存Content Provider信息,一個是以authority為鍵值保存Content Provider信息。

                  因為這個Content Provider已經加載好了,因此,把它從mLaunchingProviders列表中刪除:

          int NL = mLaunchingProviders.size();
          int j;
          for (j=0; j<NL; j++) {
          	if (mLaunchingProviders.get(j) == dst) {
          		mLaunchingProviders.remove(j);
          		j--;
          		NL--;
          	}
          }
                  最后,設置這個ContentProviderRecord對象dst的provider域為從參數傳進來的Content Provider遠程接口:
          synchronized (dst) {
          	dst.provider = src.provider;
          	dst.app = r;
          	dst.notifyAll();
          }
                  執行了dst.notiryAll語句后,在Step 7中等待要獲取的Content Provider接口加載完畢的線程就被喚醒了。喚醒之后,它檢查本地ContentProviderRecord變量cpr的provider域不為null,于是就返回了。它最終返回到Step 5中的ActivityThread類的getProvider函數中,繼續往下執行:

          IContentProvider prov = installProvider(context, holder.provider,
          	holder.info, true);
                  注意,這里是在Article應用程序中進程中執行installProvider函數的,而前面的Step 17的installProvider函數是在ArticlesProvider應用程序進程中執行的。

                  Step 22. ActivityThread.installProvider

                  這個函數定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

          public final class ActivityThread {
          	......
          	
          	private final IContentProvider installProvider(Context context,
          			IContentProvider provider, ProviderInfo info, boolean noisy) {
          		......
          		if (provider == null) {
          			......
          		} else if (localLOGV) {
          			......
          		}
          
          		synchronized (mProviderMap) {
          			// Cache the pointer for the remote provider.
          			String names[] = PATTERN_SEMICOLON.split(info.authority);
          			for (int i=0; i<names.length; i++) {
          				ProviderClientRecord pr = new ProviderClientRecord(names[i], provider,
          					localProvider);
          				try {
          					provider.asBinder().linkToDeath(pr, 0);
          					mProviderMap.put(names[i], pr);
          				} catch (RemoteException e) {
          					return null;
          				}
          			}
          			......
          		}
          
          		return provider;
          	}
          
          	......
          }
                  同樣是執行installProvider函數,與Step 17不同,這里傳進來的參數provider是不為null的,因此,它不需要執行在本地加載Content Provider的工作,只需要把從ActivityMangerService中獲得的Content Provider接口保存在成員變量mProviderMap中就可以了。

                  這樣,獲取與"shy.luo.providers.artilces"這個uri對應的Content Provider(shy.luo.providers.articles.ArticlesProvider)就完成了,它同時也是啟動Content Provider的完整過程。第三方應用程序獲得了這個Content Provider的接口之后,就可以訪問它里面的共享數據了。在下面一篇文章中,我們將重點分析Android應用程序組件Content Provider在不同進程中傳輸數據的過程,即Content Provider在不同應用程序中共享數據的原理,敬請關注。

          作者:Luoshengyang 發表于2011-11-28 0:58:59 原文鏈接
          閱讀:3733 評論:25 查看評論
          posted on 2012-04-17 21:32 mixer-a 閱讀(1222) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 连南| 昭通市| 阿拉善右旗| 买车| 玉树县| 孟村| 石屏县| 广宗县| 临湘市| 花莲县| 双辽市| 宁陵县| 盐边县| 鄂州市| 金阳县| 玛纳斯县| 台东县| 甘孜县| 会同县| 资溪县| 彩票| 南郑县| 利辛县| 高清| 清涧县| 普兰店市| 古交市| 灵台县| 万源市| 大连市| 宽城| 西丰县| 蓬莱市| 平安县| 五河县| 通海县| 克什克腾旗| 通山县| 碌曲县| 镇原县| 屯留县|