天天看點

ContentProvider的啟動流程分析

ContentProvider是Android系統的四大元件之一,主要用于向外部提供資料。不僅可以向自己應用程序提供資料,也可以向其他程序的提供資料。是以在分析ContentProvider的時候我們首先分析本程序的ContentProvider的啟動過程,然後再分析調用其他程序的ContentProvider的時候ContentProvider的安裝啟動過程。

本程序ContentProvider啟動過程分析

本程序内的ContentProvider一般是在程序啟動的時候就啟動并建立的。在Activity的啟動過程中分析過新程序的啟動過程。

1. 建立一個新的程序。調用ActivityThread的main方法

2. 調用ActivityThread的attach方法

3. 調用ActivityManagerService的attachApplication方法,通知新的程序建立完成,根據新建立的程序初始化ProcessRecord的資訊。然後查詢所有和本程序相關的ContentProvider資訊。

4. 調用建立程序的bindApplication方法,通知新程序安裝并啟動這些ContentProvider

以上就是本程序的ContentProvider的啟動和安裝過程。第一第二步我們不再分析,直接從AMS服務的attachApplication方法開始分析。

1. AMS.attachApplication

public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }

 private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {

        //找到之前建立的ProcessRecord對象,之前的ProcessRecord對象還沒有指向任何一個程序
        ProcessRecord app;
        if (pid != MY_PID && pid >= 0) {
            synchronized (mPidsSelfLocked) {
                app = mPidsSelfLocked.get(pid);
            }
        } else {
            app = null;
        }

        //根據建立的程序來初始化這個ProcessRecord對象,使它指向新建立的程序
        app.makeActive(thread, mProcessStats);
        app.curAdj = app.setAdj = -100;
        app.curSchedGroup = app.setSchedGroup = Process.THREAD_GROUP_DEFAULT;
        app.forcingToForeground = null;
        updateProcessForegroundLocked(app, false, false);
        app.hasShownUi = false;
        app.debugging = false;
        app.cached = false;
        app.killedByAm = false;

        //使用generateApplicationProvidersLocked方法在PMS服務中查詢所有和該程序有關的ContentProvider資訊
        boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
        List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;

        ……
		//最終調用建立程序的bindApplication來建立并啟動該ContentProvider
            thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());
         ……
    }
           

在該方法中調用了generateApplicationProvidersLocked方法來查詢和建立程序相關的ContetProvider的資訊,儲存在Providers清單中。同時在該方法中,還将查詢到的每一個ContentProvider的資訊,封裝成了一個ContentProviderRecord對象,儲存了ProviderMap中。

随後調用建立程序的binderApplication方法的時候将providers資訊作為參數傳入了建立程序。

2. ApplicationThread.bindApplication

public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
                Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
                Bundle coreSettings) {
            ……
            AppBindData data = new AppBindData();
            data.processName = processName;
            data.appInfo = appInfo;
            data.providers = providers;
            data.instrumentationName = instrumentationName;
            data.instrumentationArgs = instrumentationArgs;
            data.instrumentationWatcher = instrumentationWatcher;
            data.instrumentationUiAutomationConnection = instrumentationUiConnection;
            data.debugMode = debugMode;
            data.enableOpenGlTrace = enableOpenGlTrace;
            data.restrictedBackupMode = isRestrictedBackupMode;
            data.persistent = persistent;
            data.config = config;
            data.compatInfo = compatInfo;
            data.initProfilerInfo = profilerInfo;
            sendMessage(H.BIND_APPLICATION, data);
        }
           

ApplicationThread接收到AMS服務發送的請求,然後通過Handler發送BIND_APPLICATION消息給Handler來處理。Handler的handlerMessage接收到BIND_APPLICATION消息後,調用ActivityThread的handBindApplication方法來處理。

3. ActivityThread.handleBindApplication

