ContentProvider启动流程分析(三)

ContentProvider启动流程分析(三)_第1张图片

0x01 扯东扯西的前言&概述


本片博客对应时序图上的step15—26:上接第二篇博客:ContentProvider启动流程分析二!

同时此系列博客同步在博客园发布:ContentProvider启动流程分析系列!详情戳这里即可访问~

0x02 ContentProvider启动流程分析


step15: ApplicationThread#bindApplication()

上一步,在ApplicationThreadProxy类的bindApplication()函数中,通过Binder对象mRemote发出了一个进程间通信请求!

反查源码很容易知道,bindApplication()函数,其实是IApplicationThread接口类的接口函数,而ActivityThread类的内部类ApplicationThread实现了这一接口,所以自然也就实现了接口函数bindApplication(),我们来看ApplicationThread类的函数bindApplication()源码删减如下:

public final void bindApplication(String processName, ApplicationInfo appInfo,
            List providers, ComponentName instrumentationName,
            ProfilerInfo profilerInfo, Bundle instrumentationArgs,
            IInstrumentationWatcher instrumentationWatcher,
            IUiAutomationConnection instrumentationUiConnection, int debugMode,
            boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
            Configuration config, CompatibilityInfo compatInfo, Map 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类的成员函数bindApplication(),就是类型为BIND_APPLICATION_TRANSACTION的进程间通信请求的真正处理者!处理的逻辑是:先把将要启动的ContentProvider组件列表,封装成一个AppBindData对象;
  • 然后再调用外部类ActivityThread的成员函数sendMessage(),再次将这个AppBindData对象封装成一个类型为BIND_APPLICATION的消息,以便可以发送到新建应用程序进程的主线程的消息队列中!

step16: ActivityThread#sendMessage()

ActivityThread类持有一个mH成员变量,mH实际上是一个Handler对象,通过mH.sendMessage()发送消息到新建应用程序进程的主线程的消息队列中!所以,这个消息最后是最终是在mH.handleMessage()函数中处理的。

step17: ActivityThread#handleMessage()

ActivityThread内部类H的程艳方法handleMessage()源码删减如下:

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        ......
    case BIND_APPLICATION:
        AppBindData data = (AppBindData)msg.obj;
        handleBindApplication(data);
        break;
        ......
    }
    if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
  • 在handleMessage()函数中,我们只考虑BIND_APPLICATION的情况,首先将Message对象msg的成员变量obj强转成个AppBindData对象,它包含了要启动的ContentProvider列表。
  • 然后,调用handleBindApplication()函数把这些ContentProvider组件启动起来!

接下来,关注handleBindApplication()函数,是怎样把这些ContentProvider组件启动起来的?

step18: ActivityThread#handleBindApplication()

ActivityThread类的成员函数handleBindApplication()源码如下:

private void handleBindApplication(AppBindData data) {
    ......
    // don't bring up providers in restricted mode; they may depend on the
    // app's custom Application class
    if (!data.restrictedBackupMode) {
        List providers = data.providers;
        if (providers != null) {
            installContentProviders(app, providers);
            ......
        }
    }
}

参数data是一个AppBindData对象,data的成员变量providers保存了,需要在当前进程中启动的ContentProvider组件,接下来则会调用ActivityThread类的成员函数installContentProviders()来启动这些组件!

step19: ActivityThread#installContentProviders()

ActivityThread类的成员函数installContentProviders()源码如下:

private void installContentProviders(
    Context context, List providers) {
    final ArrayList results =
        new ArrayList();

    for (ProviderInfo cpi : providers) {
        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) {
    }
}
  • for-each循环得到保存在providers中的每一个组件ProviderInfo对象cpi,然后调用installProvider()函数启动每个cpi对应的ContnetProvider组件;
  • 这些组件启动之后,就可以获得其对应的一个IContentProvider接口;然后,把这个接口封装成一个ContentProviderHolder对象;
  • 最后调用AMS代理对象(也即,ActivityManagerProxy)的成员函数publishContentProviders(),将这些ContentProviderHolder对象传递给AMS;AMS正是通过这些ContentProviderHolder对象获取它所描述的ContentProvider组件的一个访问接口;

