<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    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 閱讀(1219) 評論(0)  編輯  收藏

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


    網站導航:
     
    主站蜘蛛池模板: 国产真人无码作爱视频免费| 日本妇人成熟免费中文字幕| 久久亚洲私人国产精品| 9久9久女女免费精品视频在线观看| 在线亚洲午夜片AV大片| 男人的天堂亚洲一区二区三区 | 免费人成网站在线观看不卡| 亚洲精品国产专区91在线| 日本免费一二区在线电影| 任你躁在线精品免费| 亚洲自偷自偷在线成人网站传媒| 亚洲熟妇少妇任你躁在线观看无码| 十九岁在线观看免费完整版电影| 亚洲精品无码久久久久久| 亚洲国产一二三精品无码| 处破痛哭A√18成年片免费| a国产成人免费视频| 亚洲人成电影网站色| 无码乱人伦一区二区亚洲一| 国产男女猛烈无遮挡免费视频网站| 黄网站色视频免费在线观看的a站最新| 亚洲免费闲人蜜桃| 久久久久亚洲AV成人网人人软件| 波多野结衣中文字幕免费视频| 一级女性全黄久久生活片免费| 亚洲无人区视频大全| 一本色道久久综合亚洲精品高清| 成年性生交大片免费看| 久久久久免费精品国产| 在线观看亚洲网站| 亚洲色偷偷av男人的天堂| 亚洲?v女人的天堂在线观看| 成人无码区免费A片视频WWW| 99re6在线视频精品免费| 亚洲精品理论电影在线观看 | 亚洲成a人片在线不卡一二三区| 亚洲精品免费在线观看| 亚洲人成影院在线观看| 永久黄网站色视频免费观看| 日本三级2019在线观看免费| 67194成手机免费观看|