您的位置:首页 > 移动开发 > Android开发

Android ContentProvider启动流程源码解析(8.0)

2017-12-02 00:19 363 查看

一,写在前面

       在文章 Android 如何自定义一个ContentProvider中,介绍了如何使用以及自定义一个ContentProvider,本篇不再介绍如何使用ContentProvider访问其他应用的数据。在阅读本篇文章前,建议先了解Activity的启动流程,可参考文章 Android
Activity的启动流程源码解析(8.0) ,将不再对重复的代码细节进行分析。


二,启动ContentProvider的入口

       如何启动一个ContentProvider呢?在一个Activity类中,可以调用getContentResolver().query(...)查询指定uri对应的数据,在这个过程中若ContentProvider没有启动,则会启动ContentProvider。当然,相应的insert,delete,update方法也可以启动ContentProvider,本篇文章以query方法为例进行分析。
       获取ContentResolver的对象,实际上调用的是ContextWrapper$getContentResolver方法,ContextWrapper是抽象类Context的一个子类。 
       查看ContextWrapper$getContentResolver方法源码:
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
       第3行,变量mBase对应的是ContextImpl对象,分析见Android Activity的启动流程源码解析(8.0) ,这里不再重复阐述。
       
      查看ContextImpl$getContentResolver方法源码:
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
       第3行,变量mContentResolver的初始化在ContextImpl的构造函数中完成,mContentResolver = new ApplicationContentResolver(this, mainThread, user),也就是返回的是ApplicationContentResolver的实例。值得一提的是,ContextImpl对象的创建是在启动Activity的流程中完成,具体分析见 Android
Activity的启动流程源码解析(8.0) ,这里不再重复阐述。

       ApplicationContentResolver是ContextImpl的一个内部类,且继承类ContentResolver。调用getContentResolver().query(...)方法,是从父类ContentResolver继承的query方法。
       查看ContentResolver$query方法源码:
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder,
@Nullable CancellationSignal cancellationSignal) {

//...

IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}

//...

qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);

//...
}
       第8行,获取一个IContentProvider类型的对象unstableProvider。启动ContentProvider从这里开始分析,后面会详细分析。
       第9行,对变量unstableProvider进行判空检查;
       第15行,根据指定uri,执行查询数据操作,文章的最后会详细分析。

       查看ContentResolver$acquireUnstableProvider方法(一个参数)源码:
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
return acquireUnstableProvider(mContext, uri.getAuthority());
}
return null;
}
       第2行,对uri的scheme进行检查;
       第7行,逻辑跳到ApplicationContentResolver$acquireUnstableProvider方法(两个参数);

       查看ApplicationContentResolver$acquireUnstableProvider方法源码:
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
       第3行,变量mMainThread是一个ActivityThread类型的对象,该变量的初始化也是在ContextImpl的构造函数中完成。

       查看ActivityThread$acquireProvider方法源码:
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
ContentProviderHolder holder = null;

//...

holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);

//...

holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
       第3行,调用ActivityThread$acquireExistingProvider方法,查询Map集合中是否包含该IContextProvider对象,下面会具体分析。
       第4行,若provider不为null,表示该IContextProvider对象已经存在,无需再创建。
       第11行,若provider为null,那么需要创建一个IContextProvider对象。这里就是启动ContentProvider流程的入口,后面会重点具体分析。
       第16行,将11行创建的IContextProvider对象存储在一个Map集合中,起到一个缓存的作用,不用每次创建IContextProvider对象。

       分析ActivityThread$acquireProvider方法的第3行,查看ActivityThread$acquireExistingProvider方法源码:
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
synchronized (mProviderMap) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
}

IContentProvider provider = pr.mProvider;

//...

return provider;
}
}
       第4行,将参数auth封装在ProviderKey对象中,并作为Map集合的key;

       第5行,变量mProviderMap的初始化:final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<ProviderKey, ProviderClientRecord>();当Map集合中不存在该key时,就会返回null。


三,启动ContentProvider,交给AMS处理

       前面已经提到,ActivityThread$acquireProvider方法的第11行是启动ContentProvider的入口。
       第11行代码:holder = ActivityManager.getService().getContentProvider(getApplicationThread(), auth, userId, stable)。ActivityManager.getService()这个已经再熟悉不过了,基于Binder机制,最终会调用系统服务ActivityManagerService$getContentProvider方法。具体分析见文章 Android
Activity的启动流程源码解析(8.0) ,这里不再重复阐述。
        查看ActivityManagerService$getContentProvider方法源码:
@Override
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {

//...

return getContentProviderImpl(caller, name, null, stable, userId);
}

//继续查看...

private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {

//...

ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) {

if (!proc.pubProviders.containsKey(cpi.name)) {

//...

proc.thread.scheduleInstallProvider(cpi);

//...
}
} else {

//...

proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);

//...

}

