This CL migrates most of the remaining classes to use BroadcastDispatcher. Some classes left are Views or created before the BroadcastDispatcher can be injected. Adds docs for instructions on using the BroadcastDispatcher. Using the broadcast dispatcher, the time system_server spends dispatching common intents to SystemUI like SCREEN_OFF and SCREEN_ON can be seen to decrease from ~70-150ms (in a Q build) to ~2-4ms. Additionally, once a broadcast is received by the dispatcher, time until it is fully dispatched inside SystemUI is not impacted greatly. Most broadcasts are fully dispatched after ~20ms with a few of them taking ~100ms. Test: atest SystemUITests no regressions Test: build and boot Test: tried some random broadcasts and they are properly dispatched Test: BroadcastDispatch dump Test: adb shell dumpsys activity broadcasts Bug: 134566046 Change-Id: I26a592be66b053f25669b5481b58bf7f07bfd0da
5.0 KiB
Using SystemUI's BroadcastDispatcher
What is this dispatcher?
This is an internal dispatcher class for global broadcasts that SystemUI components want to receive. The dispatcher consolidates most BroadcastReceiver
that exist in SystemUI by merging the IntentFilter
and subscribing a single BroadcastReceiver
per user with the system.
Why use the dispatcher?
Having a single BroadcastReceiver
in SystemUI improves the multi dispatch situation that occurs whenever many classes are filtering for the same intent action. In particular:
- All supported
BroadcastReceiver
will be aggregated into one single receiver per user. - Whenever there is a broadcast, the number of IPC calls from
system_server
into SystemUI will be reduced to one per user (plus one forUSER_ALL
). This is meaninful for actions that are filtered byBroadcastReceiver
in multiple classes. There could be more than one per user in the case of unsupported filters. - The dispatcher immediately moves out of the main thread upon broadcast, giving back control to
system_server
. This improves the total dispatch time for broadcasts and prevents from timing out. - The dispatcher significantly reduces time spent in main thread by handling most operations in a background thread and only using the main thread for subscribing/unsubscribind and dispatching where appropriate.
Should I use the dispatcher?
The dispatcher supports BroadcastReceiver
dynamic subscriptions in the following cases:
- The
IntentFilter
contains at least one action. - The
IntentFilter
may or may not contain categories. - The
IntentFilter
does not contain data types, data schemes, data authorities or data paths. - The broadcast is not gated behind a permission.
Additionally, the dispatcher supports the following:
- Subscriptions can be done in any thread.
- Broadcasts will be dispatched on the main thread (same as
system_server
) by default but aHandler
can be specified for dispatching - A
UserHandle
can be provided to filter the broadcasts by user.
If introducing a new BroadcastReceiver
(not declared in AndroidManifest
) that satisfies the constraints above, use the dispatcher to reduce the load on system_server
.
Do not use the dispatcher to obtain the last broadcast (by passing a null BroadcastReceiver
). BroadcastDispatcher#registerReceiver
does not return the last sticky Intent.
Additionally, if listening to some broadcast is latency critical (beyond 100ms of latency), consider registering with Context instead.
How do I use the dispatcher?
Acquire the dispatcher by using @Inject
to obtain a BroadcastDispatcher
. Then, use the following methods in that instance.
Subscribe
/**
* Register a receiver for broadcast with the dispatcher
*
* @param receiver A receiver to dispatch the [Intent]
* @param filter A filter to determine what broadcasts should be dispatched to this receiver.
* It will only take into account actions and categories for filtering. It must
* have at least one action.
* @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the
* main handler. Pass `null` to use the default.
* @param user A user handle to determine which broadcast should be dispatched to this receiver.
* By default, it is the current user.
* @throws IllegalArgumentException if the filter has other constraints that are not actions or
* categories or the filter has no actions.
*/
@JvmOverloads
fun registerReceiver(BroadcastReceiver, IntentFilter, Handler? = mainHandler, UserHandle = context.user)
All subscriptions are done with the same overloaded method. As specified in the doc, in order to pass a UserHandle
with the default Handler
, pass null
for the Handler
.
In the same way as with Context
, subscribing the same BroadcastReceiver
for the same user using different filters will result on two subscriptions, not in replacing the filter.
Unsubscribe
There are two methods to unsubscribe a given BroadcastReceiver
. One that will remove it for all users and another where the user can be specified. This allows using separate subscriptions of the same receiver for different users and manipulating them separately.
/**
* Unregister receiver for all users.
* <br>
* This will remove every registration of [receiver], not those done just with [UserHandle.ALL].
*
* @param receiver The receiver to unregister. It will be unregistered for all users.
*/
fun unregisterReceiver(BroadcastReceiver)
/**
* Unregister receiver for a particular user.
*
* @param receiver The receiver to unregister. It will be unregistered for all users.
* @param user The user associated to the registered [receiver]. It can be [UserHandle.ALL].
*/
fun unregisterReceiverForUser(BroadcastReceiver, UserHandle)
Unregistering can be done even if the BroadcastReceiver
has never been registered with BroadcastDispatcher
. In that case, it is a No-Op.