zoukankan      html  css  js  c++  java
  • 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的启动就已经分析结束。

  • 相关阅读:
    LeetCode 18. 4Sum (四数之和)
    开运算、闭运算、形态学梯度、顶帽、黑帽
    膨胀和腐蚀
    四种滤波方式
    关于平滑处理及图像滤波与滤波器
    27、简述redis的有哪几种持久化策略及比较?
    26、redis中默认有多少个哈希槽?
    25、如何实现redis集群?
    大聊Python----Select解析
    大聊Python----IO口多路复用
  • 原文地址:https://www.cnblogs.com/l1019/p/7232201.html
Copyright © 2011-2022 走看看