1# Executors
2
3go/sysui-executors
4
5[TOC]
6
7## TLDR
8
9In SystemUI, we are encouraging the use of Java's [Executor][Executor] over
10Android's [Handler][Handler] when shuffling a [Runnable][Runnable] between
11threads or delaying the execution of a Runnable. We have an implementation of
12Executor available, as well as our own sub-interface,
13[DelayableExecutor][DelayableExecutor] available. For test,
14[FakeExecutor][FakeExecutor] is available.
15
16[Executor]: https://developer.android.com/reference/java/util/concurrent/Executor.html
17[Handler]: https://developer.android.com/reference/android/os/Handler.html
18[Runnable]: https://developer.android.com/reference/java/lang/Runnable.html
19[DelayableExecutor]: /packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java
20[FakeExecutor]: /packages/SystemUI/tests/utils/src/com/android/systemui/util/concurrency/FakeExecutor.java
21
22## Rationale
23
24Executors make testing easier and are generally more flexible than Handlers.
25They are defined as an interface, making it easy to swap in fake implementations
26for testing. This also makes it easier to supply alternate implementations
27generally speaking - shared thread pools; priority queues; etc.
28
29For testing, whereas a handler involves trying to directly control its
30underlying Looper (using things like `Thread.sleep()` as well as overriding
31internal behaviors), an Executor implementation can be made to be directly
32controllable and inspectable.
33
34See also go/executors-for-the-android-engineer
35
36## Available Executors
37
38At present, there are two interfaces of Executor avaiable, each implemented, and
39each with two instances - `@Background` and `@Main`.
40
41### Executor
42
43The simplest Executor available implements the interface directly, making
44available one method: `Executor.execute()`. You can access an implementation of
45this Executor through Dependency Injection:
46
47```java
48   public class Foobar {
49       @Inject
50       public Foobar(@Background Executor bgExecutor) {
51           bgExecutor.execute(new Runnable() {
52             // ...
53           });
54       }
55   }
56```
57
58`@Main` will give you an Executor that runs on the ui thread. `@Background` will
59give you one that runs on a _shared_ non-ui thread. If you ask for an
60non-annotated Executor, you will get the `@Background` Executor.
61
62We do not currently have support for creating an Executor on a new, virgin
63thread. We do not currently support any sort of shared pooling of threads. If
64you require either of these, please reach out.
65
66### DelayableExecutor
67
68[DelayableExecutor][DelayableExecutor] is the closest analogue we provide to
69Handler. It adds `executeDelayed(Runnable r, long delayMillis)` and
70`executeAtTime(Runnable r, long uptimeMillis)` to the interface, just like
71Handler's [postDelayed][postDelayed] and [postAtTime][postAttime]. It also adds
72the option to supply a [TimeUnit][TimeUnit] as a third argument.
73
74A DelayableExecutor can be accessed via Injection just like a standard Executor.
75In fact, at this time, it shares the same underlying thread as our basic
76Executor.
77
78```java
79 public class Foobar {
80     @Inject
81     public Foobar(@Background DelayableExecutor bgExecutor) {
82         bgExecutor.executeDelayed(new Runnable() {
83           // ...
84         }, 1, TimeUnit.MINUTES);
85     }
86 }
87```
88
89Unlike Handler, the added methods return a Runnable that, when run, cancels the
90originally supplied Runnable if it has not yet started execution:
91
92```java
93 public class Foobar {
94     @Inject
95     public Foobar(@Background DelayableExecutor bgExecutor) {
96         Runnable cancel = bgExecutor.executeDelayed(new Runnable() {
97           // ...
98         }, 1, TimeUnit.MINUTES);
99
100         cancel.run();  // The supplied Runnable will (probably) not run.
101     }
102 }
103```
104
105[postDelayed]: https://developer.android.com/reference/android/os/Handler#postDelayed(java.lang.Runnable,%20long)
106[postAttime]: https://developer.android.com/reference/android/os/Handler#postAtTime(java.lang.Runnable,%20long)
107[TimeUnit]: https://developer.android.com/reference/java/util/concurrent/TimeUnit
108
109## Moving From Handler
110
111Most use cases of Handlers can easily be handled by the above two interfaces
112above. A minor refactor makes the switch:
113
114Handler       | Executor  | DelayableExecutor
115------------- | --------- | -----------------
116post()        | execute() | execute()
117postDelayed() | `none`    | executeDelayed()
118postAtTime()  | `none`    | executeAtTime()
119
120There are some notable gaps in this implementation: `Handler.postAtFrontOfQueue()`.
121If you require this method, or similar, please reach out. The idea of a
122PriorityQueueExecutor has been floated, but will not be implemented until there
123is a clear need.
124
125Note also that "canceling" semantics are different. Instead of passing a `token`
126object to `Handler.postDelayed()`, you receive a Runnable that, when run,
127cancels the originally supplied Runnable.
128
129### Message Handling
130
131Executors have no concept of message handling. This is an oft used feature of
132Handlers. There are (as of 2019-12-05) 37 places where we subclass Handler to
133take advantage of this. However, by-and-large, these subclases take the
134following form:
135
136```Java
137mHandler = new Handler(looper) {
138    @Override
139    public void handleMessage(Message msg) {
140        switch (msg.what) {
141            case MSG_A:
142                handleMessageA();
143                break;
144            case MSG_B:
145                handleMessageB((String) msg.obj);
146                break;
147            case MSG_C:
148                handleMessageC((Foobar) msg.obj);
149                break;
150            // ...
151        }
152    }
153};
154
155// Elsewhere in the class
156void doSomething() {
157    mHandler.obtainMessage(MSG_B, "some string");
158    mHandler.sendMessage(msg);
159}
160```
161
162This could easily be replaced by equivalent, more direct Executor code:
163
164```Java
165void doSomething() {
166    mExecutor.execute(() -> handleMessageB("some string"));
167}
168```
169
170If you are posting Runnables frequently and you worry that the cost of creating
171anonymous Runnables is too high, consider creating pre-defined Runnables as
172fields in your class.
173
174If you feel that you have a use case that this does not cover, please reach out.
175
176### ContentObserver
177
178One notable place where Handlers have been a requirement in the past is with
179[ContentObserver], which takes a Handler as an argument. However, we have created
180[ExecutorContentObserver], which is a hidden API that accepts an [Executor] in its
181constructor instead of a [Handler], and is otherwise identical.
182
183[ContentObserver]: https://developer.android.com/reference/android/database/ContentObserver.html
184[ExecutorContentObserver]: /core/java/android/database/ExecutorContentObserver.java
185
186### Handlers Are Still Necessary
187
188Handlers aren't going away. There are other Android APIs that still require them.
189Avoid Handlers when possible, but use them where necessary.
190
191## Testing (FakeExecutor)
192
193We have a [FakeExecutor][FakeExecutor] available. It implements
194DelayableExecutor (which in turn is an Executor). It takes a FakeSystemClock in
195its constructor that allows you to control the flow of time, executing supplied
196Runnables in a deterministic manner.
197
198The implementation is well documented and tested. You are encouraged to read and
199reference it, but here is a quick overview:
200
201<table>
202    <tr>
203        <th>Method</th>
204        <th>Description</th>
205    </tr>
206    <tr>
207        <td>execute()</td>
208        <td>
209            Queues a Runnable so that it is "ready"
210            to run. (A Runnable is "ready" when its
211            scheduled time is less than or equal to
212            the clock.)
213        </td>
214    </tr>
215    <tr>
216        <td>postDelayed() & postAtTime()</td>
217        <td>
218            Queues a runnable to be run at some
219            point in the future.
220        </td>
221    </tr>
222    <tr>
223        <td>runNextReady()</td>
224        <td>
225            Run one runnable if it is ready to run
226            according to the supplied clock.
227        </td>
228    </tr>
229    <tr>
230        <td>runAllReady()</td>
231        <td>
232            Calls runNextReady() in a loop until
233            there are no more "ready" runnables.
234        </td>
235    </tr>
236    <tr>
237        <td>advanceClockToNext()</td>
238        <td>
239            Move the internal clock to the item at
240            the front of the queue, making it
241            "ready".
242        </td>
243    </tr>
244    <tr>
245        <td>advanceClockToLast()</td>
246        <td>
247            Makes all currently queued items ready.
248        </td>
249    </tr>
250    <tr>
251        <td>numPending()</td>
252        <td>
253            The number of runnables waiting to be run
254            They are not necessarily "ready".
255        </td>
256    </tr>
257    <tr>
258        <td>(static method) exhaustExecutors()</td>
259        <td>
260            Given a number of FakeExecutors, it
261            calls runAllReady() repeated on them
262            until none of them have ready work.
263            Useful if you have Executors that post
264            work to one another back and forth.
265        </td>
266    </tr>
267</table>
268
269_If you advance the supplied FakeSystemClock directly, the FakeExecutor will
270execute pending Runnables accordingly._ If you use the FakeExecutors
271`advanceClockToNext()` and `advanceClockToLast()`, this behavior will not be
272seen. You will need to tell the Executor to run its ready items. A quick example
273shows the difference:
274
275Here we advance the clock directly:
276
277```java
278FakeSystemClock clock = new FakeSystemClock();
279FakeExecutor executor = new FakeExecutor(clock);
280executor.execute(() -> {});             // Nothing run yet. Runs at time-0
281executor.executeDelayed(() -> {}, 100); // Nothing run yet. Runs at time-100.
282executor.executeDelayed(() -> {}, 500); // Nothing run yet. Runs at time-500.
283
284clock.synchronizeListeners(); // The clock just told the Executor it's time-0.
285                              // One thing run.
286clock.setUptimeMillis(500);   // The clock just told the Executor it's time-500.
287                              // Two more items run.
288```
289
290Here we have more fine-grained control:
291
292```java
293FakeSystemClock clock = new FakeSystemClock();
294FakeExecutor executor = new FakeExecutor(clock);
295executor.execute(() -> {});             // Nothing run yet. Runs at time-0
296executor.executeDelayed(() -> {}, 100); // Nothing run yet. Runs at time-100.
297executor.executeDelayed(() -> {}, 500); // Nothing run yet. Runs at time-500.
298
299executor.runNextReady();        // One thing run.
300executor.advanceClockToNext();  // One more thing ready to run.
301executor.runNextReady();        // One thing run.
302executor.runNextReady();        // Extra calls do nothing. (Returns false).
303executor.advanceClockToNext();  // One more thing ready to run.
304executor.runNextReady();        // Last item run.
305```
306
307One gotcha of direct-clock-advancement: If you have interleaved Runnables split
308between two executors like the following:
309
310```java
311FakeSystemClock clock = new FakeSystemClock();
312FakeExecutor executorA = new FakeExecutor(clock);
313FakeExecutor executorB = new FakeExecutor(clock);
314executorA.executeDelayed(() -> {}, 100);
315executorB.executeDelayed(() -> {}, 200);
316executorA.executeDelayed(() -> {}, 300);
317executorB.executeDelayed(() -> {}, 400);
318clock.setUptimeMillis(500);
319```
320
321The Runnables _will not_ interleave. All of one Executor's callbacks will run,
322then all of the other's.
323
324### Testing Handlers without Loopers
325
326If a [Handler] is required because it is used by Android APIs, but is only
327used in simple ways (i.e. just `Handler.post(Runnable)`), you may still
328want the benefits of [FakeExecutor] when writing your tests, which
329you can get by wrapping the [Executor] in a mock for testing. This can be
330done with `com.android.systemui.util.concurrency.mockExecutorHandler` in
331`MockExecutorHandler.kt`.
332
333### TestableLooper.RunWithLooper
334
335As long as you're using FakeExecutors in all the code under test (and no
336Handlers or Loopers) you don't need it. Get rid of it. No more TestableLooper;
337no more Looper at all, for that matter.
338