接下来,先分析ActivityThread类成员函数installProvider()在当前应用程序进程中启动一个ContentProvider组件的过程;然后,分析AMS代理对象ActivityManagerProxy成员函数publishContentProviders()将启动完成的组件发布到AMS的过程!

step20: ActivityThread#installProvider()

ActivityThread类成员函数installProvider()源码如下:

/**
 * Installs the provider.
 *
 * Providers that are local to the process or that come from the system server
 * may be installed permanently which is indicated by setting noReleaseNeeded to true.
 * Other remote providers are reference counted.  The initial reference count
 * for all reference counted providers is one.  Providers that are not reference
 * counted do not have a reference count (at all).
 *
 * This method detects when a provider has already been installed.  When this happens,
 * it increments the reference count of the existing provider (if appropriate)
 * and returns the existing provider.  This can happen due to concurrent
 * attempts to acquire the same provider.
 */
private IActivityManager.ContentProviderHolder installProvider(Context context,
        IActivityManager.ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    // holder为空,表示参数info所描述的ContentProvider组件需要在当前进程中启动
    if (holder == null || holder.provider == null) {
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        if (context.getPackageName().equals(ai.packageName)) {
            c = context;
        }
        ......
        final java.lang.ClassLoader cl = c.getClassLoader();
        localProvider = (ContentProvider)cl.
                        loadClass(info.name).newInstance();
        provider = localProvider.getIContentProvider();
        if (provider == null) {
            return null;
        }
        // XXX Need to create the correct context for this provider.
        localProvider.attachInfo(c, info);
    } else {
        provider = holder.provider;
    }

    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);
            if (pr != null) {
                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) {
                ......
            } 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;
}
  • 对传进来的参数holder判空,如果为null,表示要在当前应用程序进程中,将参数info所描述的ContentProvider组件启动起来;另一个参数context描述了将要启动的ContentProvider组件运行的上下文环境;
  • localProvider表示一个ContentProvider组件对象,通过localProvider.getIContentProvider(),也即调用localProvider的成员函数getIContentProvider()来获得该组件对应的一个IContentProvider接口provider;这个IContentProvider最终需要发布到AMS,AMS会将它返回给那些需要访问它所描述的ContentProvider组件的调用方应用程序进程;
  • 接下来通过localProvider.attachInfo()来进一步初始化前面所创建的ContentProvider组件;
  • 接下来对全局变量mProviderMap加同步锁,然后对localProvider判空,正常情况下此时的localProvider应该是非空的,所以,自然要把它保存到mLocalProviders和mLocalProvidersByName中;
  • 但是如果localProvider仍然为空,接下来会出现一个ProviderRefCount对象prc,用来表示和记录ContentProvider组件的引用计数,如果prc为空,则把这个localProvider封装成一个ProviderClientRecord对象,并保存在prc变量中;
  • 最后,返回这个ContentProviderHolder对象!

接下来,继续分析ContentProvider组件的成员函数getIContentProvider()和成员函数attachInfo()的实现细节!

step21: ContentProvider#getIContnetProvider()

ContentProvider类成员函数getIContentProvider()源码如下:

public abstract class ContentProvider implements ComponentCallbacks2 {
    ......
    private Transport mTransport = new Transport();
    ......
    class Transport extends ContentProviderNative {......}
    ......
    public IContentProvider getIContentProvider() {
        return mTransport;
    }
    ......
}
  • 每一个ContentProvider组件都有一个内部类Transport,其本质是一个Binder本地对象;并且持有一个类型为Transport的全局变量mTransPort来表示一个Binder本地对象。
  • 通过将这个Binder本地对象传递给AMS,然后AMS会将引用了这个Binder本地对象的一个Binder代理对象返回给需要访问该ContentProvider组件的其他应用程序进程;这样,其他应用程序进程就可以通过这个Binder代理对象来间接的访问一个ContentProvider组件中的数据了!
  • ContentProvider类成员函数getIContentProvider()的实现很简单,只是简单地将全局变量mTransPort描述的一个IContentProvider接口返回给调用者。

step22: ContentProvider#attachInfo()

ContentProvider类的成员函数attachInfo(),作用是初始化前面所创建的一个ContentProvider组件!attachInfo()源码如下:

public abstract class ContentProvider implements ComponentCallbacks2 {
    ......
    /**
     * After being instantiated, this is called to tell the content provider
     * about itself.
     *
     * @param context The context this provider is running in
     * @param info Registered information about this content provider
     */
    public void attachInfo(Context context, ProviderInfo info) {
        attachInfo(context, info, false);
    }

    private void attachInfo(Context context, ProviderInfo info, boolean testing) {
        ......
        /*
         * Only allow it to be set once, so after the content service gives
         * this to us clients can't change it.
         */
        if (mContext == null) {
            mContext = context;
            if (context != null) {
                mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                        Context.APP_OPS_SERVICE);
            }
            mMyUid = Process.myUid();
            if (info != null) {
                setReadPermission(info.readPermission);
                setWritePermission(info.writePermission);
                setPathPermissions(info.pathPermissions);
                ......
            }
            ContentProvider.this.onCreate();
        }
    }
    ......
}
  • 依然是两个重载函数,接收两个参数的attachInfo()函数内部调用了接收三个参数的attachInfo()函数,我们直接关注接收三个参数的attachInfo()函数;
  • setReadPermission(info.readPermission),setWritePermission(info.writePermission)和setPathPermissions(info.pathPermissions)这三个函数的作用是,设置ContentProvider组件的读写权限和访问权限;
  • 最后ContentProvider.this.onCreate()函数,实际调用的是COntentProvider子类的onCreate()函数,以便在子类的onCreate()函数中,执行一些业务相关的初始化操作!
  • 在我们最开始的场景中,ContentProvider组件指的就是BxxApp应用程序进程中SubContentProvider组件,所以上面调用的时加上就是SubContentProvider类的onCreate()函数,而我们的业务逻辑相关的一些初始化工作,也正是放在SubContentProvider类的onCreate()函数中执行的!

step23: SubContentProvider#onCreate()

public class SubContentProvider extends ContentProvider {
    ......
    @Override
    public boolean onCreate() {
        ContentResolver resolver = getContext().getContentResolver();
        DatabaseHelper helper = new DtatabaseHelper(......);
        ......
        return true;
    }
    ......
}
  • 需要注意,由于一个ContentProvider组件再启动过程中需要执行onCreate()函数,因此,我们应该避免在onCeate()方法中执行耗时操作,例如和IO相关的操作,否则可能造成这个ContentProvider组件启动超时!

这一步执行完成后,就会返回到前面的step20,然后再次返回到前面的step19,即ActivityThread类成员函数installContentProviders()中,接下来就会调用AMS代理对象的成员函数publishContentProviders(),也即ActivityManagerProxy的成员函数publishContentProviders(),将前面所有启动的ContentProvider组件的一个IContentProvider访问接口发布到ActivityManagerService中~

step24: ActivityManagerProxy#publishContentProviders()

ActivityManagerProxy类成员函数publishContentProviders(),会将所有启动的ContentProvider组件的一个IContentProvider访问接口发布到ActivityManagerService中,源码如下:

public void publishContentProviders(IApplicationThread caller,
        List providers) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeTypedList(providers);
    mRemote.transact(PUBLISH_CONTENT_PROVIDERS_TRANSACTION, data, reply, 0);
    reply.readException();
    data.recycle();
    reply.recycle();
}
  • 先把接收的参数保存到Parcel对象data中;
  • 接着再通过ActivityManagerProxy类内部的一个Binder代理对象mRemote向ActivityMnagerService发送一个类型为PUBLISH_CONTENT_PROVIDERS_TRANSACTION进程间通信请求;

step25: ActivityManagerService#publishContentProviders()

以上10步都是在新建的应用程序进程中执行的!step25是在ActivityManagerService中执行的,AMS类成员函数publishContentProviders(),用来处理类型为PUBLISH_CONTENT_PROVIDERS_TRANSACTION进程间通信请求,其源码如下:

ContentProvider启动流程分析(三)_第2张图片
AMS#publishContentProviders()
  • 参数caller是一个类型为ApplicationThread的Binder代理对象,它引用了运行在新建应用程序进程中的一个ApplicationThread对象,getRecordForAppLocked(caller)方法通过caller来获取一个用来描述新建应用程序进程的ProcessRecord对象r;
  • 新建应用程序进程在启动时,会将需要在它里面运行的ContentProvider组件启动起来!从前面的step13可知,在AMS中,这些ContentProvider组件使用一个ContentProviderRecord对象来描述,它们保存在用来描述新建应用程序进程的一个ProcessRecord对象r的一个成员变量pubProviders中;
  • 第10到14行,第一个for循环,取出providers中的每一个ContentProvider组件,并且拿到ContentProvider组件对应的ContentProviderRecord对象dst;
  • 第15到22行,第二个for循环,通过两种方式,把ContentProviderRecord对象dst保存到全局变量mProviderMap;
  • 参数providers包含了要发布到AMS中的ContentProvider组件,每一个ContentProvider组件都使用一个ContentProviderHolder对象来描述,它里面包含了要发布的ContentProvider组件的一个IContentProvider接口,如图第34行到40行所示!

从前面的step8可知,一个应用程序进程请求ActivityManagerService返回一个ContentProvider组件的代理对象时,如果这个ContentProvider组件还未启动起来,那么AMS就会先创建一个新的应用程序进程来启动该ContentProvider组件,然后再在一个while循环中等待该ContentProvider组件启动完成,并且将他的一个代理对象发布到AMS中。

现在既然这个ContentProvider已经启动完成,并且将它的一个代理对象,即一个类型为Transport的Binder代理对象发布到AMS,因此,前面正在等待的一个AMS线程就可以停止等待,并且将这个类型为Transport的Binder代理对象封装成一个ContentProvider对象返回给请求它的应用程序进程。

这一步执行完毕,就会使得AMS从前面的step8返回step5,即返回到MainActivity组件所运行的应用程序进程中,即AxxApp应用程序进程!然后,继续执行ActivityThread类成员函数installProvider(),用来保存前面从ActivityManagerService获得的一个ContentProvider组件的一个IContentProvider访问接口。

step26: ActivityThread#installProvider()

ActivityThread类成员函数installProvider()源码如下:

/**
 * Installs the provider.
 *
 * Providers that are local to the process or that come from the system server
 * may be installed permanently which is indicated by setting noReleaseNeeded to true.
 * Other remote providers are reference counted.  The initial reference count
 * for all reference counted providers is one.  Providers that are not reference
 * counted do not have a reference count (at all).
 *
 * This method detects when a provider has already been installed.  When this happens,
 * it increments the reference count of the existing provider (if appropriate)
 * and returns the existing provider.  This can happen due to concurrent
 * attempts to acquire the same provider.
 */
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;
        } else {
            try {
                c = context.createPackageContext(ai.packageName,
                                                 Context.CONTEXT_INCLUDE_CODE);
            } catch (PackageManager.NameNotFoundException e) {
                // Ignore
            }
        }
        if (c == null) {
            return null;
        }
        try {
            final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                            loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
            if (provider == null) {
                return null;
            }
            // XXX Need to create the correct context for this provider.
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {......}
    } else {
        provider = holder.provider;
    }

    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);
            if (pr != null) {
                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) {
                // 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;
}
  • 这步与前面的step20,都是在ActivityThread类成员函数installProvider()。不过,前面step20是在启动ContentProvider组件的应用程序进程中执行的;而这步是在AxxApp应用程序的MainActivity中执行的!另一个区别是:这步得到参数provider不等于null,它用来描述一个在其他应用程序进程中启动的ContentProvider组件的一个IContentProvider访问接口。

  • 这步执行完成后,就返回到前面的step1,这时在AxxApp应用的MainActivity中,就获得了与URI值对应的SubContentProvider组件的一个IContentProvider访问接口,最后就可以通过这个接口访问另一个应用程序的数据内容了!

0x03 参考文献与简单的结语


至此,ContentProvider启动流程分析到此结束!

你可能感兴趣的:(ContentProvider启动流程分析(三))