List<ProviderInfo> providers = data.providers;
    if (providers != null) {
     installContentProviders(app, providers);
     // For process that contains content providers, we want to
     // ensure that the JIT is enabled "at some point".
      mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
           

handleBindApplication方法中我們隻分析和初始化ContentProvider相關的代碼邏輯。首先擷取到參數傳遞的和本程序有關的provider的資訊,調用installContentProviders方法來安裝并啟動contentProvider元件。

4. ActivityThread.installContentProviders

private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<IActivityManager.ContentProviderHolder> results =
            new ArrayList<IActivityManager.ContentProviderHolder>();

        for (ProviderInfo cpi : providers) {
            ……
			//通過調用installProvider方法,生成了一個ContetProviderHolder對象
            IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
		    //通知AMS服務來釋出這些ContentProvider,這樣其他程序就可以通過AMS服務來通路這些ContentProvider了
            ActivityManagerNative.getDefault().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
        }
    }
           

這個方法主要做了兩件事。

第一.通過循環變量providerinfo資訊,調用installProvider方法将provider資訊安裝完成并封裝成了一個ContentProviderHolder類型的對象。

第二.調用AMS服務的publishContentProviders方法,将這些安裝完成的Provider資訊釋出到AMS服務,以便其他程序通路。

那就先來看installProvider的處理過程,在來看AMS釋出ContentProvider的過程。

5. ActivityThread.installProvider過程

private IActivityManager.ContentProviderHolder installProvider(Context context,
            IActivityManager.ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null;
        IContentProvider provider;
        if (holder == null || holder.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;
            ……
            }
           ……
            try {
                final java.lang.ClassLoader cl = c.getClassLoader();
                localProvider = (ContentProvider)cl.
                    loadClass(info.name).newInstance();
                //擷取ContetProvider的IContentProvider指派給provider變量
                provider = localProvider.getIContentProvider();
                localProvider.attachInfo(c, info);
            } 
        } else {
			……
		}
           

我們首先來看這個方法的第一部分。在installContentProviders方法中調用這個方法的時候,holder參數傳遞的值為Null,也是因為這些ContentProvider是第一次安裝。是以holde肯定為Null。是以此時滿座if的條件。在If語句中,首先根據條件擷取相應的Context上下文資訊。

然後ClassLoader加載對應的ContentProvider類,并建立該類的對象,然後調用ContentProvider的attach方法。該方法作用是将新建立的ContentProvider和Context,ProviderInfo關聯起來,最後調用該Provider的onCreate方法啟動ContentProvider。這個一個ContentProvider就建立完成了,下一步就是将它儲存到應用程序的中,以友善查找和管理。并釋出到AMS服務中,友善其他程序調用。

擷取ContetProvider的IContentProvider指派給provider變量,IContentProvider是ContentProvider用戶端和服務端通信的接口,genIcontentProvider了解為得到一個Binder類型的對象,用于ContentProvider用戶端和服務端之間的通信。

IActivityManager.ContentProviderHolder retHolder;
     synchronized (mProviderMap) {
            IBinder jBinder = provider.asBinder();
            if (localProvider != null) {
                ComponentName cname = new ComponentName(info.packageName, info.name);
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);
               //第一次建立ContentProvider,還沒有儲存,是以pr為null
                if (pr != null) {
                    provider = pr.mProvider;
                } else {
                    //根據Provider建立ContentProviderHolder對象
                    holder = new IActivityManager.ContentProviderHolder(info);
                    holder.provider = provider;
                    holder.noReleaseNeeded = true;
                    //通過該方法吧Provider的資訊儲存到ProviderMap中
pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
                    mLocalProviders.put(jBinder, pr);
                    mLocalProvidersByName.put(cname, pr);
                }
                retHolder = pr.mHolder;
            } else {
			……
			}
return retHolder;
}
           

由于是第一次啟動ContentProvider,是以該資訊還沒有儲存,是以變量pr為空,此時根據ProviderInfo的資訊和Binder類型IContentProvider對象,建立一個ContentProviderHolder對象,它裡邊封裝了這個ContentProvider的ProviderInfo和IContentProvider資訊。

方法最後傳回建立的這個ContentProviderHolder的對象。

接着看看一下installProviderAuthoritiesLocked的實作

installProviderAuthoritiesLocked

private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
            ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {
        final String auths[] = holder.info.authority.split(";");
        final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);

        final ProviderClientRecord pcr = new ProviderClientRecord(
                auths, provider, localProvider, holder);
        for (String auth : auths) {
            final ProviderKey key = new ProviderKey(auth, userId);
            final ProviderClientRecord existing = mProviderMap.get(key);
            if (existing != null) {
                Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
                        + " already published as " + auth);
            } else {
                mProviderMap.put(key, pcr);
            }
        }
        return pcr;
    }
           

根據Provider的資訊建立了一個ProviderClientRecord對象,authority是一個多屬性值,變量這個Provider對應的所有authority,每個authority屬性為key,儲存這個ProviderClientReocrd到mProviderMap描述的HashMap中。

在一個應用程序中有三個清單來儲存本程序中的ContentProvider的資訊。

1. ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap

主要以authority為key,儲存providerClientRecord資訊

2. ArrayMap<IBinder, ProviderClientRecord> mLocalProviders

以通信的接口Binder對象為key儲存ProviderClientRecord對象。主要儲存了本程序的ContentProvider的資訊

3. ArrayMap<ComponentName,ProviderClientRecord> mLocalProvidersByName

以Provider的ComponentName資訊為key 儲存ProviderClientRecord對象。主要儲存了本程序的ContentProvider的資訊

通過installProvider方法将ContentProvider的類加載到記憶體中來,并建立了ContentProvider的對象,調用了ContentProvider的onCreate來啟動它。然後将它按照不同的存儲類型分别儲存不同的ContentProvider集合中。

6. AMS.publishContentProviders過程

ContentProvider本地建立完成并儲存後,将它封裝成立一個ContentProviderHolder對象傳回,然後我們調用AMS的publishContentProviders方法,将這些Holder對象發送給AMS服務将他們釋出到AMS服務中。

public final void publishContentProviders(IApplicationThread caller,
            List<ContentProviderHolder> providers) {
        if (providers == null) {
            return;
        }
        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) {
                    ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
                    mProviderMap.putProviderByClass(comp, dst);
					//将ContentProvider的資訊按照Authority為key儲存到AMS的ProviderMap集合中
                    String names[] = dst.info.authority.split(";");
                    for (int j = 0; j < names.length; j++) {
                        mProviderMap.putProviderByName(names[j], dst);
                    }

					//查詢是否有程序在等待這個ContentProvider啟動,如果等待清單中有,從等待清單删除
                    int launchingCount = mLaunchingProviders.size();
                    int j;
                    boolean wasInLaunchingProviders = false;
                    for (j = 0; j < launchingCount; j++) {
                        if (mLaunchingProviders.get(j) == dst) {
                            mLaunchingProviders.remove(j);
                            wasInLaunchingProviders = true;
                            j--;
                            launchingCount--;
                        }
                    }
                    if (wasInLaunchingProviders) {
                        mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                    }
                    synchronized (dst) {
					    //将AMS服務中這個ConentProviderRecord指向應用程序的一個ContentProviderClientRecord.
                        dst.provider = src.provider;
						 //将AMS服務中這個ConentProviderRecord指向一個對應的應用程序
                        dst.proc = r;
						//通知這個ContentProvider已經啟動完成,結束等待
                        dst.notifyAll();
                    }
                
                }
            }
        }
    }
           

在前面generateApplicationProvidersLocked方法查詢在該程序運作的所有ContentProvider的資訊的時候,為每一個ContentProvider資訊建立了一個ContentProviderRecord對象,儲存到了ProviderMap集合中。那時候這些ContnetProvider還沒有啟動建立,隻是一些ContentProvider的資訊。這個方法就是要将建立并啟動的ContentProvider和ContentProviderRecord關聯起來。

首先将啟動的ContentProvider按照authority為key儲存到ProviderMap中。然後将ContentProviderRecord的provider指向應用程序中啟動的ContentProviderClientRecord對象,proc指向新啟動應用程序。這樣ContentProvider就在AMS服務中釋出完成了。釋出的工作主要就是将ContentProvider的資訊儲存到AMS服務中去。

AMS服務儲存ContentProvider的資訊主要是在類ProviderMap中,它裡邊有兩種儲存的Provider資訊的集合,以兩種不同的方式儲存了provider的資訊。

1. ProviderByClass

