AMS分析「 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及其所在的进程。

1 : 该进程已经启动
直接通知该进程安装并启动ContentProvider,并将该ContentProviderRecord保存到ProviderMap中。然后添加到mLaunchingProviders等待队列,调用wait方法等待该 ContentProvider启动完成。

2 : 该进程及ContentProvider都还未创建启动
启动该ContentProvider所在的进程,并启动ContentProvider.此处逻辑和前面ContentProvider启动过程一致。将该ContentProviderRecord保存到ProviderMap中。然后添加到mLaunchingProviders等待队列,调用wait方法等待该ContentProvider启动完成。

当ContentProvider启动后,唤醒此处等待,然后返回该ContentProviderHolder信息

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容