Android——简单易懂说原理之Android四大组件工作过程(热修复、插件化基础2)

写在前面:这是“简单易懂说原理(热修复、插件化基础)”系列的第二篇文章,也是理解插件化原理的必备基础。阅读本文前须有Android系统启动流程及应用程序进程启动过程相关基础。


在前一篇文章中我们说到应用程序进程在创建的同时也创建了Binder线程池和消息循环。其实,在创建了Binder线程池后,应用程序进程还会创建并运行一个代表主线程的 ActivityThread,他管理着当前应用程序进程的主线程。

在本文中,我们将会经常提到ActivityThread。

一、根Activity的启动过程

根Activity是应用程序启动的第一个Activity,因此根Activity的启动过程在一般情况下我们也可以理解为是应用程序启动的过程。

根Activity的启动过程如下图所示。
在这里插入图片描述

其中,ApplicationThread是ActivityThread的内部类,是一个Binder。

  1. Launcher请求AMS。当我们点击图标时,就会通过Launcher请求AMS来启动该应用程序。其中这里会传递Intent.FLAG_ACTIVITY_NEW_TASK,使根Activity在新的任务栈中启动。
  2. AMS请求ApplicationThread。这部分有些复杂,涉及到两个流程(创建应用程序进程流程和启动根Activity流程),其中创建应用程序进程流程在这里就不赘述了,我们只来讲启动根Activity流程,也就是AMS到ApplicationThread的调用流程。如下图所示:在这里插入图片描述
    可以看到,AMS到ApplicationThread这一段的入口方法是startActivity()。
  • ActivityRecord:用于描述一个Activity,用来记录一个Activity的所有信息。在这里我们在获取完Launcher进程的pid和uid后,创建了一个ActivityRecord,用于描述将要启动的Activity。
  • TaskRecord:用来描述一个Activity任务栈,在这里其实我们也就是创建了一个新的Activity任务栈。需要注意的是,Activity任务栈是一个假象的模型,并不真实存在。
    在创建新的TaskRecord后,去获取要启动的Activity所在的栈的栈顶不是处于停止状态的ActivityRecord,对于要启动的Activity由于是个根Activity,所以ActivityRecord为空。
    接下来就是启动栈顶Activity。首先是获取这个即将启动的Activity所在的应用程序进程,之后就是在目标应用程序进程中启动Activity。
    上面的这些逻辑都运行在AMS所在的进程中,但是我们在获取即将启动的Activity所在的应用程序进程后,会得到这个应用程序进程的ApplicationThread。我们知道ApplicationThread是一个Binder,因此,逻辑自然由AMS所在进程经过Binder通信来到了ActivityThread,也就是进入到了应用程序进程中。
  1. ActivityThread启动根Activity。在得到应用程序进程的ApplicationThread后,ApplicationThread会将启动Activity的参数封装成ActivityClientRecord,并向H类发送类型为LAUNCH_ACTIVITY的消息,且在发消息时将ActivityClientRecord传递过去。
    这里H类继承自Handler,是ActivityThread的内部类,用于处理主线程的消息循环。
    因为ApplicationThread是一个Binder,也就是说它的调用逻辑是运行在Binder线程池中的,所以我们需要用H类将代码的逻辑切换到主线程中。
    接下来我们将需要启动的根Activity所属的APK加载进来,然后启动Activity。但是这并没有结束,还需要将Activity的状态设置为Resume。
    我们来看一下启动Activity的详细流程,也就是逻辑切换到主线程后,将Activity的状态设置为Resume前的这一部分。在这里插入图片描述
  • ActivityInfo类:用于存储代码以及AndroidManifes设置的Activity和Receiver节点信息,比如Activity的theme和launchMode。
  • ComponentName类:保存了该Activity的包名和类名。
  • attach方法:在attach方法中会创建Window对象(PhoneWindow)并与Activity自身进行关联。

二、Service的启动与绑定过程