//...code
}
       第17行,ProcessRecord封装了ContentProvider所在应用程序进程的相关信息;
       第25行,若应用程序进程已经启动,则调用scheduleInstallProvider方法,启动ContentProvider;
       第33行,若应用程序进程没有启动,则调用AMS$startProcessLocked方法启动进程。本篇文章假设应用程序进程没有启动,来研究ContentProvider的启动流程,下面会重点分析这里。


四,入口ActivityThread$main的分析

       为什么要分析应用程序进程的启动呢?因为应用程序进程启动后,接着启动ContentProvider。进程启动是交给ActivityManagerService$startProcessLocked方法来处理,最终会调用ActivityThread$main方法(ActivityThread是代表主线程的实例)。这里推荐一篇博客 Android应用程序进程启动过程 ,文章详细讲解了进程的启动流程。
       
       下面继续分析ActivityThread$main方法,这个方法很重要。想要完全理解Handler机制,了解main方法也是必须的,下面会讲述一些篇外话。
       ActivityThread$main方法查看源码:
public static void main(String[] args) {

//...

Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

//...

Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}
       第5行,创建Looper对象,并将该对象放入ThreadLocal中。
       简单介绍下ThreadLocal:可以使某一个变量在不同线程代表不同的值。在这里用于存放主线程ActivityThread中创建的Looper,当然子线程也可以创建自己的Looper,但不同线程的Looper并不是一个对象。
       值得一提的:调用Looper.prepareMainLooper()方法专门用于创建主线程的Looper对象,在子线程中创建Looper对象也比较熟悉了,一般调用Looper.prepare()方法。当然这两种方式创建Looper是有区别,区别在于主线程的Looper不被允许停掉,具体代码细节分析留给感兴趣哥们验证~

       第7行,创建ActivityThread的实例;
       第8行,调用ActivityThread$attach方法,内部完成ContentProvider的启动过程,后面会重点分析。
       第16行,进行消息循环的操作,内部有一个无限for循环,不断的获取Message对象且处理消息。
       第18行,该行代码被执行的前提是:Looper$loop方法跳出无限for循环。因此需要调用Looper$quit方法停掉Looper,前面已经讲到主线程的Looper不会被停掉。由于不是本篇重点,具体的代码细节不做阐述,这里直接给出结论。也就是说,抛出RuntimeException这个异常还是比较少见的,Android系统也不支持这样做。

       回到ActivityThread$attach方法的调用,它会启动ContentProvider,下面继续分析。
       查看ActivityThread$attach方法源码:
private void attach(boolean system) {

//...

final IActivityManager mgr = ActivityManager.getService();

//...

mgr.attachApplication(mAppThread);

//...
}
       第5行,返回一个IActivityManager接口的代理对象,这个应该比较熟悉了,具体分析可以参考文章 Android Activity的启动流程源码解析(8.0) ;
       第9行,向系统服务AMS发起请求,基于Binder机制,回调用ActivityManagerService$attachApplication方法;变量mAppThread是一个ApplicationThread类型的变量,这个也比较熟悉了,具体分析可以参考文章 Android Activity的启动流程源码解析(8.0) ;

       查看ActivityManagerService相关方法的源码:
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
//...

attachApplicationLocked(thread, callingPid);

//...
}
}

//继续查看...

private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
//...

thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
//...
}
       第17行,变量thread是一个ApplicationThread的对象,调用bindApplication方法完成一次IPC的调用,代码逻辑切换到ApplicationThread中去执行。这个应该比较熟悉了,具体分析可以参考文章 Android Activity的启动流程源码解析(8.0) ,这里不再重复阐述。

       查看ActivityThread$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 enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial) {

//...

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.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
sendMessage(H.BIND_APPLICATION, data);
}
       第13行,创建AppBindData对象,用于封装一些数据。

       第30行,ActivityThread$sendMessage方法的内部代码不再展示。具体来说,使用Handler发送了一个消息,基于Handler的消息机制,将任务切换到Handler所在的线程中执行,于是任务切换到ActivityThread中执行。
       我们会发现,Android四大组件的启动,代码逻辑由系统服务AMS切换到ApplicationThread,然后基于Handler的消息机制,最终切换到主线程ActivityThread中执行。
    
       继续分析消息的处理过程,查看ActivityThread$H$handleMessage方法源码:
public void handleMessage(Message msg) {
switch (msg.what) {

//...

case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;

//...
}
}
       第9行,调用ActivityThread$handleBindApplication方法;

       查看ActivityThread$handleBindApplication方法源码:
private void handleBindApplication(AppBindData data) {

//...

final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

//..

final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = instantiate(cl, data.instrumentationName.getClassName(),
instrContext, Application::instantiateInstrumentation);

//..

Application app = data.info.makeApplication(data.restrictedBackupMode, null);

//...

installContentProviders(app, data.providers);

//...

mInstrumentation.callApplicationOnCreate(app);

//...

}
       handleBindApplication做的事情,有些跟启动Activity的流程比较类似,类似的代码细节就不再重复分析了。
       第5行,创建上下文环境,具体来说是创建ContextImpl对象;
       第10行,获取Instrumentation类型的对象;
       第15行,创建Application对象,由于第2个参数为null,不会调用Application$onCreate方法;
       第19行,调用ctivityThread$installContentProviders方法,启动ContentProvider,后面会重点分析;
       第23行,Instrumentation$callApplicationOnCreate方法内部,会调用Application$onCreate方法,意味着应用程序进程启动完毕。
       
       查看ActivityThread$installContentProviders方法源码:
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();

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());
}
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);

//...
}

//...
}
       第2行,集合providers中存储了应用程序进程中所有的ProviderInfo对象,ProviderInfo封装了ContentProvider相关的信息。
       第5行,遍历集合providers;
       第14行,调用ActivityThread$installProvider方法,ProviderInfo对象作为方法的参数,启动ContentProvider;

       查看ActivityThread$installProvider方法源码:
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;

//..

final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = instantiate(cl, info.name, context,
Application::instantiateProvider);

//...

localProvider.attachInfo(c, info);

//...

}
       第10行,通过ClassLoader创建ContentProvider的实例;
       第15行,调用ContentProvider$attachInfo方法启动ContentProvider;
       
       查看ContentProvider$attachInfo方法源码:
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}

//继续查看...

private void attachInfo(Context context, ProviderInfo info, boolean testing) {

//...

if (mContext == null) {

// ...

ContentProvider.this.onCreate();
}
}
       第15行,哇,调用了ContentProvider$onCreate方法,意味着ContentProvider已经启动了。

五,ContentProvider启动后,分析query的流程

       前面分析了ContentProvider的启动流程,下面继续分析query的流程。回到ContentResolver$query方法,该方法内部调用了IContentProvider$query方法。
       查看IContentProvider源码:
public interface IContentProvider extends IInterface {

//...

public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
throws RemoteException;
public String getType(Uri url) throws RemoteException;
public Uri insert(String callingPkg, Uri url, ContentValues initialValues)
throws RemoteException;
public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
throws RemoteException;
public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
throws RemoteException;
public int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException;

//...

}
       原来,IContentProvider是一个接口,还继承了IInterface接口。有意思的是,在AIDL文件自动生成的java文件中,可以找到一样的结构。但这里并没有类似于Stub的类,在源码中search "extends Binder implements IContentProvider",这个相当于Stub的类就是ContentProviderNative。

       查看ContentProviderNative源码:
abstract public class ContentProviderNative extends Binder implements IContentProvider {
public ContentProviderNative()
{
attachInterface(this, descriptor);
}

static public IContentProvider asInterface(IBinder obj)
{
if (obj == null) {
return null;
}
IContentProvider in =
(IContentProvider)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}

return new ContentProviderProxy(obj);
}

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
switch (code) {
case QUERY_TRANSACTION:
{
//...

Cursor cursor = query(callingPkg, url, projection, queryArgs, cancellationSignal);

//...code

}
return super.onTransact(code, data, reply, flags);
}

//...code

@Override
public IBinder asBinder()
{
return this;
}

final class ContentProviderProxy implements IContentProvider
{
public ContentProviderProxy(IBinder remote)
{
mRemote = remote;
}

@Override
public IBinder asBinder()
{
return mRemote;
}

@Override
public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
throws RemoteException {
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();

//...code

mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);

//...code
}

//...code

}

//...code

}
       哇~这个结构是不是似曾相识,类ContentProviderNative相当于Stub类。接下来,需要找到接口的实现类,源码中search "extends ContentProviderNative ",这个实现类就是ContentProvider的内部类Transport。

       查看ContentProvider$Transport$query源码:
class Transport extends ContentProviderNative {

//...

@Override
public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
@Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {

//...

return ContentProvider.this.query(
uri, projection, queryArgs,
CancellationSignal.fromTransport(cancellationSignal));

//...

}

//...
}
       第11行,哇,最终调用了ContentProvider的query方法,这个不就是我们想要的嘛。这也验证访问ContentProvider的数据,是基于Binder机制完成了一次进程间的通信。
       值得一提的是理解这个流程,需要对AIDL文件生成的java文件有比较好的认识,由于不是本篇重点,这里不进行阐述。

六,最后

       本篇文章分两部分进行,前面一部分,用大量篇幅分析ContentProvider的启动过程,这也是本篇文章的重点;后面一部分,以查询数据为例,分析其他应用是如何访问ContentProvider暴露的数据。相信通过上面的分析,应该对ContentProvider的工作流程有比较好的认识了。同时,本篇文章到这里就结束啦~        ^_^
                                 

       

       

       

       
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: