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.app.FragmentManager;
     20 import android.support.annotation.NonNull;
     21 import com.android.dialer.common.Assert;
     22 import com.android.dialer.common.concurrent.DialerExecutor.Worker;
     23 
     24 /**
     25  * Factory methods for creating {@link DialerExecutor} objects for doing background work.
     26  *
     27  * <p>You may create an executor from a UI component (activity or fragment) or a non-UI component.
     28  * Using this class provides a number of benefits:
     29  *
     30  * <ul>
     31  *   <li>Ensures that UI tasks keep running across configuration changes by using a headless
     32  *       fragment.
     33  *   <li>Forces exceptions to crash the application, unless the user implements their own onFailure
     34  *       method.
     35  *   <li>Checks for dead UI components which can be encountered if a UI task runs longer than its
     36  *       UI. If a dead UI component is encountered, onSuccess/onFailure are not called (because they
     37  *       can't be) but a message is logged.
     38  *   <li>Helps prevents memory leaks in UI tasks by ensuring that callbacks are nulled out when the
     39  *       headless fragment is detached.
     40  *   <li>UI and non-UI threads are shared across the application and run at reasonable priorities
     41  * </ul>
     42  *
     43  * <p>Executors accept a single input and output parameter which should be immutable data objects.
     44  * If you don't require an input or output, use Void and null as needed.
     45  *
     46  * <p>You may optionally specify onSuccess and onFailure listeners; the default behavior on success
     47  * is a no-op and the default behavior on failure is to crash the application.
     48  *
     49  * <p>To use an executor from a UI component, you must create it in your onCreate method and then
     50  * use it from anywhere:
     51  *
     52  * <pre><code>
     53  *
     54  * public class MyActivity extends Activity {
     55  *
     56  *   private final DialerExecutor&lt;MyInputType&gt; myExecutor;
     57  *
     58  *   public void onCreate(Bundle state) {
     59  *     super.onCreate(bundle);
     60  *
     61  *     // Must be called in onCreate; don't use non-static or anonymous inner classes for worker!
     62  *     myExecutor = DialerExecutors.createUiTaskBuilder(fragmentManager, taskId, worker)
     63  *         .onSuccess(this::onSuccess)  // Lambdas, anonymous, or non-static inner classes all fine
     64  *         .onFailure(this::onFailure)  // Lambdas, anonymous, or non-static inner classes all fine
     65  *         .build();
     66  *     );
     67  *   }
     68  *
     69  *   private static class MyWorker implements Worker&lt;MyInputType, MyOutputType&gt; {
     70  *     MyOutputType doInBackground(MyInputType input) { ... }
     71  *   }
     72  *   private void onSuccess(MyOutputType output) { ... }
     73  *   private void onFailure(Throwable throwable) { ... }
     74  *
     75  *   private void userDidSomething() { myExecutor.executeParallel(input); }
     76  * }
     77  * </code></pre>
     78  *
     79  * <p>Usage for non-UI tasks is the same, except that tasks can be created from anywhere instead of
     80  * in onCreate. Non-UI tasks use low-priority threads separate from the UI task threads so as not to
     81  * compete with more critical UI tasks.
     82  *
     83  * <pre><code>
     84  *
     85  * public class MyManager {
     86  *
     87  *   private final DialerExecutor&lt;MyInputType&gt; myExecutor;
     88  *
     89  *   public void init() {
     90  *     // Don't use non-static or anonymous inner classes for worker!
     91  *     myExecutor = DialerExecutors.createNonUiTaskBuilder(worker)
     92  *         .onSuccess(this::onSuccess)  // Lambdas, anonymous, or non-static inner classes all fine
     93  *         .onFailure(this::onFailure)  // Lambdas, anonymous, or non-static inner classes all fine
     94  *         .build();
     95  *     );
     96  *   }
     97  *
     98  *   private static class MyWorker implements Worker&lt;MyInputType, MyOutputType&gt; {
     99  *     MyOutputType doInBackground(MyInputType input) { ... }
    100  *   }
    101  *   private void onSuccess(MyOutputType output) { ... }
    102  *   private void onFailure(Throwable throwable) { ... }
    103  *
    104  *   private void userDidSomething() { myExecutor.executeParallel(input); }
    105  * }
    106  * </code></pre>
    107  *
    108  * Note that non-UI tasks are intended to be relatively quick; for example reading/writing shared
    109  * preferences or doing simple database work. If you submit long running non-UI tasks you may
    110  * saturate the shared application threads and block other tasks. Also, this class does not create
    111  * any wakelocks, so a long running task could be killed if the device goes to sleep while your task
    112  * is still running. If you have to do long running or periodic work, consider using a job
    113  * scheduler.
    114  */
    115 public final class DialerExecutors {
    116 
    117   /** @see DialerExecutorFactory#createUiTaskBuilder(FragmentManager, String, Worker) */
    118   @NonNull
    119   public static <InputT, OutputT> DialerExecutor.Builder<InputT, OutputT> createUiTaskBuilder(
    120       @NonNull FragmentManager fragmentManager,
    121       @NonNull String taskId,
    122       @NonNull Worker<InputT, OutputT> worker) {
    123     return new DefaultDialerExecutorFactory()
    124         .createUiTaskBuilder(
    125             Assert.isNotNull(fragmentManager), Assert.isNotNull(taskId), Assert.isNotNull(worker));
    126   }
    127 
    128   /** @see DialerExecutorFactory#createNonUiTaskBuilder(Worker) */
    129   @NonNull
    130   public static <InputT, OutputT> DialerExecutor.Builder<InputT, OutputT> createNonUiTaskBuilder(
    131       @NonNull Worker<InputT, OutputT> worker) {
    132     return new DefaultDialerExecutorFactory().createNonUiTaskBuilder(Assert.isNotNull(worker));
    133   }
    134 }
    135