首先来看下Service的启动过程,和根Activity的启动流程大致相似。在这里插入图片描述

  1. ContextImpl请求AMS。我们会调用startService方法来启动Service,这个方法在ContextWrapper中实现,在实现的时候实际上调用的是ContextImpl的startService方法,在ContextImpl的startService方法中,最终调用的是AMS的startService方法,由此逻辑走到AMS中。在AMS的startService方法中,又会调用ActiveServices中的方法,使逻辑进入到ActiveServices中。
  2. ActiveServices请求创建Service。这里首先我们会获得Service对应的ServiceRecord,这个ServiceRecord和之前说过的ActivityRecord类似,是用来描述一个Service的。获得ServiceRecord后,接下来就会去获得Service要运行的进程。我们通过ServiceRecord的processName值来得到运行的进程,默认是当前进程,也可以在AndroidManifest文件中设置android:process来新开启一个进程来运行Service。
    和启动根Activity时候一样,接下来在进入启动Service流程前,我们需要判断这个运行Service的应用程序进程是否存在,如果不存在则需要去创建这个应用程序进程,我们在流程图里省略了,只讨论了没有设置android:process属性,即应用程序进程存在情况。
    我们在获取即将启动的Service所在的应用程序进程后,会得到这个应用程序进程的ApplicationThread。我们知道ApplicationThread是一个Binder,因此,逻辑自然经过Binder通信来到了ActivityThread,也就是进入到了应用程序进程中。
  3. ActivityThread启动Service。在得到应用程序进程的ApplicationThread后,ApplicationThread会将启动Service的参数封装成CreateServiceData,并向H类发送类型为CREATE_SERVICE的消息,且在发消息时将CreateServiceData传递过去,由此启动了Service。这个部分也和ActivityThread启动Activity的过程是类似的。
    最后我们来看一下H类接收消息的过程。
    在这里插入图片描述
    整体流程也是和启动Activity的详细流程差不多的,这里大家可以自行体会,触类旁通。

说完了Service的启动过程,再来看看Service的绑定过程。
在这里插入图片描述

Service的绑定过程要比启动过程复杂一些。

  1. ContextImpl请求AMS。我们会调用bindService()方法来绑定Service,这个方法在ContextWrapper中实现,在实现的时候实际上调用的是ContextImpl的bindService方法,在ContextImpl的bindService方法中,最终调用的是AMS的bindService方法,由此逻辑走到AMS中。在AMS的bindService方法中,又会调用ActiveServices中的方法,使逻辑进入到ActiveServices中。
  2. ActiveServices中的逻辑。 这里首先我们会通过ServiceRecord获得AppBindRecord 。在应用程序进程通过Intent绑定Service时, 会通过AppBindRecord来维护Service和应用程序进程之间的关联。也就是说,在这个AppBindRecord内部存储了谁绑定的Service、被绑定的Service、绑定Service的Intent和所有绑定通信记录的信息。接下来由ActivityThread来调用Service的onCreate方法来启动Service。在Service运行且当前应用程序进程已经接收到绑定Service时返回的Binder后,会调用H的post方法向主线程发送消息,这样应用程序进程可以通过Binder来获取到绑定的Service的访问接口。如果当前应用程序进程是第一个与Service进行绑定的,并且Service已经调用过onUnBind方法,则需要重新绑定;如果应用程序进程的Client端没有发送过绑定Service的请求,则不会进行绑定。这段逻辑说起来有点绕,用下图表示能清晰些:在这里插入图片描述
    最后在进入绑定流程后,首先会将Service的信息封装成BindServiceData,之后和绑定Service类似,会向H类发送类型为BIND_SERVICE的消息,且在发消息时将BindServiceData传递过去,之后会根据Service的绑定状态来确认接下来的操作逻辑。这里逻辑和上图差不多,也是分为绑定过Service的情况和未绑定的情况,我们只说未绑定的情况。由Service的绑定过程泳道流程图可知,这里会调用AMS的publishService方法。
  3. AMS中的publishService方法逻辑。AMS会为每个绑定Service的Intent分配一个IntentBindRecord类型对象(IntentBindRecord:用于描述绑定Service的Intent),不同的应用程序进程可能使用同一个Intent来绑定Service。所以这里我们先决条件是解决当前应用程序进程和Service跨进程通信问题。这里还是通过调用H的post方法使LoadApk的内部类RunConnection对象运行在主线程中,之后回调ServiceConnection接口类的onServiceConnected方法, Service绑定完毕。

三、广播的动态注册、发送和及接收过程

首先来看下广播的动态注册过程。在这里插入图片描述

我们会调用registerReceiver方法来注册动态广播,这个方法在ContextWrapper中实现,在实现的时候实际上调用的是ContextImpl的registerReceiver方法,在ContextImpl的registerReceiver方法中,最终调用的是AMS的registerReceiver方法,由此逻辑走到AMS中。注意在调用AMS的registerReceiver方法前,需要获取到IIntentReceiver对象(IIntentReceiver是一个Binder接口,用于广播的跨进程的通信)并传给AMS的registerReceiver方法,因为注册广播是一个跨进程过程,所以需要具有跨进程的通信功能的IIntentReceiver。
我们再来看一下AMS的registerReceiver方法内逻辑。
在这里插入图片描述
左面支线逻辑:

  • ProcessRecord:用于描述请求AMS注册广播接收者的Activity所在的应用程序进程。
  • 获取到actions列表后,根据actions列表和userIds(即应用程序的uid)得到所有的粘性广播intent(发送后就一直存在于系统的消息容器里面,等待对应的处理器去处理),之后再从上面得到的粘性广播中找到匹配传入参数filter的粘性广播intent。可以看到,整个的这一部分的操作都是在查找符合条件的粘性广播intent,这些粘性广播是存储在AMS中的

