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

一,写在前面

       在文章 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的工作流程有比较好的认识了。同时,本篇文章到这里就结束啦~        ^_^
                                 

       

















       




       

       








  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值