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 android.support.annotation.Nullable; 22 import com.android.dialer.common.Assert; 23 import com.android.dialer.common.LogUtil; 24 import com.android.dialer.common.concurrent.DialerExecutor.Builder; 25 import com.android.dialer.common.concurrent.DialerExecutor.FailureListener; 26 import com.android.dialer.common.concurrent.DialerExecutor.SuccessListener; 27 import com.android.dialer.common.concurrent.DialerExecutor.Worker; 28 import java.util.concurrent.ExecutorService; 29 import java.util.concurrent.Executors; 30 import java.util.concurrent.ThreadFactory; 31 import javax.inject.Inject; 32 33 /** The production {@link DialerExecutorFactory}. */ 34 public class DefaultDialerExecutorFactory implements DialerExecutorFactory { 35 36 @Inject 37 public DefaultDialerExecutorFactory() {} 38 39 @Override 40 @NonNull 41 public <InputT, OutputT> DialerExecutor.Builder<InputT, OutputT> createUiTaskBuilder( 42 @NonNull FragmentManager fragmentManager, 43 @NonNull String taskId, 44 @NonNull Worker<InputT, OutputT> worker) { 45 return new UiTaskBuilder<>( 46 Assert.isNotNull(fragmentManager), Assert.isNotNull(taskId), Assert.isNotNull(worker)); 47 } 48 49 @Override 50 @NonNull 51 public <InputT, OutputT> DialerExecutor.Builder<InputT, OutputT> createNonUiTaskBuilder( 52 @NonNull Worker<InputT, OutputT> worker) { 53 return new NonUiTaskBuilder<>(Assert.isNotNull(worker)); 54 } 55 56 private abstract static class BaseTaskBuilder<InputT, OutputT> 57 implements DialerExecutor.Builder<InputT, OutputT> { 58 59 private final Worker<InputT, OutputT> worker; 60 private SuccessListener<OutputT> successListener = output -> {}; 61 private FailureListener failureListener = 62 throwable -> { 63 throw new RuntimeException(throwable); 64 }; 65 @Nullable final ExecutorService serialExecutorService; 66 @Nullable final ExecutorService parallelExecutorService; 67 68 BaseTaskBuilder( 69 Worker<InputT, OutputT> worker, 70 @Nullable ExecutorService serialExecutorService, 71 @Nullable ExecutorService parallelExecutorService) { 72 this.worker = worker; 73 this.serialExecutorService = serialExecutorService; 74 this.parallelExecutorService = parallelExecutorService; 75 } 76 77 @NonNull 78 @Override 79 public Builder<InputT, OutputT> onSuccess(@NonNull SuccessListener<OutputT> successListener) { 80 this.successListener = Assert.isNotNull(successListener); 81 return this; 82 } 83 84 @NonNull 85 @Override 86 public Builder<InputT, OutputT> onFailure(@NonNull FailureListener failureListener) { 87 this.failureListener = Assert.isNotNull(failureListener); 88 return this; 89 } 90 } 91 92 /** Convenience class for use by {@link DialerExecutorFactory} implementations. */ 93 public static class UiTaskBuilder<InputT, OutputT> extends BaseTaskBuilder<InputT, OutputT> { 94 95 private final FragmentManager fragmentManager; 96 private final String id; 97 98 private DialerUiTaskFragment<InputT, OutputT> dialerUiTaskFragment; 99 100 UiTaskBuilder(FragmentManager fragmentManager, String id, Worker<InputT, OutputT> worker) { 101 this( 102 fragmentManager, 103 id, 104 worker, 105 null /* serialExecutorService */, 106 null /* parallelExecutorService */); 107 } 108 109 public UiTaskBuilder( 110 FragmentManager fragmentManager, 111 String id, 112 Worker<InputT, OutputT> worker, 113 ExecutorService serialExecutor, 114 ExecutorService parallelExecutor) { 115 super(worker, serialExecutor, parallelExecutor); 116 this.fragmentManager = fragmentManager; 117 this.id = id; 118 } 119 120 @NonNull 121 @Override 122 public DialerExecutor<InputT> build() { 123 dialerUiTaskFragment = 124 DialerUiTaskFragment.create( 125 fragmentManager, 126 id, 127 super.worker, 128 super.successListener, 129 super.failureListener, 130 serialExecutorService, 131 parallelExecutorService); 132 return new UiDialerExecutor<>(dialerUiTaskFragment); 133 } 134 } 135 136 /** Convenience class for use by {@link DialerExecutorFactory} implementations. */ 137 public static class NonUiTaskBuilder<InputT, OutputT> extends BaseTaskBuilder<InputT, OutputT> { 138 private static final ExecutorService defaultSerialExecutorService = 139 Executors.newSingleThreadExecutor( 140 new ThreadFactory() { 141 @Override 142 public Thread newThread(Runnable runnable) { 143 LogUtil.i("NonUiTaskBuilder.newThread", "creating serial thread"); 144 Thread thread = new Thread(runnable, "NonUiTaskBuilder"); 145 thread.setPriority(4); // Corresponds to Process.THREAD_PRIORITY_BACKGROUND 146 return thread; 147 } 148 }); 149 150 private static final ExecutorService defaultParallelExecutorService = 151 Executors.newFixedThreadPool( 152 5, 153 new ThreadFactory() { 154 @Override 155 public Thread newThread(Runnable runnable) { 156 LogUtil.i("NonUiTaskBuilder.newThread", "creating parallel thread"); 157 Thread thread = new Thread(runnable, "NonUiTaskBuilder"); 158 thread.setPriority(4); // Corresponds to Process.THREAD_PRIORITY_BACKGROUND 159 return thread; 160 } 161 }); 162 163 NonUiTaskBuilder(Worker<InputT, OutputT> worker) { 164 this(worker, defaultSerialExecutorService, defaultParallelExecutorService); 165 } 166 167 public NonUiTaskBuilder( 168 Worker<InputT, OutputT> worker, 169 @NonNull ExecutorService serialExecutor, 170 @NonNull ExecutorService parallelExecutor) { 171 super(worker, Assert.isNotNull(serialExecutor), Assert.isNotNull(parallelExecutor)); 172 } 173 174 @NonNull 175 @Override 176 public DialerExecutor<InputT> build() { 177 return new NonUiDialerExecutor<>( 178 super.worker, 179 super.successListener, 180 super.failureListener, 181 serialExecutorService, 182 parallelExecutorService); 183 } 184 } 185 186 private static class UiDialerExecutor<InputT, OutputT> implements DialerExecutor<InputT> { 187 188 private final DialerUiTaskFragment<InputT, OutputT> dialerUiTaskFragment; 189 190 UiDialerExecutor(DialerUiTaskFragment<InputT, OutputT> dialerUiTaskFragment) { 191 this.dialerUiTaskFragment = dialerUiTaskFragment; 192 } 193 194 @Override 195 public void executeSerial(@Nullable InputT input) { 196 dialerUiTaskFragment.executeSerial(input); 197 } 198 199 @Override 200 public void executeParallel(@Nullable InputT input) { 201 dialerUiTaskFragment.executeParallel(input); 202 } 203 204 @Override 205 public void executeOnCustomExecutorService( 206 @NonNull ExecutorService executorService, @Nullable InputT input) { 207 dialerUiTaskFragment.executeOnCustomExecutor(Assert.isNotNull(executorService), input); 208 } 209 } 210 211 private static class NonUiDialerExecutor<InputT, OutputT> implements DialerExecutor<InputT> { 212 213 private final Worker<InputT, OutputT> worker; 214 private final SuccessListener<OutputT> successListener; 215 private final FailureListener failureListener; 216 217 private final ExecutorService serialExecutorService; 218 private final ExecutorService parallelExecutorService; 219 220 NonUiDialerExecutor( 221 Worker<InputT, OutputT> worker, 222 SuccessListener<OutputT> successListener, 223 FailureListener failureListener, 224 ExecutorService serialExecutorService, 225 ExecutorService parallelExecutorService) { 226 this.worker = worker; 227 this.successListener = successListener; 228 this.failureListener = failureListener; 229 this.serialExecutorService = serialExecutorService; 230 this.parallelExecutorService = parallelExecutorService; 231 } 232 233 @Override 234 public void executeSerial(@Nullable InputT input) { 235 executeOnCustomExecutorService(serialExecutorService, input); 236 } 237 238 @Override 239 public void executeParallel(@Nullable InputT input) { 240 executeOnCustomExecutorService(parallelExecutorService, input); 241 } 242 243 @Override 244 public void executeOnCustomExecutorService( 245 @NonNull ExecutorService executorService, @Nullable InputT input) { 246 Assert.isNotNull(executorService) 247 .execute( 248 () -> { 249 OutputT output; 250 try { 251 output = worker.doInBackground(input); 252 } catch (Throwable throwable) { 253 ThreadUtil.postOnUiThread(() -> failureListener.onFailure(throwable)); 254 return; 255 } 256 ThreadUtil.postOnUiThread(() -> successListener.onSuccess(output)); 257 }); 258 } 259 } 260 } 261