广播的注册,发送和接收过程:
Android的广播主要可以分为两类:标准广播和有序广播。也可以分为自定义广播和系统广播。
1.广播的注册过程:
广播的注册通俗来讲就是广播接收者注册自己感兴趣的广播。广播的注册分为静态注册和动态注册。静态注册在应用安装时由PackageManagerService来完成注册,也就是在AndroidManifest.xml中注册。动态注册是在代码中注册,它可以自由的控制注册和注销,但必须在程序启动之后才能接收广播。理论上讲动态注册能够监听到的系统广播,静态注册也能够监听到,但是由于静态注册的恶意使用,导致在Android8.0之后,所有的隐式广播都不允许使用静态注册的方式来接收。
这里只介绍动态注册,想要动态注册广播,就需要调用registerReceiver方法,它在ContextWrapper中实现,路径:frameworks/base/core/java/android/content/ContextWrapper.java该方法返回mBase的registerReceiver方法,其中mBase指的是ContextImpl类型,而mBase的registerReceiver方法有很多重载方法,最终它调用的是registerReceiverInternal方法。
我们来看registerReceiverInternal方法:先判断LoadedApk类型的mPackageInfo不等于null,并且context不等于null就调用mPackageInfo的getReceiverDispatcher方法来获取rd对象;否则就调用new LoadedApk.ReceiverDispatcher().getIIntentReceiver方法来创建rd对象。这里的rd对象是IIntentReceiver类型的,它是一个Binder接口,用于广播的跨进程通信。接着调用了ActivityManage.getServicer的registerReceiver方法,最终就会调用AMS的registerReceiver方法,并且将rd对象传进去。过程类似:1.Launcher请求AMS过程这一节。这里不直接传入BroadcastReceiver而是传入IIntentReceiver,是因为注册广播是一个跨进程过程,需要有跨进程通信功能的IIntentReceiver。
IIntentReceiver类在LoadedApk.ReceiverDispatcher.InnerReceiver中实现,路径为:frameworks/base/core/java/android/app/LoadedApk.java。它使用了AIDL来实现进程之间的通信,IntentReceiver继承自IIntentReceiver.Stub,它是Binder通信的服务端,而IIntentReceiver则是Binder的客户端,IntentReceiver在本地的代理。
再继续查看AMS的registerReceiver方法:首先调用getRecordForAppLocked方法来得到ProcessRecord类型的caallerApp对象,它是用于描述请求AMS注册广播接收者的Activity所在的应用程序进程。接着调用InntentFilter的filter对象的actionsIterator方法得到actions列表,再根据actions列表和userIds(可以理解为应用程序的uid)得到所有的粘性广播的intent。再把这些intent通过addAll方法传入stickyIntents中去。接下来遍历stickyIntents寻找匹配的粘性广播,并通过add方法传入allSticky中。从这里可以看出这些粘性广播是存储在AMS中的。再然后通过mRegisteredReceivers.get方法,获取ReceiverList列表,如果为空则创建。其中ReceiverList是用来存储广播接收者的。再接着创建BroadcastFilter并且传入之前创建的ReceiverList列表,其中BroadcastFilte用来描述注册的广播接收者。再然后把创建的BroadcastFilter通过add方法添加到ReceiverList列表中。最后再将BroadcastFilter通过addFilter方法添加到IntentResolver类型的mReceiverResolver对象中。这样当AMS接收到广播时就可以从mReceiverResolver中找到对应的接收者了,从而达到了注册广播的目的。
2.广播的发送和接收:
广播的发送和接收可以分为两部分:ContextImpl到AMS的调用过程和AMS到BroadcastReceiver的调用过程。
2.1:ContextImpl到AMS的调用过程:
发送标准广播调用的是sendBroadcast方法,它同样在ContextWrapper中实现。ContextWrapper中的sendBroadcast方法返回的是mBase的sendBroadcast方法,其中mBase指的是ContextImpl类型,最终调用的是AMS的broadcastIntent方法。
AMS的broadcastIntent方法主要调用了verifyBroadcastLocked方法判断广播是否合法;然后调用broadcastIntentLocked方法。
verifyBroadcastLocked方法主要看先判断intent是否不为空并且有文件描述符,就通过intent的getFlags获取flag,然后再判断如果系统正在启动中,就再判断如果flag设置为FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT(启动检查时只接受动态注册的广播接受者)则不做处理;否则如果flag没有设置为FLAG_RECEIVER_REGISTERED_ONLY(只接受动态注册的广播接受者)就会抛出异常。
再来看broadcastIntentLocked方法,该方法首先会将动态注册的广播接收者和静态注册的广播接收者按照优先级不同存储在不同的列表中去,再将这两个列表合并到receivers列表中,这样receivers列表就包含了所有的广播接收者。然后创建BroadcastReceiver对象,并且把receivers传进去。再然后调用BroadcastQueue类型的queue对象的scheduleBroadcastsLocked方法。
2.2:AMS到BroadcastReceiver的调用过程:
BroadcastQueue的scheduleBroadcastsLocked方法主要做了:通过sendMessage方法向BroadcastHandler类型的mHandler对象发送了BROADCAST_INTENT_MSG类型的消息。这个消息在BroadcastHandler类型的handleMessage方法中处理。在该方法中调用了processNextBroadcast方法,分别对标准广播和有序广播进行处理,旨在将广播发送给广播接收者。
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
我们来看processNextBroadcast方法处理标准广播的部分:首先根据传入的参数,把mBroadcastScheduled设置为false,表示对之前发送来的BROADCAST_INTENT_MSG类型的消息已经处理了。然后使用mParallelBroadcasts列表来存储标准广播,再通过while循环将mParallelBroadcasts列表中的标准广播发送给对应的广播接收者。而while循环中的mParallelBroadcasts.remove方法用于获取一个mParallelBroadcasts列表中存储的BroadcastRecord类型的r对象。r.receivers.get方法用于获取广播接收者。deliverToRegisteredReceiverLocked方法用于将r对象描述的广播发送给对应的广播接收者。
继续来看deliverToRegisteredReceiverLocked方法,首先会进行大量的检查广播发送者和广播接收者权限的工作。如果通过了权限就调用performReceiveLocked方法。该方法首先判断如果广播接收者所在的应用程序进程存在并且正在运行,就调用app.thread.scheduleRegisteredReceiver方法。表示广播接收者所在的应用程序进程来接收广播。
app.thread.scheduleRegisteredReceiver方法调用了IIntentReceiver类型的receiver对象的performReceiver方法。其中IIntentReceiver类是用于广播跨进程的通信。该方法调用了ReceiverDispatcher类型的rd对象的performReceiver方法。
ReceiverDispatcher的performReceiver方法首先创建一个Args对象,并把广播的intent等消息封装进去。然后调用mActivityThread的post方法,并把Args对象的getRunnable方法传进去。其中mActivityThread是一个Handler对象,具体是H类。这么做是把Args对象的getRunnable方法通过H发送到线程的消息队列中。发送之后,getRunnable方法会在内部调用BroadcastReceiver类型的receiver的onReceiver方法,这样注册的广播接收者就收到了广播并且得到了intent。
ContentProvider的启动过程:
ContentProvider,即内容提供者,在通常情况下,并没有使用的很频繁,主要用于进程之间和进程内的数据共享。ContentProvider的启动部分分为两部分:query方法到AMS的调用过程和AMS启动ContentProvider的过程。
1.query方法到AMS的调用过程:
使用ContentProvider,就要借助ContentResolver类,可以通过Context的getContentResolver方法来获取该类的实例,Context的getContentResolver方法返回mBase的getContentResolver方法,其中mBase指的是ContextImpl类。
frameworks/base/core/java/android/app/ContextImpl.java
frameworks/base/core/java/android/content/ContentResolver.java
ContextImpl的getContentResolver方法返回了ApplicationContentResolver类型的mContentResolver对象。其中ApplicationContentResolver是ContextImpl类的静态内部类,继承自ContentResolver类,它会在ContextImpl的构造方法中创建。这说明我们在调用ContentResolver的增删改查时就会启动ContentProvider。
举例:query方法在ApplicationContentResolver的父类ContentResolver中实现,它有三个重载方法,最后调用的query方法主要做了:通过acquireUnstableProvider方法返回IContentProvider类型的unstableProvider对象。然后再调用unstableProvider的query方法。其中IContentProvider类是ContentProvider在本地的代理,具体实现为ContentProvider。
我们来看acquireUnstableProvider方法:首先检查uri.getScheme方法返回的scheme是否等于SCHEME_CONTENT,不等于返回null。然后调用了acquireUnstableProvider方法,这是一个抽象方法,在ApplicationContentResolver类中实现。在ApplicationContentResolver类中的acquireUnstableProvider方法返回了ActivityThread类型的mMainThread对象的acquireProvider方法。
acquireProvider方法:首先会调用acquireExistingProvider方法内部检查ActivityThread中的全局变量mProviderMap中是否有目标ContentProvider的存在,如果有则返回,如果没有就会后续调用AMS的getContentProvider方法返回ContentProvider相关数据。然后通过installProvider方法来安装ContentProvider,并且把AMS的getContentProvider方法返回的数据存储在mProviderMap中,起到缓存的作用。
继续来看AMS的getContentProvider方法:它返回了getContentProviderImpl方法。该方法内容较多,关注的有:通过getProcessRecordLocked方法来获取目标ContentProvider的应用程序进程的信息,用ProcessRecord类型的proc对象表示。如果该进程没有启动就调用startProcessLocked方法启动进程,如果已经启动,就会调用proc.thread.scheduleInstallProvider方法。而进程没有启动最后会调用ActivityThread的main方法(过程查看:Android应用程序进程启动过程)。
启动:proc.thread.scheduleInstallProvider方法,实际上调用的是ActivityThread的scheduleInstallProvider方法,该方法向H类发送了INSTALL_PROVIDER类型的消息。H类的handleMessage方法根据消息类型调用handleInstallProvider方法,该方法调用了installContentProvider方法,接下来查看//①。
没有启动:在到了ActivityThread的main方法之后,就会先调用Looper.prepareMainLooper方法来获取Looper,并且后续通过Looper.loop方法开启消息循环。然后创建ActivityThread,并且调用它的attach方法。该方法首先调用ActivityManager.getService方法获取IActivityManager,然后调用IActivityManager的attachApplication方法,最终调用的是AMS的attachApplication方法。
2.AMS启动ContentProvider的过程:
继续来看AMS的attachApplication方法,它在方法中调用了attachApplicationLocked方法。该方法中调用了IApplicationThread类型的thread对象的bindApplication方法,其中IApplicationThread类和IActivityManager一样采用了AIDL,而实现bindApplication方法的是ApplicationThread类,而不是Android7.0的ApplicationThreadProxy类。
接着看bindApplication方法:它最后会调用sendMessage方法向H类发送BIND_APPLICATION类型的消息。而H类的handleMessage方法根据消息类型调用handleBindApplication方法,它主要先通过ContextImpl的createAppContext方法创建ContextImpl,然后通过ClassLoader对象的loadClass().newInstance方法(反射)创建Instrumentation,并且后续通过init初始化Instrumentation。然后创建Application,并且后续通过Application的callApplicationOnCreate方法调用Application的onCreate方法。再接着通过installContentProvider方法来启动ContentProvider。这就意味着:ContentProvider所在的应用程序已经启动。
//①:继续installContentProvider方法:首先遍历当前应用程序进程的ProviderInfo列表,得到每个ContentProvider的ProviderInfo(这是存储在ContentProvider里的信息)。再通过installProvider方法来启动这些ContentProvider。再然后通过AMS的publishContentProvider方法将这些ContentProvider存储在AMS的mProviderMap中(前面写到过,起到缓存作用,也是为了防止使用相同的ContentProvider时都会调用AMS的getContentProvider方法)。
最后我们来看installProvider方法是怎么启动ContentProvider的:它首先通过ClassLoader对象的loadClass().newInstance方法(反射)创建ContentProvider类型的localProvider对象。并且后续调用了它的attachInfo方法,这个方法中调用了ContentProvider的onCreate方法,它是一个抽象方法,这样ContentProvider就已经启动完毕了。