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<MyInputType> 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<MyInputType, MyOutputType> { 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<MyInputType> 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<MyInputType, MyOutputType> { 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 createUiTaskBuilder( @onNull FragmentManager fragmentManager, @NonNull String taskId, @NonNull Worker<InputT, OutputT> worker)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 createNonUiTaskBuilder( @onNull Worker<InputT, OutputT> worker)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