ContentProvider启动浅析

一、自己的理解

对于content provide的启动我是这样认为的,要用ContentResolver去获得一个contentProvider,在这的获得的过程中,

1、如果本应用之前有contentProvider的引用,则直接返回。

2、如果没用,则向AMS(ActivityManagerService)去申请,然后AMS返回一个ContentProvideHolder对象,这时又分两种情况:

2.1如果此contentprovider需要在本应用进程中创建,则返回一个Holder对象,然后在本进程中创建一个contentprovider,并保存一系列引用。

2.2 如果此contentprovider在其他进程中,这时又要分情况:

2.2.1如果此contentprovider已经存在,并且可以在运行在本应用进程中,则直接返回;如果不可以,则需要设置一个ContentProviderConnection对象 conn,然后将contentProvider添加到conn中,即为contentProvider添加了一个异地引用。

2.2.2如果此contentProvider不存在,则需要去创建一个,如果这个contentProvider所在的进程已经被创建,则直接在此进程中加载,如果没有被创建,则先启动这个应用进程,然后加载。在这个加载过程中会有一个while循环,直到contentprovider被加载完毕。

最后,会返回一个带有contentProvider的Holder对象。

ps:其实返回的ContentProvider对象并不真正是一个ContentProvider对象,因为这是两个进程之间的通信,他们不能直接获得彼此进程中的一些引用,而客户进程得到的ContentProvider对象引用,本质上是一个Binder对象。因为Android中只有BInder才能在两个进程之间通信。在这里,客户端得到的其实是Transport对象,通过它来操作服务端的ContentProvider

二、源码浅析

在分析源码前,先说明几个变量:

1、AMS的mProviderMap,它保存了整个系统中所有的ContentProviderRecord对象,而这个记录里面包含了contentProvider,mProviderMap有两种获得ContentProviderRecord的方法,一是通过类名获得,另一个是通过contentProvider的授权URI获得。

2、ProcessRecord的pubProviders和conProviders,他们是被存放在每个应用各自的进程中,以键值对的方式记录着ContentProviderRecord,pubProviders保存着进程中所有创建的ContentProvider,conProviders保存着进程中所有使用的ContentProvider。

3、ContentProviderHolder中保存了ContentProvider的相关信息,它含有ProviderInfo(包含了contentProvider的授权URI,读写权限等信息),ContentProvider的引用,IBInder(这其实是一个服务端的ContentProviderConnection对象,其继承了BInder,用来作为客户端与服务端的链接)

下面我们来看一下源码中使怎么写的:

1、ContentResolver的query

在query()中,会首先获得contentProvider对象,然后用此对象去进行查询。要获得contentProvider对象会调用acquireUnstableProvider(uri)或acquireProvider(uri)。其实这两个方法的最终调用都是一样的,现在,我们选择acquireUnstableProvider(uri)来分析。

2、ContentResolver的acquireUnstableProvider()

要分析此方法,首先要知道resolver对象是如何获得的。resolver对象是通过context对象的getContentResolver()获得的。其实是ContextImpl实现了Context,然后由ContextWrapper对其进行了修饰,即有ContextImpl对象的一个引用——mBase,然后Service,Activity又继承了ContextWrapper ,由此就可以使用上下文的资源。在ContextImpl中,有一个ApplicationContentResolver类继承了ContentResolver,并在ContextImplement初始化时,对其进行实例化,保存为mContentResolver引用。在getContentResolver()中会使用mBase的getContentResolver(),得到的就是这个mContentResolver,然后会在它的acquireUnstableProvider()去调用mMainThread(ActivityThread)的acquireProvider().

3、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;
        }
// There is a possible race here. Another thread may try to acquire // the same provider at the same time. When this happens, we want to ensure // that the first one wins. // Note that we cannot hold the lock while acquiring and installing the // provider since it might take a long time to run and it could also potentially // be re-entrant in the case where the provider is in the same process. IActivityManager.ContentProviderHolder holder = null; try { //向AMS请求一个含有ContentProvider的Holder holder = ActivityManagerNative.getDefault().getContentProvider( getApplicationThread(), auth, userId, stable); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } if (holder == null) { Slog.e(TAG, "Failed to find provider info for " + auth); return null; } // Install provider will increment the reference count for us, and break // any ties in the race. //如注释所说,对ContentProvider添加引用 holder = installProvider(c, holder, holder.info, true /*noisy*/, holder.noReleaseNeeded, stable); return holder.provider; }

关于此方法的作用,已在注释中写明,从这个方法中我们的值ContentProvider主要是从两个方面去获取的,一,从本地存在的COntentProvider集合中获取,即第4步;二,向AMS索取,即5,6,7步

4、ActivityThread的acquireExistingProvider()