以ComponentName為key儲存了ContentProviderRecord的資訊

2. ProviderByName

以authority為key儲存了ContentProviderRecord的資訊

本程序的ContentProvider的啟動過程就分析完成了。

接下來幾行代碼和和不同程序間調用ContentProvider有關。我們順便分析下。

如果是不同程序間調用ContentProvider的時候,首先會判斷該ContentProvider所在的程序是否已經啟動,如果有啟動需要首先啟動該程序,在該程序啟動完成後這個ContentProvider也就啟動起來了。

如果沒有啟動的時候,AMS就會首先啟動個程序及ContentProvider,并把這個ContentProviderRecord添加到等待隊列mLaunchingProviders中去,然後等他它啟動完成。

此處代碼就是新的程序及ContentProvider啟動完成後,首先判斷是否在等待程序中,如果有,就将該ContentProvider資訊從等待隊列中移除,并調用notifyAll來喚醒等待的工作。 

調用ContentProvider的過程分析

ContentProvider的調用方法:

getContext().getContentResolver().insert(uri);

我們就分析insert方法來分析ContentProvider的調用過程。

getContext最終的實作是在ContextImpl中,那我們就從ContextImpl的getContentResolver開始分析

1. ContextImpl.getContentResolver

public ContentResolver getContentResolver() {
        return mContentResolver;
    }
	
	private final ApplicationContentResolver mContentResolver;

	private static final class ApplicationContentResolver extends ContentResolver {
    private final ActivityThread mMainThread;
    private final UserHandle mUser;

    public ApplicationContentResolver(
            Context context, ActivityThread mainThread, UserHandle user) {
        super(context);
        mMainThread = Preconditions.checkNotNull(mainThread);
        mUser = Preconditions.checkNotNull(user);
    }
           

從代碼中可以看出,ContextImpl的getContentResolver最終傳回的是一個ApplicationContentResolver的對象。這個ApplicationContentResolver是ContextImpl的一個内部類,繼承自ContentResolver。在ContextImpl建立的時候就建立了這個對象。

Insert方法最終就是要看ApplicationContentResolver的Insert方法的實作了。但是ApplicationContentResolver裡面并沒有Insert方法,是以最終還是要看ContentResolver的insert方法。

2. ContentResolver.insert

public final @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues values) {
        Preconditions.checkNotNull(url, "url");
		//擷取調用ContentProvider的接口
        IContentProvider provider = acquireProvider(url);

        try {
            long startTime = SystemClock.uptimeMillis();
			//調用contentProvider的insert方法
            Uri createdRow = provider.insert(mPackageName, url, values);
            long durationMillis = SystemClock.uptimeMillis() - startTime;
            return createdRow;
        } catch (RemoteException e) {
            ……
        } finally {
            releaseProvider(provider);
        }
    }
           

這個方法主要做了兩個工作:

第一. 通過acquireProvider方法擷取ContentProvider的通信接口,用戶端通過該通信接口可以調用服務端的ContentProvider的方法。

第二. 通過擷取的通信接口,調用ContentProvider的insert方法,插入資料。

ContentProvider的用戶端和服務端通信主要通過Binder間程序通信實作和AMS服務關系不是太大,此處不再詳細分析。主要分析擷取ContentProvider通信接口的過程。

調用ContentResolver的acquireProvider方法,該方法是一個abstrict 方法。在ApplicationContentResolver中實作了該方法。

3. ApplicationContentResolver.acquireProvider

protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), true);
        }
           

mMainThread就是ActivityThread,此處直接調用ActivityThread的acquireProvider方法。

4. ActivityThread. acquireProvider

public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
		//首先檢視是否已經儲存了該ContentProvider的通信接口,已經儲存的話直接傳回
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
            return provider;
        }
		//未儲存的話,去AMS服務查詢
        IActivityManager.ContentProviderHolder holder = null;
        try {
            holder = ActivityManagerNative.getDefault().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } catch (RemoteException ex) {
        }
        if (holder == null) {
            Slog.e(TAG, "Failed to find provider info for " + auth);
            return null;
        }

        //根據查詢的結果,儲存到本地程序中
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }
           

1. 通過acquireExistingProvider方法檢查本地程序是否已經儲存了該ContentProvider的通信接口,如果已經儲存的話直接傳回,不再需要去AMS服務查詢并在本地儲存安裝。

2. 如果本地沒有儲存,請求AMS服務的getContentProvider方法,去擷取該ContentProvider的資訊。

3. 根據從AMS服務擷取的資訊,将該ContentPrivider儲存在本地

5. ActivityThread. acquireExistingProvider

public final IContentProvider acquireExistingProvider(
            Context c, String auth, int userId, boolean stable) {
        synchronized (mProviderMap) {
		    //在本程序的ProviderMap中查詢是否包含請求的ContentProvider資訊
            final ProviderKey key = new ProviderKey(auth, userId);
            final ProviderClientRecord pr = mProviderMap.get(key);
            if (pr == null) {
                return null;
            }
			//如果有的話,直接傳回并對該ContentProvider引用加1
            IContentProvider provider = pr.mProvider;
            IBinder jBinder = provider.asBinder();
            
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                incProviderRefLocked(prc, stable);
            }
            return provider;
        }
    }
           

這個方法的實作邏輯比較簡單,直接根據要請求ContentProvider的Uri從ProviderMap的集合中查詢,如果已經存在直接傳回該Provider的通信接口IContentProvider,并對它的引用加1。

一般情況下調用本程序的ContentProvider都是存在的,因為在程序啟動的時候這些ContentProvider就已進啟動完成,并儲存到相關的集合中去了。

如果不存在,那就應該是第一次調用其他程序的ContentProvider,接着往下分析。

6. AMS.getContentProvider

getContentProvider直接調用getContentProviderImpl方法來實作

private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
            String name, IBinder token, boolean stable, int userId) {
        ContentProviderRecord cpr;
        ContentProviderConnection conn = null;
        ProviderInfo cpi = null;

        synchronized(this) {
            long startTime = SystemClock.elapsedRealtime();

            ProcessRecord r = null;
            if (caller != null) {
                r = getRecordForAppLocked(caller);
            }

            //首先檢查該ContentProvider是否已經釋出
            cpr = mProviderMap.getProviderByName(name, userId);
           
            boolean providerRunning = cpr != null;
			//已經釋出
            if (providerRunning) {
                cpi = cpr.info;
				//檢查是否允許該ContentProvider在多個程序中運作,允許的話直接傳回Holder為Null
                if (r != null && cpr.canRunHere(r)) {
                   
                    ContentProviderHolder holder = cpr.newHolder(null);
                    holder.provider = null;
                    return holder;
                }

            boolean singleton;
			//還未釋出
            if (!providerRunning) {
                ……
                ComponentName comp = new ComponentName(cpi.packageName, cpi.name);         
                cpr = mProviderMap.getProviderByClass(comp, userId);
                //還未釋出的情況下cpr還沒有儲存,查詢ContentProvider資訊,并封裝成一個ContentProviderReocrd對象
                final boolean firstClass = cpr == null;
                if (firstClass) {
                    final long ident = Binder.clearCallingIdentity();
                    try {
                        checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");
                        ApplicationInfo ai =
                            AppGlobals.getPackageManager().
                                getApplicationInfo(
                                        cpi.applicationInfo.packageName,
                                        STOCK_PM_FLAGS, userId);
                        checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");
                        if (ai == null) {
                            Slog.w(TAG, "No package info for content provider "
                                    + cpi.name);
                            return null;
                        }
                        ai = getAppInfoForUser(ai, userId);
                        cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
                    }
                }

                
                if (r != null && cpr.canRunHere(r)) {
                    //檢查是否允許該ContentProvider在多個程序中運作,允許的話直接傳回Holder為Null
                    return cpr.newHolder(null);
                }

                不允許的話,啟動ContentProvider及其所在的程序,把這個ContentProviderRecord添加到等待隊列中去
                final int N = mLaunchingProviders.size();
                int i;
                for (i = 0; i < N; i++) {
                    if (mLaunchingProviders.get(i) == cpr) {
                        break;
                    }
                }
                if (i >= N) {
                    final long origId = Binder.clearCallingIdentity();

                    try {
                        //判斷該程序是否已經啟動,如果已經啟動的話直接調用scheduleInstallProvider來啟動該ContentProvider
                        ProcessRecord proc = getProcessRecordLocked(
                                cpi.processName, cpr.appInfo.uid, false);
                        if (proc != null && proc.thread != null) {
                            if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
                                    "Installing in existing process " + proc);
                            if (!proc.pubProviders.containsKey(cpi.name)) {
                                checkTime(startTime, "getContentProviderImpl: scheduling install");
                                proc.pubProviders.put(cpi.name, cpr);
                                try {
                                    proc.thread.scheduleInstallProvider(cpi);
                                } catch (RemoteException e) {
                                }
                            }
                        } else {
						//程序沒有啟動,則直接啟動程序
                            checkTime(startTime, "getContentProviderImpl: before start process");
                            proc = startProcessLocked(cpi.processName,
                                    cpr.appInfo, false, 0, "content provider",
                                    new ComponentName(cpi.applicationInfo.packageName,
                                            cpi.name), false, false, false);
                            checkTime(startTime, "getContentProviderImpl: after start process");
                            if (proc == null) {
                                Slog.w(TAG, "Unable to launch app "
                                        + cpi.applicationInfo.packageName + "/"
                                        + cpi.applicationInfo.uid + " for provider "
                                        + name + ": process is bad");
                                return null;
                            }
                        }
                        cpr.launchingApp = proc;
                        mLaunchingProviders.add(cpr);
                    } finally {
                        Binder.restoreCallingIdentity(origId);
                    }
                }

                //将該ContentProviderRecord儲存到mProviderMap中
                if (firstClass) {
                    mProviderMap.putProviderByClass(comp, cpr);
                }

                mProviderMap.putProviderByName(name, cpr);
                conn = incProviderCountLocked(r, cpr, token, stable);
                if (conn != null) {
                    conn.waiting = true;
                }
            }
            checkTime(startTime, "getContentProviderImpl: done!");
        }

        //一直等到這個Provider啟動完成之後喚醒
        synchronized (cpr) {
					……
                    cpr.wait();
					……
                }
            }
        }
        return cpr != null ? cpr.newHolder(conn) : null;
    }
           

