ContentProvider启动流程分析(三)

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<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类的成员函数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<ProviderInfo> 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<ProviderInfo> providers) {
	final ArrayList<IActivityManager.ContentProviderHolder> results =
	    new ArrayList<IActivityManager.ContentProviderHolder>();

	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<ContentProviderHolder> 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进程间通信请求,其源码如下:

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启动流程分析到此结束!

posted @ 2016-10-13 21:09  布鲁克林一棵树  阅读(569)  评论(0编辑  收藏  举报