1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.dialer.common.concurrent;
18 
19 import android.support.annotation.MainThread;
20 import android.support.annotation.NonNull;
21 import android.support.annotation.Nullable;
22 import android.support.annotation.WorkerThread;
23 import java.util.concurrent.ExecutorService;
24 
25 /**
26  * Provides a consistent interface for doing background work in either UI or non-UI contexts.
27  *
28  * <p>You may create an executor from a UI component (activity or fragment) or a non-UI component.
29  * Using this class provides a number of benefits:
30  *
31  * <ul>
32  *   <li>Ensures that UI tasks keep running across configuration changes by using a headless
33  *       fragment.
34  *   <li>Forces exceptions to crash the application, unless the user implements their own onFailure
35  *       method.
36  *   <li>Checks for dead UI components which can be encountered if a UI task runs longer than its
37  *       UI. If a dead UI component is encountered, onSuccess/onFailure are not called (because they
38  *       can't be) but a message is logged.
39  *   <li>Helps prevent memory leaks in UI tasks by ensuring that callbacks are nulled out when the
40  *       headless fragment is detached.
41  *   <li>UI and non-UI threads are shared across the application and run at reasonable priorities
42  * </ul>
43  *
44  * <p>Executors accept a single input and output parameter which should be immutable data objects.
45  * If you don't require an input or output, use Void and null as needed.
46  *
47  * <p>You may optionally specify onSuccess and onFailure listeners; the default behavior on success
48  * is a no-op and the default behavior on failure is to crash the application.
49  *
50  * <p>To use an executor from a UI component, you must create it in your onCreate method and then
51  * use it from anywhere:
52  *
53  * <pre><code>
54  *
55  * public class MyActivity extends Activity {
56  *
57  *   private final DialerExecutor&lt;MyInputType&gt; myExecutor;
58  *
59  *   public void onCreate(Bundle state) {
60  *     super.onCreate(bundle);
61  *
62  *     // Must be called in onCreate; don't use non-static or anonymous inner classes for worker!
63  *     myExecutor = DialerExecutorComponent.get(context).dialerExecutorFactory()
64  *         .createUiTaskBuilder(fragmentManager, taskId, worker)
65  *         .onSuccess(this::onSuccess)  // Lambdas, anonymous, or non-static inner classes all fine
66  *         .onFailure(this::onFailure)  // Lambdas, anonymous, or non-static inner classes all fine
67  *         .build();
68  *     );
69  *   }
70  *
71  *   private static class MyWorker implements Worker&lt;MyInputType, MyOutputType&gt; {
72  *     MyOutputType doInBackground(MyInputType input) { ... }
73  *   }
74  *   private void onSuccess(MyOutputType output) { ... }
75  *   private void onFailure(Throwable throwable) { ... }
76  *
77  *   private void userDidSomething() { myExecutor.executeParallel(input); }
78  * }
79  * </code></pre>
80  *
81  * <p>Usage for non-UI tasks is the same, except that tasks can be created from anywhere instead of
82  * in onCreate. Non-UI tasks use low-priority threads separate from the UI task threads so as not to
83  * compete with more critical UI tasks.
84  *
85  * <pre><code>
86  *
87  * public class MyManager {
88  *
89  *   private final DialerExecutor&lt;MyInputType&gt; myExecutor;
90  *
91  *   public void init() {
92  *     // Don't use non-static or anonymous inner classes for worker!
93  *     myExecutor = DialerExecutorComponent.get(context).dialerExecutorFactory()
94  *         .createNonUiTaskBuilder(worker)
95  *         .onSuccess(this::onSuccess)  // Lambdas, anonymous, or non-static inner classes all fine
96  *         .onFailure(this::onFailure)  // Lambdas, anonymous, or non-static inner classes all fine
97  *         .build();
98  *     );
99  *   }
100  *
101  *   private static class MyWorker implements Worker&lt;MyInputType, MyOutputType&gt; {
102  *     MyOutputType doInBackground(MyInputType input) { ... }
103  *   }
104  *   private void onSuccess(MyOutputType output) { ... }
105  *   private void onFailure(Throwable throwable) { ... }
106  *
107  *   private void userDidSomething() { myExecutor.executeParallel(input); }
108  * }
109  * </code></pre>
110  *
111  * Note that non-UI tasks are intended to be relatively quick; for example reading/writing shared
112  * preferences or doing simple database work. If you submit long running non-UI tasks you may
113  * saturate the shared application threads and block other tasks. Also, this class does not create
114  * any wakelocks, so a long running task could be killed if the device goes to sleep while your task
115  * is still running. If you have to do long running or periodic work, consider using a job
116  * scheduler.
117  */
118 public interface DialerExecutor<InputT> {
119 
120   /** Functional interface for doing work in the background. */
121   interface Worker<InputT, OutputT> {
122     @WorkerThread
123     @Nullable
doInBackground(@ullable InputT input)124     OutputT doInBackground(@Nullable InputT input) throws Throwable;
125   }
126 
127   /** Functional interface for handling the result of background work. */
128   interface SuccessListener<OutputT> {
129     @MainThread
onSuccess(@ullable OutputT output)130     void onSuccess(@Nullable OutputT output);
131   }
132 
133   /** Functional interface for handling an error produced while performing background work. */
134   interface FailureListener {
135     @MainThread
onFailure(@onNull Throwable throwable)136     void onFailure(@NonNull Throwable throwable);
137   }
138 
139   /** Builder for {@link DialerExecutor}. */
140   interface Builder<InputT, OutputT> {
141 
142     /**
143      * Optional. Default is no-op.
144      *
145      * @param successListener a function executed on the main thread upon task success. There are no
146      *     restraints on this as it is executed on the main thread, so lambdas, anonymous, or inner
147      *     classes of your activity or fragment are all fine.
148      */
149     @NonNull
onSuccess(@onNull SuccessListener<OutputT> successListener)150     Builder<InputT, OutputT> onSuccess(@NonNull SuccessListener<OutputT> successListener);
151 
152     /**
153      * Optional. If this is not set and your worker throws an exception, the application will crash.
154      *
155      * @param failureListener a function executed on the main thread upon task failure. There are no
156      *     restraints on this as it is executed on the main thread, so lambdas, anonymous, or inner
157      *     classes of your activity or fragment are all fine.
158      */
159     @NonNull
onFailure(@onNull FailureListener failureListener)160     Builder<InputT, OutputT> onFailure(@NonNull FailureListener failureListener);
161 
162     /**
163      * Builds the {@link DialerExecutor} which can be used to execute your task (repeatedly with
164      * differing inputs if desired).
165      */
166     @NonNull
build()167     DialerExecutor<InputT> build();
168   }
169 
170   /** Executes the task such that repeated executions for this executor are serialized. */
171   @MainThread
executeSerial(@ullable InputT input)172   void executeSerial(@Nullable InputT input);
173 
174   /**
175    * Executes the task after waiting {@code waitMillis}. If called while the previous invocation is
176    * still waiting to be started, the original invocation is cancelled.
177    *
178    * <p>This is useful for tasks which might get scheduled many times in very quick succession, but
179    * it is only the last one that actually needs to be executed.
180    */
181   @MainThread
executeSerialWithWait(@ullable InputT input, long waitMillis)182   void executeSerialWithWait(@Nullable InputT input, long waitMillis);
183 
184   /**
185    * Executes the task on a thread pool shared across the application. Multiple calls using this
186    * method may result in tasks being executed in parallel.
187    */
188   @MainThread
executeParallel(@ullable InputT input)189   void executeParallel(@Nullable InputT input);
190 
191   /**
192    * Executes the task on a custom executor service. This should rarely be used; instead prefer
193    * {@link #executeSerial(Object)} or {@link #executeParallel(Object)}.
194    */
195   @MainThread
executeOnCustomExecutorService( @onNull ExecutorService executorService, @Nullable InputT input)196   void executeOnCustomExecutorService(
197       @NonNull ExecutorService executorService, @Nullable InputT input);
198 }
199