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<MyInputType> 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<MyInputType, MyOutputType> { 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<MyInputType> 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<MyInputType, MyOutputType> { 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