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.Fragment; 20 import android.app.FragmentManager; 21 import android.os.AsyncTask; 22 import android.os.Bundle; 23 import android.support.annotation.MainThread; 24 import android.support.annotation.Nullable; 25 import com.android.dialer.common.Assert; 26 import com.android.dialer.common.LogUtil; 27 import com.android.dialer.common.concurrent.AsyncTaskExecutors.SimpleAsyncTaskExecutor; 28 import com.android.dialer.common.concurrent.DialerExecutor.FailureListener; 29 import com.android.dialer.common.concurrent.DialerExecutor.SuccessListener; 30 import com.android.dialer.common.concurrent.DialerExecutor.Worker; 31 import java.util.concurrent.ExecutorService; 32 33 /** 34 * Do not use this class directly. Instead use {@link DialerExecutors}. 35 * 36 * @paramthe type of the object sent to the task upon execution 37 * @param the type of the result of the background computation 38 */ 39 public final class DialerUiTaskFragment<InputT, OutputT> extends Fragment { 40 41 private String taskId; 42 private Worker<InputT, OutputT> worker; 43 private SuccessListener<OutputT> successListener; 44 private FailureListener failureListener; 45 46 private AsyncTaskExecutor serialExecutor = AsyncTaskExecutors.createAsyncTaskExecutor(); 47 private AsyncTaskExecutor parallelExecutor = AsyncTaskExecutors.createThreadPoolExecutor(); 48 49 /** 50 * Creates a new {@link DialerUiTaskFragment} or gets an existing one in the event that a 51 * configuration change occurred while the previous activity's task was still running. Must be 52 * called from onCreate of your activity or fragment. 53 * 54 * @param taskId used for the headless fragment ID and task ID 55 * @param worker a function executed on a worker thread which accepts an {@link InputT} and 56 * returns an {@link OutputT}. It should ideally not be an inner class of your 57 * activity/fragment (meaning it should not be a lambda, anonymous, or non-static) but it can 58 * be a static nested class. The static nested class should not contain any reference to UI, 59 * including any activity or fragment or activity context, though it may reference some 60 * threadsafe system objects such as the application context. 61 * @param successListener a function executed on the main thread upon task success. There are no 62 * restraints on this as it is executed on the main thread, so lambdas, anonymous, or inner 63 * classes of your activity or fragment are all fine. 64 * @param failureListener a function executed on the main thread upon task failure. The exception 65 * is already logged so this can often be a no-op. There are no restraints on this as it is 66 * executed on the main thread, so lambdas, anonymous, or inner classes of your activity or 67 * fragment are all fine. 68 * @param the type of the object sent to the task upon execution 69 * @param the type of the result of the background computation 70 * @return a {@link DialerUiTaskFragment} which may be used to call the "execute*" methods 71 */ 72 @MainThread 73 static <InputT, OutputT> DialerUiTaskFragment<InputT, OutputT> create( 74 FragmentManager fragmentManager, 75 String taskId, 76 Worker<InputT, OutputT> worker, 77 SuccessListener<OutputT> successListener, 78 FailureListener failureListener, 79 @Nullable ExecutorService serialExecutorService, 80 @Nullable ExecutorService parallelExecutorService) { 81 Assert.isMainThread(); 82 83 DialerUiTaskFragment<InputT, OutputT> fragment = 84 (DialerUiTaskFragment<InputT, OutputT>) fragmentManager.findFragmentByTag(taskId); 85 86 if (fragment == null) { 87 LogUtil.i("DialerUiTaskFragment.create", "creating new DialerUiTaskFragment"); 88 fragment = new DialerUiTaskFragment<>(); 89 fragmentManager.beginTransaction().add(fragment, taskId).commit(); 90 } 91 fragment.taskId = taskId; 92 fragment.worker = worker; 93 fragment.successListener = successListener; 94 fragment.failureListener = failureListener; 95 if (serialExecutorService != null) { 96 fragment.serialExecutor = new SimpleAsyncTaskExecutor(serialExecutorService); 97 } 98 if (parallelExecutorService != null) { 99 fragment.parallelExecutor = new SimpleAsyncTaskExecutor(parallelExecutorService); 100 } 101 return fragment; 102 } 103 104 @Override 105 public void onCreate(Bundle savedInstanceState) { 106 super.onCreate(savedInstanceState); 107 setRetainInstance(true); 108 } 109 110 @Override 111 public void onDetach() { 112 super.onDetach(); 113 LogUtil.enterBlock("DialerUiTaskFragment.onDetach"); 114 taskId = null; 115 successListener = null; 116 failureListener = null; 117 } 118 119 void executeSerial(InputT input) { 120 serialExecutor.submit(taskId, new InternalTask(), input); 121 } 122 123 void executeParallel(InputT input) { 124 parallelExecutor.submit(taskId, new InternalTask(), input); 125 } 126 127 void executeOnCustomExecutor(ExecutorService executor, InputT input) { 128 new SimpleAsyncTaskExecutor(executor).submit(taskId, new InternalTask(), input); 129 } 130 131 private final class InternalTask extends AsyncTask<InputT, Void, InternalTaskResult<OutputT>> { 132 133 @SafeVarargs 134 @Override 135 protected final InternalTaskResult<OutputT> doInBackground(InputT... params) { 136 try { 137 return new InternalTaskResult<>(null, worker.doInBackground(params[0])); 138 } catch (Throwable throwable) { 139 LogUtil.e("InternalTask.doInBackground", "task failed", throwable); 140 return new InternalTaskResult<>(throwable, null); 141 } 142 } 143 144 @Override 145 protected void onPostExecute(InternalTaskResult<OutputT> result) { 146 if (result.throwable != null) { 147 if (failureListener == null) { 148 LogUtil.i("InternalTask.onPostExecute", "task failed but UI is dead"); 149 } else { 150 failureListener.onFailure(result.throwable); 151 } 152 } else if (successListener == null) { 153 LogUtil.i("InternalTask.onPostExecute", "task succeeded but UI is dead"); 154 } else { 155 successListener.onSuccess(result.result); 156 } 157 } 158 } 159 160 private static class InternalTaskResult<OutputT> { 161 162 private final Throwable throwable; 163 private final OutputT result; 164 165 InternalTaskResult(Throwable throwable, OutputT result) { 166 this.throwable = throwable; 167 this.result = result; 168 } 169 } 170 } 171