Android中ContentProvider的启动与请求源码流程详解(基于api29)

前言

你好 ~
我是一只修仙的猿,欢迎阅读我的文章。

ContentProvider作为四大组件之一,但是使用的频率确实很少,甚至有一些读者都没用过他,真是毫无存在感的四大组件。但是既然他能作为四大组件,说明他的重要性肯定不低,只是目前来说我们使用不到。

ContentProvider的作用是跨进程共享数据,他向我们屏蔽了底层的Binder操作,使得我们可以像查询本地的数据一样查询别的进程的数据,让跨进程的数据共享变得非常方便。如我们查询手机的通讯录、短信等,都是通过ContentProvider来实现的。

ContentProvider的使用方式也很简单,内容提供方只需要继承ContentProvider类,并实现对应的onCreate,query,delete,insert,update,getType方法即可。内容获取方只需要通过Content.getContentResolver()获取到ContentResolver对象然后通过uri直接调用对应的ContentProvider的方法就可以增删查改数据了。

这篇文章我们要讨论的不是如何使用ContentProvider,而是ContentProvider启动以及请求的流程。了解他背后的工作机制,有助于我们更好地了解ContentProvider。文章涉及到很多的源码,我以“思路先行,代码后讲”的思维来讲解这一过程。先对总体有个感知,这样不会在海量的源码中迷失,再对具体代码进行研究会有更深刻的理解。希望读者可以认真看完每一处的代码,我更加建议把一边看文章一边自己查看源码,这样可以加深理解。文章源码我加了非常多的注释,帮助理解每一处关键代码。文章涉及到跨进程通信,如果对此还没有学习的话,可能看起来一些步骤比较难以理解,建议先学一下跨进程通信AIDL,当然,文中我也会简单介绍一下。

笔者才疏学浅,有不同观点欢迎评论区或私信讨论。如需转载请留言告知。
另外欢迎阅读笔者的个人博客一只修仙的猿的个人博客,更精美的UI,拥有更好的阅读体验。

本文大纲:

本文大纲

工作流程总体思路

ContentProvider的工作离不开AMS(ActivityManagerService),事实上,四大组件的工作流程都离不开AMS。我们在创建一个ContentProvider的时候,除了新建一个类继承并重写方法,还需要在AndroidManifest中进行注册,而AndroidManifest就是AMS进行处理的。AndroidManifest会在当前应用被创建时进行处理,因而ContentProvider是伴随着应用进程的启动而被创建的。下面我们看一张图了解一下ContentProvider的启动流程:

ContentProvider启动流程

ContentProvider是在应用进程被创建的时候被创建的,所以这个流程也是进程创建的流程。进程间的通信是通过Binder机制实现的。

  • 应用进程创建后的代码入口是ActivityThread的main函数,然后他通过Binder机制把ApplicationThread发送给AMS进行缓存,以后AMS要访问该进程就是通过ApplicationThread来访问的。
  • AMS做处理之后,通过ApplicationThread来让ActivityThread进行初始化。
  • 在ActivityThread初始化中会创建ContextImp,Application和ContentProvider。
  • 然后ActivityThread把ContentProvider发布到AMS中。以后如果有别的进程想要读取数据可以直接从AMS中获取到对应的ContentProvider。

这就是ContentProvider的启动流程了。当然涉及到跨进程通信,不可能直接以ContentProvider来通信,而是以IContentProvider来通信,同样也是利用AIDL技术,下面我会详细讲到。那么一个应用请求另一个应用的ContentProvider的流程是怎么样的呢?先看图:

ContentProvider的请求流程

好像看起来很复杂对吧?其实就是判断的情况比较多了。整体的思路就是:获取到对应URI的IContentProvider,然后跨进程访问数据,所以重点在获取IContentProvider。

  • 首先会查询本地是否有和URI对应的IContentProvider,有的话直接使用,没有的话去AMS获取。
  • 如果ContentProvider对应的进程已启动,那么AMS一般是有该IContentProvider的,原因在上面的启动有讲。但是如果应用尚未启动,那么需要AMS去启动该进程。
  • 进程B启动后会自然把IContentProvider发布给AMS,AMS先把他缓存起来,然后再返回给进程A
  • 进程A拿到IContentProvider之后,就是直接跨进程访问ContentProvider了。

可以看到整体的思路并不复杂,那么下面我会根据这些流程,按照源码详细走一遍。还是再强调一下,源码中我加了大量的注释,千万不要忽视源码中的注释。

源码讲解

ContentProvider的启动流程

ContentProvider的启动源码流程
  1. ContentProvider是在AndroidManifest中进行静态注册的,也就说明了他是在应用启动的时候被创建的。我们要了解他的启动流程,就必须了解应用的启动流程。这里不深入讲应用的启动流程,应用进程启动最终会调用ActivityThread的main方法,有兴趣的读者可以去深入了解一下。那么我们就可以定位到main方法中,看看这个方法做了什么事情。

    /frameworks/base/core/java/android/app/ActivityThread.java;
    public static void main(String[] args) {
         
        ...
        Looper.prepareMainLooper();
        ...;
        // 这里创建了ActivityThread对象,并紧接着调用了他的attach方法
        // 初始化是在attach中完成的,我们进入看一下
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
    	...;
        // 这里开启主线程的消息循环
        Looper.loop();
    }
    
    private void attach(boolean system, long startSeq) {
         
        // 这里判断是否是系统进程我们的应用自然不是
        if (!system) {
         
            ...
            //这个地方比较复杂,先说结论。下面再进行解释    
        	//ActivityManager.getService()获取到的对象是ActivityManagerService,简称AMS
        	//通过AMS来启动activity。AMS是全局唯一的,所有的活动启动都要经过他的验证,运行在独立的进程中
        	//所以这里是采用AIDL的方式进行跨进程通信,获取到的对象其实是一个IBinder接口
            final IActivityManager mgr = ActivityManager.getService();
            try {
         
                // 这里通过访问AMS把IApplicationThread发送给AMS
                mgr.attachApplication(mAppThread, startSeq);
            } catch (RemoteException ex) {
         
                throw ex.rethrowFromSystemServer();
            }
        }
    }
    

    这里深入讲一下ActivityManager.getService()。

    /frameworks/base/core/java/android/app/ActivityManager.java/;
    // 这里可以看出来是个单例类,可以直接获取到单例
    public static IActivityManager getService() {
          
    	return IActivityManagerSingleton.get();
    }
    
    private static final Singleton<IActivityManager> IActivityManagerSingleton =
    new Singleton<IActivityManager>() {
          
       @Override
       protected IActivityManager create() {
          
       // 得到AMS的IBinder对象
       final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
       // 转化成IActivityManager对象。远程服务实现了这个接口,所以可以直接调用这个
       // AMS代理对象的接口方法来请求AMS。这里采用的技术是AIDL
       final IActivityManager am = IActivityManager.Stub.asInterface(b);
       return am;
       }
    };
    
  2. AMS逻辑很多,但是我们只要注意到他最终会调用IApplicationThread的bindApplication方法回到本地进程进行初始化。AMS这里的处理包括了读取AndroidManifest,特别是ContentProvider的信息,后面会讲到。然后把初始化工作交给了ActivityThread去处理。

    /frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java/;
    public final void attachApplication(IApplicationThread thread, long startSeq) {
         
        synchronized (this) {
         
            ...
            // 直接跳转下个方法
            attachApplicationLocked(thread, callingPid, callingUid, startSeq);
            Binder.restoreCallingIdentity(origId);
        }
    }
    
    private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid, int callingUid, long startSeq) {
         
        ...
        final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
        if (app.isolatedEntryPoint != null) 
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值