ContentProvider的启动流程分析

Posted zhenjie_chang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ContentProvider的启动流程分析相关的知识,希望对你有一定的参考价值。

ContentProvider是android系统的四大组件之一,主要用于向外部提供数据。不仅可以向自己应用进程提供数据,也可以向其他进程的提供数据。所以在分析ContentProvider的时候我们首先分析本进程的ContentProvider的启动过程,然后再分析调用其他进程的ContentProvider的时候ContentProvider的安装启动过程。

本进程ContentProvider启动过程分析

本进程内的ContentProvider一般是在进程启动的时候就启动并创建的。在Activity的启动过程中分析过新进程的启动过程。

1. 创建一个新的进程。调用ActivityThreadmain方法

2. 调用ActivityThreadattach方法

3. 调用ActivityManagerServiceattachApplication方法,通知新的进程创建完成,根据新创建的进程初始化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来处理。HandlerhandlerMessage接收到BIND_APPLICATION消息后,调用ActivityThreadhandBindApplication方法来处理。

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对象返回,然后我们调用AMSpublishContentProviders方法,将这些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信息




  

以上是关于ContentProvider的启动流程分析的主要内容,如果未能解决你的问题,请参考以下文章

Android 源码分析 ContentProvider 启动

ContentProvider启动过程and多进程调用全过程源码详解

Android中的ContentProvider源码解析

Android 启动流程

Android动态部署六:如何从插件apk中启动BroadcastReceiver和ContentProvider

Android动态部署六:如何从插件apk中启动BroadcastReceiver和ContentProvider