右面支线逻辑:

  • ReceiverList: 继承自ArrayList,用来存储广播接收者。
  • BroadcastFilter:用来描述注册的广播接收者。

最后将BroadcastFilter添加到IntentResolver对象中,这样当AMS接收到广播时就可以在这里找到对应的广播接收者了,从而达到了注册广播的目的。

说完了广播的动态注册过程,我们再来看看广播的发送和接收过程。还是先看一下整体的流程,这里以普通广播为例,如下图所示:
在这里插入图片描述

  1. ContextImpl请求AMS。我们会调用sendBroadcast方法来发送普通广播,这个方法在ContextWrapper中实现,在实现的时候实际上调用的是ContextImpl的sendBroadcast方法,在ContextImpl的sendBroadcast方法中,最终调用的是AMS的broadcastIntent方法,由此逻辑走到AMS中。
  2. 在AMS的broadcastIntent方法中,逻辑如下图所示:在这里插入图片描述
    这里我们先省略进入接收广播流程步骤,说一下在这之前的步骤。
    首先会验证广播是否合法,在验证合法的过程中,需要验证传过去的intent是否不为空且有文件描述符,符合的话抛出异常;之后会获取intent中的flag;如果系统正在启动过程中,判断如果flag设置为FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_ROOT(启动检查时只接受动态注册的广播接收者),则不做处理;如果不是的话也要判断flag是否设置为FLAG_RECEIVER_REGISTERED_ONLY(只接受动态注册的广播接收者),是的话依旧抛出异常。经过上述步骤,广播的合法性就验证完了。用流程图表示的话如下图所示。在这里插入图片描述
    验证完广播的合法性后,我们将不同广播的接收者列表做了合并,这样这个合并后的列表就包含了所有广告接收者(普通广播和有序广播)。
  3. AMS到BroadcastHandler的调用过程。在AMS的broadcastIntent方法中我们通过逻辑图看到最后一步是进入接收广播流程,现在我们就来具体说一下这个流程。首先向BroadcastHandler类型对象发送BROADCAST_INTENT_MSG类型的消息,BroadcastHandler在收到消息后会对普通广播和有序广播分别进行处理,旨在将广播发送给广播接收者。在流程图上可以看到调用的是processNextBroadcast()方法,我们来看这个方法中普通广播的处理部分:在这个方法中,会遍历普通广播列表并且将列表中的普通广播BroadcastRecord类型对象发送给对应的广播接收者,在发送的时候依旧会检查广播发送者和广播接收者的权限。如果通过了权限检查,逻辑就会来到应用程序进程中。
  4. ActivityThread收到广播并得到intent。如果广播接收者所在的应用程序进程存在并且正在运行,则用广播接收者所在的应用程序进程来接收广播,依旧是通过我们熟悉的ApplicationThread。调用ApplicationThread的scheduleRegisteredReceiver方法,在scheduleRegisteredReceiver方法中又调用了IIntentReceiver类型的对象的performReceive方法,我们知道,IIntentReceiver是用于广播的跨进程通信的。最后在这个IIntentReceiver类型的对象的performReceive方法中,又将广播的intent等信息封装后调用ActivityThread的post方法传入,并通过H发送到线程的消息队列中,这样注册的广播接收者就收到了广播并得到了intent。

四、Content Provider的启动过程

Content Provider主要用于进程内和进程间的数据共享。首先还是看一下整体流程,如下图所示:在这里插入图片描述

  1. ContextImpl请求ActivityThread。我们在使用ContentProvider时会调用getContentResolver方法,这个方法在ContextWrapper中实现,在实现的时候实际上调用的是ContextImpl的getContentResolver方法,在ContextImpl的getContentResolver方法中,返回了ApplicationContentResolver类型的对象,它是ContextImpl中的静态内部类,继承自ContentResolver,在ContextImpl的构造方法中被创建,因此,当我们调用ContentResolver的insert、query、update等方法时就会启动Content Provider,最终调用的是ActivityThread的acquireProvider方法,由此逻辑走到ActivityThread中。
  2. 在获取目标ContentProvider的应用程序进程信息后,会将这些信息用ProcessRecord类型对象来表示,并且会安装ContentProvider并将ContentProvider相关数据做缓存,以避免即便使用相同的Content Provider,也每次都要去重新获取。
  3. AMS中,最终调用attachApplication方法,向H发送BIND_APPLICATION类型的消息,这样逻辑就又回到了ActivityThread中。最终启动Content Provider,并启动Content Provider所在应用程序(如果此前并没有启动过的话)。
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页