Home | History | Annotate | Download | only in concurrent
      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
    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
    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
    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
    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
    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
    167     DialerExecutor<InputT> build();
    168   }
    169 
    170   /** Executes the task such that repeated executions for this executor are serialized. */
    171   @MainThread
    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
    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
    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
    196   void executeOnCustomExecutorService(
    197       @NonNull ExecutorService executorService, @Nullable InputT input);
    198 }
    199