美文网首页
ContentProvider笔记[转]

ContentProvider笔记[转]

作者: zjfclimin | 来源:发表于2017-04-13 16:50 被阅读152次

    关于ContentProvider网上有很多文章进行了详细的分析,这里我初略地画了一个getContentProvider时序图,帮助整体上进行理解把握。其中ActivityThread_A代表进程A的ActivityThread,ActivityThread_B代表进程B的ActivityThread,这里是在进程A中去get进程B中的ContentProvider。有四个关键点,下面将一一详细说明下。


    节点1、在getContentProviderImpl()函数中会判断ContentProvider所在的目标进程B是否已经启动,如果未启动,那么会先启动目标进程B,然后当前线程会wait在一个ContentProviderRecord对象上,只有等到进程B启动并将ContentProvider publish到AMS中,通过notifyAll()唤醒,前面那个线程才会继续执行。
    节点2、进程B启动过程中,在handleBindApplication()函数中进行ContentProvider安装,安装函数便是installProvider(),我们可以看到关键点4也是调用installProvider()进行ContentProvider安装,为什么会有两次ContentProvider安装呢?是因为同时需要在进程A和进程B中进行安装,那么进程A、B中安装有何不同?答案就在installProvider()函数中,installProvider()具体分析见下文。
    节点3、当进程B本地安装完ContentProvider,然后夸Binder调用AMS.publishContentProviders()接口将ContentProvider中Transport 的Binder实体传递到AMS中,并调用notifyAll()进行唤醒上面所说的等待线程。
    节点4、进程A中调用完AMS.getContentProvider()后立即调用installProvider()进行ContentProvider本地安装,这是第二次调用installProvider(),具体分析见下面。

    installProvider()函数分析:
    在分析这个函数之前我们先要搞清楚ContentProviderHolder类的两个成员变量provider、connection。在进程B启动过程中,会实例化ContentProvider对象, ContentProvider中有一个Transport类型的Binder本地对象mTransport,进程A就是要拿到这个mTransport的代理来与B进程中的ContentProvider建立Binder通信。
    那进程A是如何拿到进程B中ContentProvider.mTransport的代理的呢?mTransport是一个匿名Binder对象,我们知道匿名Binder对象是可以在已经建立起的Binder通信的链路中进行传递的,于是,mTransport便是这样传递到进程A并保存在ContentProviderHolder.provider中:
    ①进程B启动安装ContentProvider时通过publishContentProviders()接口将mTransport传递到AMS,并保存在ContentProviderRecord.provider中;
    ②进程A调用AMS.getContentProvider()接口,AMS会将ContentProviderRecord.provider和新创建的ContentProviderConnection匿名Binder对象一并传递到进程A中,分别保存在ContentProviderHolder.provider和ContentProviderHolder.connection中。
    注意,进程A中ContentProviderHolder.provider与AMS中的ContentProviderRecord.provider均是进程B中mTransport的Binder代理。进程A拿到了进程B中ContentProvider.mTransport代理,便建立Binder通信。更具体的ContentProvider操作在自定义的ContentProvider中实现,Transport只是通信的封装。



    了解了上面这些信息我们再来看installProvider()函数。在上面的时序图中有两次调用,一次在进程B中,一次在进程A中。这里我们只关心installProvider(..,holder,..)函数的第二个参数holder。holder是一个ContentProviderHolder类型参数,如果holder.provider为null,那边需要在本次installProvider()函数调用中实例化ContentProvider对象,如果holder.provider不为null,则无需实例化ContentProvider对象。在进程B中installProvider()函数调用参数holder.provider=null,在进程A中installProvider()函数调用参数holder.provider!=null,因此进程B中会实例化ContentProvider对象,而进程A中holder.provider保存着进程B中ContentProvider.mTransport的代理,这一点读者稍微跟下代码便可发现。

    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) {    //holder.provider=null,意味着需要在此实例化ContentProvider对象。进程B中调用该函数满足这个条件。  
            if (DEBUG_PROVIDER || noisy) {  
                Slog.d(TAG, "Loading provider " + info.authority + ": "  
                        + info.name);  
            }  
            Context c = null;  
            ApplicationInfo ai = info.applicationInfo;  
            Slog.d(TAG, "installProvider: context.getPackageName()=" + context.getPackageName());  
            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);    //创建ContentProvider所在的package的Context;  
                } catch (PackageManager.NameNotFoundException e) {  
                    // Ignore  
                }  
            }  
            if (c == null) {  
                Slog.w(TAG, "Unable to get context for package " +  
                      ai.packageName +  
                      " while loading content provider " +  
                      info.name);  
                return null;  
            }  
            try {  
                final java.lang.ClassLoader cl = c.getClassLoader();  
                localProvider = (ContentProvider)cl.  
                    loadClass(info.name).newInstance();    //实例化ContentProvider对象;  
                provider = localProvider.getIContentProvider();  
                if (provider == null) {  
                    Slog.e(TAG, "Failed to instantiate class " +  
                          info.name + " from sourceDir " +  
                          info.applicationInfo.sourceDir);  
                    return null;  
                }  
                if (DEBUG_PROVIDER) Slog.v(  
                    TAG, "Instantiating local provider " + info.name);  
                // XXX Need to create the correct context for this provider.  
                localProvider.attachInfo(c, info);  
            } catch (java.lang.Exception e) {  
                if (!mInstrumentation.onException(null, e)) {  
                    throw new RuntimeException(  
                            "Unable to get provider " + info.name  
                            + ": " + e.toString(), e);  
                }  
                return null;  
            }  
        } else {                        //holder.provider!=null,进程A中调用该函数时满足这个逻辑,holder.provider指向mTransport的代理;  
            provider = holder.provider;  
            if (DEBUG_PROVIDER) Slog.v(TAG, "Installing external provider " + info.authority + ": "  
                    + info.name);  
        }  
      
        IActivityManager.ContentProviderHolder retHolder;  
        //下面的逻辑主要是进行本地保存provider,方便第二次调用同一个ContentProvider时,无需重新到AMS中去查询。  
        synchronized (mProviderMap) {  
            if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider  
                    + " / " + info.name);  
            IBinder jBinder = provider.asBinder();  
            if (localProvider != null) {  
                ComponentName cname = new ComponentName(info.packageName, info.name);  
                ProviderClientRecord pr = mLocalProvidersByName.get(cname);  
                if (pr != null) {  
                    if (DEBUG_PROVIDER) {  
                        Slog.v(TAG, "installProvider: lost the race, "  
                                + "using existing local provider");  
                    }  
                    provider = pr.mProvider;  
                } else {  
                    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 {  
                ProviderRefCount prc = mProviderRefCountMap.get(jBinder);  
                if (prc != null) {  
                    if (DEBUG_PROVIDER) {  
                        Slog.v(TAG, "installProvider: lost the race, updating ref count");  
                    }  
                    // We need to transfer our new reference to the existing  
                    // ref count, releasing the old one...  but only if  
                    // release is needed (that is, it is not running in the  
                    // system process).  
                    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;  
    }  
    

    上面所说的只是普通情况,当然还有特殊情况。比如这个ContentProvider定义时设置了multiprocess=true,那么ContentProvider便会在进程A中进行实例化,而是不是在进程B中实例化,同时也没必要启动进程B了,意味着哪个进程去get ContentProvider,哪个进程自己实例化ContentProvider,这个读者可以仔细分析AMS.getContentProviderImpl()函数什么时候return的holder.provider = null来验证这一点。

    附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) {  
            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);  
                }  
            }  
      
            // First check if this content provider has been published...  
            cpr = mProviderMap.getProviderByName(name, userId);   //②根据authority获取ContentProviderRecord,如果之前publish过ContentProvider,那么返回值必然不为null。  
            boolean providerRunning = cpr != null;  
            if (providerRunning) {         //②providerRunning=true表示ContentProvider已经启动过了;  
                cpi = cpr.info;  
                String msg;  
                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {  
                    throw new SecurityException(msg);  
                }  
      
                if (r != null && cpr.canRunHere(r)) {            //③如果ContentProvider允许多进程实例化或目标进程就是调用进程,那么直接返回。返回的时候将ContentProviderHolder. Provider置空,这样调用进程便会在自己的进程中实例化ContentProvider。  
                    // This provider has been published or is in the process  
                    // of being published...  but it is also allowed to run  
                    // in the caller's process, so don't make a connection  
                    // and just let the caller instantiate its own instance.  
                    ContentProviderHolder holder = cpr.newHolder(null);  
                    // don't give caller the provider object, it needs  
                    // to make its own.  
                    holder.provider = null;  
                    return holder;  
                }                  
                final long origId = Binder.clearCallingIdentity();  
      
                // In this case the provider instance already exists, so we can  
                // return it right away.  
                conn = incProviderCountLocked(r, cpr, token, stable);<span style="white-space:pre">   </span>//④创建一个ContentProviderConnection;  
                if (conn != null && (conn.stableCount+conn.unstableCount) == 1) {  
                    if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {  
                        // If this is a perceptible app accessing the provider,  
                        // make sure to count it as being accessed and thus  
                        // back up on the LRU list.  This is good because  
                        // content providers are often expensive to start.  
                        updateLruProcessLocked(cpr.proc, false, null);  
                    }  
                }  
      
                if (cpr.proc != null) {  
                    if (false) {  
                        if (cpr.name.flattenToShortString().equals(  
                                "com.android.providers.calendar/.CalendarProvider2")) {  
                            Slog.v(TAG, "****************** KILLING "  
                                + cpr.name.flattenToShortString());  
                            Process.killProcess(cpr.proc.pid);  
                        }  
                    }  
                    boolean success = updateOomAdjLocked(cpr.proc);    //返回false表示cpr.proc被杀了  
                    if (DEBUG_PROVIDER) Slog.i(TAG, "Adjust success: " + success);  
                    // NOTE: there is still a race here where a signal could be  
                    // pending on the process even though we managed to update its  
                    // adj level.  Not sure what to do about this, but at least  
                    // the race is now smaller.  
                    if (!success) {                 
                        // Uh oh...  it looks like the provider's process  
                        // has been killed on us.  We need to wait for a new  
                        // process to be started, and make sure its death  
                        // doesn't kill our process.  
                        Slog.i(TAG,  
                                "Existing provider " + cpr.name.flattenToShortString()  
                                + " is crashing; detaching " + r);  
                        boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);  
                        appDiedLocked(cpr.proc, cpr.proc.pid, cpr.proc.thread);  
                        if (!lastRef) {  
                            // This wasn't the last ref our process had on  
                            // the provider...  we have now been killed, bail.  
                            return null;  
                        }  
                        providerRunning = false;   
                        conn = null;  
                    }  
                }  
      
                Binder.restoreCallingIdentity(origId);  
            }  
      
            boolean singleton;  
            if (!providerRunning) {        // ⑤providerRunning=false,有两种情况:ContentProvider所在进程没有启动过、ContentProvider所在进程启动了,并publish了ContentProvider,但是挂掉了。  
                try {  
                    cpi = AppGlobals.getPackageManager().  
                        resolveContentProvider(name,  
                            STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);  
                } catch (RemoteException ex) {  
                }  
                if (cpi == null) {  
                    return null;  
                }  
                singleton = isSingleton(cpi.processName, cpi.applicationInfo,  
                        cpi.name, cpi.flags);   
                if (singleton) {  
                    userId = 0;  
                }  
                cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);  
      
                String msg;  
                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {  
                    throw new SecurityException(msg);  
                }  
      
                if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate  
                        && !cpi.processName.equals("system")) {  
                    // If this content provider does not run in the system  
                    // process, and the system is not yet ready to run other  
                    // processes, then fail fast instead of hanging.  
                    throw new IllegalArgumentException(  
                            "Attempt to launch content provider before system ready");  
                }  
      
                // Make sure that the user who owns this provider is started.  If not,  
                // we don't want to allow it to run.  
                if (mStartedUsers.get(userId) == null) {  
                    Slog.w(TAG, "Unable to launch app "  
                            + cpi.applicationInfo.packageName + "/"  
                            + cpi.applicationInfo.uid + " for provider "  
                            + name + ": user " + userId + " is stopped");  
                    return null;  
                }  
      
                ComponentName comp = new ComponentName(cpi.packageName, cpi.name);  
                cpr = mProviderMap.getProviderByClass(comp, userId);          
                final boolean firstClass = cpr == null;  
                if (firstClass) {                        //这是第一种情况,ContentProvider所在进程未启动过,那么new ContentProviderRecord  
                    try {  
                        ApplicationInfo ai =  
                            AppGlobals.getPackageManager().  
                                getApplicationInfo(  
                                        cpi.applicationInfo.packageName,  
                                        STOCK_PM_FLAGS, userId);  
                        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);  
                    } catch (RemoteException ex) {  
                        // pm is in same process, this will never happen.  
                    }  
                }  
      
                if (r != null && cpr.canRunHere(r))  {       //⑤如果ContentProvider允许多进程实例化,那么直接返回,让调用进程自己实例化ContentProvider;  
                    // If this is a multiprocess provider, then just return its  
                    // info and allow the caller to instantiate it.  Only do  
                    // this if the provider is the same user as the caller's  
                    // process, or can run as root (so can be in any process).  
                    return cpr.newHolder(null);  
                }  
      
      
                // This is single process, and our app is now connecting to it.  
                // See if we are already in the process of launching this  
                // provider.  
                final int N = mLaunchingProviders.size();  
                int i;  
                for (i=0; i<N; i++) {       //查询是否有其他进程正在加载该Provider  
                    if (mLaunchingProviders.get(i) == cpr) {  
                        break;  
                    }  
                }  
      
                // If the provider is not already being launched, then get it  
                // started.  
                if (i >= N) {      
                    final long origId = Binder.clearCallingIdentity();  
      
                    try {  
                        // Content provider is now in use, its package can't be stopped.  
                        try {  
                            AppGlobals.getPackageManager().setPackageStoppedState(  
                                    cpr.appInfo.packageName, false, userId);  
                        } catch (RemoteException e) {  
                        } catch (IllegalArgumentException e) {  
                            Slog.w(TAG, "Failed trying to unstop package "  
                                    + cpr.appInfo.packageName + ": " + e);  
                        }  
      
                        // Use existing process if already started  
                        ProcessRecord proc = getProcessRecordLocked(  
                                cpi.processName, cpr.appInfo.uid, false);  
                        if (proc != null && proc.thread != null) {  
                            if (DEBUG_PROVIDER) {  
                                Slog.d(TAG, "Installing in existing process " + proc);  
                            }  
                            proc.pubProviders.put(cpi.name, cpr);  
                            try {  
                                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);  
                            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);  
                    }  
                }  
      
                // Make sure the provider is published (the same provider class  
                // may be published under multiple names).  
                if (firstClass) {  
                    mProviderMap.putProviderByClass(comp, cpr);  
                }  
      
                mProviderMap.putProviderByName(name, cpr);  
                conn = incProviderCountLocked(r, cpr, token, stable);  
                if (conn != null) {  
                    conn.waiting = true;  
                }  
            }  
        }  
      
        // Wait for the provider to be published...  
        synchronized (cpr) {        //⑦等待Provider被Publish,目标进程publish后会调用notifiyAll()接口,此时当前线程便可返回;  
            while (cpr.provider == null) {  
                if (cpr.launchingApp == null) {  
                    Slog.w(TAG, "Unable to launch app "  
                            + cpi.applicationInfo.packageName + "/"  
                            + cpi.applicationInfo.uid + " for provider "  
                            + name + ": launching app became null");  
                    EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,  
                            UserHandle.getUserId(cpi.applicationInfo.uid),  
                            cpi.applicationInfo.packageName,  
                            cpi.applicationInfo.uid, name);  
                    return null;  
                }  
                try {  
                    if (DEBUG_MU) {  
                        Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="  
                                + cpr.launchingApp);  
                    }  
                    if (conn != null) {  
                        conn.waiting = true;  
                    }  
                    cpr.wait();  
                } catch (InterruptedException ex) {  
                } finally {  
                    if (conn != null) {  
                        conn.waiting = false;  
                    }  
                }  
            }  
        }  
        return cpr != null ? cpr.newHolder(conn) : null;  
    }  
    

    相关文章

      网友评论

          本文标题:ContentProvider笔记[转]

          本文链接:https://www.haomeiwen.com/subject/yqobattx.html