public final IContentProvider acquireExistingProvider(
            Context c, String auth, int userId, boolean stable) {
        synchronized (mProviderMap) {
            final ProviderKey key = new ProviderKey(auth, userId);
        //查看本应用中是否存有要求的ContentProvider,没有则返回null
            final ProviderClientRecord pr = mProviderMap.get(key);
            if (pr == null) {
                return null;
            }
        //查看provider是否可用,不可用返回null
            IContentProvider provider = pr.mProvider;
            IBinder jBinder = provider.asBinder();
            if (!jBinder.isBinderAlive()) {
                // The hosting process of the provider has died; we can't
                // use this one.
                Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
                        + ": existing object's process dead");
                handleUnstableProviderDiedLocked(jBinder, true);
                return null;
            }
        //至此,说明provider是可用的,然后对contentProvider的引用数量进行改变
            // Only increment the ref count if we have one.  If we don't then the
            // provider is not reference counted and never needs to be released.
            ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
            if (prc != null) {
                incProviderRefLocked(prc, stable);
            }
            return provider;
        }
    }

关于incProviderRefLocked(),当请求一个新的ContentProvider时会调用此方法,它会将ContentProvider的引用计数加1,即将ContentProviderConnection对象进行更新,告诉它有某个应用需要使用新的ContentProvider,令其更新他的计数器。

5、AMS的getContentProvider()

调用此方法说明,在本应用没有已经存在的ContentProvider,需要向AMS申请一个。在此方法中会进行参数检查,最终去调用getContentProviderImpl()

5.1、AMS的getContentProviderImpl()

这个方法较长我们将它分为几个部分:

  5.1.1若ContentProvider已经存在,则返回引用

  5.1.2若其不存在,但是提供它的进程存在,则令此进程加载ContentProvider,然后返回新加载的对象

  5.1.3若其不存,且提供它的进程也不存在,则开启此进程,并加载ContentProvider ,而后返回新加载的对象

好的下面来分析5.1.1:

先声明一些变量来保存数据
     ContentProviderRecord cpr;
        ContentProviderConnection conn = null;
        ProviderInfo cpi = null;

接下来是一个同步代码块,再此代码块中获得ContentProvider,并防止多个进程进行争抢,导致出错

 ProcessRecord r = null;
            if (caller != null) {
                r = getRecordForAppLocked(caller);
                if (r == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                          + " (pid=" + Binder.getCallingPid()
                          + ") when getting content provider " + name);
                }
            }

获得客户端的进程,用来判断获得ContentProvider后,能否直接在客户端进程运行。

// First check if this content provider has been published...
            cpr = mProviderMap.getProviderByName(name, userId);
            // If that didn't work, check if it exists for user 0 and then
            // verify that it's a singleton provider before using it.
            if (cpr == null && userId != UserHandle.USER_SYSTEM) {
                cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
                if (cpr != null) {
                    cpi = cpr.info;
                    if (isSingleton(cpi.processName, cpi.applicationInfo,
                            cpi.name, cpi.flags)
                            && isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
                        userId = UserHandle.USER_SYSTEM;
                        checkCrossUser = false;
                    } else {
                        cpr = null;
                        cpi = null;
                    }
                }
            }

在此代码块中,首先查看,授权URI对应的进程中是否有ContentProvider,若没有,则去系统进程中查找,找到则证明此ContentProvider是单实例的,即系统中只有一个。

boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;

这一句是判断是否有存在且存活的ContentProvider,若有则为true,否则为false

if (providerRunning) {
                cpi = cpr.info;
                ......
               //查看是否可以在请求contentProvider的进程中运行,如果可以或者此contentProvider就是请求,提供的
                //则直接返回
                if (r != null && cpr.canRunHere(r)) {
                    ContentProviderHolder holder = cpr.newHolder(null);
                    holder.provider = null;
                    return holder;
                }
                ......
                // In this case the provider instance already exists, so we can
                // return it right away.
             //为contentProvider增加引用数
                conn = incProviderCountLocked(r, cpr, token, stable);
                if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {
                    if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
                        // 更新提供contentProvider进程的位置
                        ......
                        updateLruProcessLocked(cpr.proc, false, null);
                    }
                }
             //下面检查提供contentprovider的进程是否存活,若已死亡,等待新的进程启动
                //它是通过oom_adj的值来检查的
                final int verifiedAdj = cpr.proc.verifiedAdj;
                boolean success = updateOomAdjLocked(cpr.proc);
                if (success && verifiedAdj != cpr.proc.setAdj && !isProcessAliveLocked(cpr.proc)) {
                    success = false;
                }
                ......
                if (!success) {
                    // 进程已为空,等待新进程的启动
                    appDiedLocked(cpr.proc);
                    ......
                    providerRunning = false;
                    conn = null;
                } else {
                    cpr.proc.verifiedAdj = cpr.proc.setAdj;
                }

                Binder.restoreCallingIdentity(origId);
            }

上面的代码是当有contentProvider时,该如何做。应在注释中写明

这两个方法的作用详见:

下面我们来分析contentProvider不存在的情况,这里我们把5.1.2和5.1.3合并到一起分析

          if (!providerRunning) {               
         try{                    
            //在此获得provider的信息

            cpi
= AppGlobals.getPackageManager(). resolveContentProvider(name, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId); checkTime(startTime, "getContentProviderImpl: after resolveContentProvider"); } catch (RemoteException ex) { } if (cpi == null) { return null; } // 参数检查
          ......
           ComponentName comp = new ComponentName(cpi.packageName, cpi.name); ......
          //以类名的来获得provider cpr = mProviderMap.getProviderByClass(comp, userId); ......final boolean firstClass = cpr == null; if (firstClass) { ......try { checkTime(startTime, "getContentProviderImpl: before getApplicationInfo"); ApplicationInfo ai = AppGlobals.getPackageManager(). getApplicationInfo( cpi.applicationInfo.packageName, STOCK_PM_FLAGS, userId); ...... ai = getAppInfoForUser(ai, userId);
              //因为我们需要provider,但是provider记录不存在,所以在此创建一个ContentProviderRecord来保存要获得provider cpr
= new ContentProviderRecord(this, cpi, ai, comp, singleton); } catch (RemoteException ex) { // pm is in same process, this will never happen. } finally { Binder.restoreCallingIdentity(ident); } } ......
          //到这里我们已经获得了contentProviderRecord对象cpr,不管它含不含有provider
           if (r != null && cpr.canRunHere(r)) {
            //条件成立,表示provider可以在请求者进程运行,或在请求者进程创建,则可以直接返回一个Holder
return cpr.newHolder(null); } if (DEBUG_PROVIDER) Slog.w(TAG_PROVIDER, "LAUNCHING REMOTE PROVIDER (myuid " + (r != null ? r.uid : null) + " pruid " + cpr.appInfo.uid + "): " + cpr.info.name + " callers=" + Debug.getCallers(6)); //查看是否有正在启动的provider,若有等待启动完成 final int N = mLaunchingProviders.size(); int i; for (i = 0; i < N; i++) { if (mLaunchingProviders.get(i) == cpr) { break; } } // 没有正在启动的provider,去启动它 if (i >= N) { final long origId = Binder.clearCallingIdentity(); try { // 加载provider所在的包 try { ...... AppGlobals.getPackageManager().setPackageStoppedState( cpr.appInfo.packageName, false, userId); checkTime(startTime, "getContentProviderImpl: after set stopped state"); } catch (RemoteException e) { } catch (IllegalArgumentException e) { Slog.w(TAG, "Failed trying to unstop package " + cpr.appInfo.packageName + ": " + e); } // 获得provider所在的进程,因为我们要在此进程中启动provider ProcessRecord proc = getProcessRecordLocked( cpi.processName, cpr.appInfo.uid, false); if (proc != null && proc.thread != null && !proc.killed) {
                //条件成立,表示进程已经启动
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 {
                     //在此进程启动provider proc.thread.scheduleInstallProvider(cpi); }
catch (RemoteException e) { } } } else { //条件不成立,则需要先启动一个进程,然后等待此进程加载provider proc = startProcessLocked(cpi.processName, cpr.appInfo, false, 0, "content provider", new ComponentName(cpi.applicationInfo.packageName, cpi.name), false, false, false); ...... } cpr.launchingApp = proc; mLaunchingProviders.add(cpr); } finally { Binder.restoreCallingIdentity(origId); } } //此处保存这个新provider的一些引用信息 if (firstClass) { mProviderMap.putProviderByClass(comp, cpr); } mProviderMap.putProviderByName(name, cpr); conn = incProviderCountLocked(r, cpr, token, stable); if (conn != null) { conn.waiting = true; } }

完成上面的代码,表示provider已经存在,或正在启动,下面的代码用来检查

synchronized (cpr) {
            while (cpr.provider == null) {
                ......try {
                    ......
                    cpr.wait();
                } catch (InterruptedException ex) {
                } finally {
                    if (conn != null) {
                        conn.waiting = false;
                    }
                }
            }
        }
        return cpr != null ? cpr.newHolder(conn) : null;

新的provider成功获得后,把它放在一个Holder中返回

下面我们来说一下,上面的两个进程中启动provider的方法startProcessLocked()和scheduleInstallProvider(),他们最终都会调用installProvider()。现在,我们以scheduleInstallProvider()来分析。首先,会调用ApplicationThread的scheduleInstallProvider(),而在这个方法中会给消息队列发送一个消息,然后,会转到ActivityThread中的Handler对象中去处理。继而调用handleInstallProvider().

ActivityThread的handleInstallProvider():

public void handleInstallProvider(ProviderInfo info) {
        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
        try {
            installContentProviders(mInitialApplication, Lists.newArrayList(info));
        } finally {
            StrictMode.setThreadPolicy(oldPolicy);
        }
    }

此方法会转而去调用installContentProviders()

ActivityThread的installContentProviders()

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

        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(": ");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            ActivityManagerNative.getDefault().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

在这个方法中,首先会获得要加载的每一个provider的信息,因为我们这里只传入了一个,所以providers的长度为1.然后调用installProvider()去启动provider,启动完成之后会去告诉AMS,此provider已经启动完毕,让AMS去更新一些信息。installProvider()这个方法我们在前面请求者请求provider时见过,所以在这我们结合着两种不同的场景来分这方法的实现

ActivityThread的installProviders()          

    private IActivityManager.ContentProviderHolder installProvider(Context context,                  I
       ActivityManager.ContentProviderHolder holder, ProviderInfo inf
 

       boolean noisy, boolean noReleaseNeeded, boolean stable) {       
       ContentProvider localProvider 
null;

       IContentProvider provider;
     if (holder == null || holder.provider == null) { ......
      //在此首先要获得与要加载provider相关的Context,因为provider是null,所以我们在后面要去实例化一个provider,这是当前情况下的处理做法
        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) { // Ignore  } } ......try { final java.lang.ClassLoader cl = c.getClassLoader(); localProvider = (ContentProvider)cl. loadClass(info.name).newInstance();
          
          //实例化一个provider,并获得可以在进程间通信的Transport对象 provider
= localProvider.getIContentProvider(); ......
        
// 为新创建的provider配置一些信息,如读写权限之类的 localProvider.attachInfo(c, info); } catch (java.lang.Exception e) { ...... } } else {
       //这是在请求者请求时的处理,传进来的是一个在其他进程已经启动好了的provider provider
= holder.provider; ...... } IActivityManager.ContentProviderHolder retHolder; synchronized (mProviderMap) { ...... IBinder jBinder = provider.asBinder(); if (localProvider != null) {
          //条件成立,表示此provider是新建的,需要保存一些引用 ComponentName cname
= new ComponentName(info.packageName, info.name); ProviderClientRecord pr = mLocalProvidersByName.get(cname); if (pr != null) { //为null表示,在多个进程同时请求时,竞争失败,已经有其他进程先获得了provider,在此不需要在此保存 provider = pr.mProvider; } else {
            //需要将provider保存在holder中 holder
= new IActivityManager.ContentProviderHolder(info); holder.provider = provider; holder.noReleaseNeeded = true; pr = installProviderAuthoritiesLocked(provider, localProvider, holder); mLocalProviders.put(jBinder, pr); mLocalProvidersByName.put(cname, pr); } retHolder = pr.mHolder; } else {
         //表示此provider是用其他进程传入的,要在此保存provider的引用数量,当Pro不为null时,是第一次传入,可以根据要求判断是否进行更新
         //pro为null,则需要创建一个provider远程引用数,并进行保存
ProviderRefCount prc
= mProviderRefCountMap.get(jBinder); if (prc != null) { ...... if (!noReleaseNeeded) { incProviderRefLocked(prc, stable); try { ActivityManagerNative.getDefault().removeContentProvider( holder.connection, stable); } catch (RemoteException e) { //do nothing content provider object is dead any way } } } else { ProviderClientRecord client = installProviderAuthoritiesLocked( provider, localProvider, holder); if (noReleaseNeeded) { prc = new ProviderRefCount(holder, client, 1000, 1000); } else { prc = stable ? new ProviderRefCount(holder, client, 1, 0) : new ProviderRefCount(holder, client, 0, 1); } mProviderRefCountMap.put(jBinder, prc); } retHolder = prc.holder; } } return retHolder; }

然后,我们可以通知AMS去发布provider了

AMS的publishContentProviders()

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;
                }
         //dst为之前在getContentProviderImpl中创建的provider记录 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); String names[] = dst.info.authority.split(";"); for (int j = 0; j < names.length; j++) { mProviderMap.putProviderByName(names[j], dst); } 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--; } } //在此表示,请求的provider已经被启动,并向record中添加provider,然后打断前面的while循环。synchronized (dst) { dst.provider = src.provider; dst.proc = r; dst.notifyAll(); } updateOomAdjLocked(r); maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); } } Binder.restoreCallingIdentity(origId); } }

传入的providers 是已经加载好的provider,会与加载的provider进行比较,若相同则说明已经启动,并将其从待启动队列中移除。并向provider记录中添加provider,这样前面的while循环就可以被打断,从而AMS就可以将provider返回,给请求者。

至此contentProvider的启动就已经分析结束。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注