這個方法的邏輯有點複雜。首先從mProviderMap中查詢這個ContentProvider是否存在。

1. 如果存在,表示已經釋出

    a. 首先檢查該Provider是否允許在多個程序中運作。如果運作多個程序中都有執行個體的話,直接傳回ContentProviderHolder為null

一般情況下,ContentProvider的服務端都是運作在程序應用所在的程序中,但是如果ContentProvider的屬性multiProcess為true的話表示,該ContentProvider可以在多個程序中都有執行個體。

    b. 不允許在多個程序中都有執行個體,直接傳回已經釋出的ContentProvider的Holder資訊

2. 如果不存在,表示該ContentProvider還沒有釋出

如果還沒有釋出,則首先去查詢該ContentProvider的ProviderInfo資訊。然後根據ProviderInfo,再去查詢ContentProviderRecord是否在ProviderMap中存在。第一次調用的話ContentProviderRecord肯定為null.然後去PMS服務中查詢ContentProvider資訊,建立一個ContentProviderRecord對象。

    a. 判斷是否允許多個程序中都有執行個體,允許的話直接傳回Holder為Null

    b. 不允許的允許多個程序中都有執行個體,需要啟動該ContentProvider及其所在的程序。

        a) 該程序已經啟動

        直接通知該程序安裝并啟動ContentProvider,并将該ContentProviderRecord儲存到ProviderMap中。然後添加到mLaunchingProviders等待隊列,調用wait方法等待該    ContentProvider啟動完成。

        b) 該程序及ContentProvider都還未建立啟動

啟動該ContentProvider所在的程序,并啟動ContentProvider.此處邏輯和前面ContentProvider啟動過程一緻。将該ContentProviderRecord儲存到ProviderMap中。然後添加到mLaunchingProviders等待隊列,調用wait方法等待該ContentProvider啟動完成。

    當ContentProvider啟動後,喚醒此處等待,然後傳回該ContentProviderHolder資訊