blob: 82e517d9a28ebbd2ed2fb1f52b05a04695a432ba [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.dialer.common.concurrent;
import android.app.FragmentManager;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.DialerExecutor.Builder;
import com.android.dialer.common.concurrent.DialerExecutor.FailureListener;
import com.android.dialer.common.concurrent.DialerExecutor.SuccessListener;
import com.android.dialer.common.concurrent.DialerExecutor.Worker;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
/** The production {@link DialerExecutorFactory}. */
public class DefaultDialerExecutorFactory implements DialerExecutorFactory {
@Inject
public DefaultDialerExecutorFactory() {}
@Override
@NonNull
public <InputT, OutputT> DialerExecutor.Builder<InputT, OutputT> createUiTaskBuilder(
@NonNull FragmentManager fragmentManager,
@NonNull String taskId,
@NonNull Worker<InputT, OutputT> worker) {
return new UiTaskBuilder<>(
Assert.isNotNull(fragmentManager), Assert.isNotNull(taskId), Assert.isNotNull(worker));
}
@Override
@NonNull
public <InputT, OutputT> DialerExecutor.Builder<InputT, OutputT> createNonUiTaskBuilder(
@NonNull Worker<InputT, OutputT> worker) {
return new NonUiTaskBuilder<>(Assert.isNotNull(worker));
}
private abstract static class BaseTaskBuilder<InputT, OutputT>
implements DialerExecutor.Builder<InputT, OutputT> {
private final Worker<InputT, OutputT> worker;
private SuccessListener<OutputT> successListener = output -> {};
private FailureListener failureListener =
throwable -> {
throw new RuntimeException(throwable);
};
@Nullable final ScheduledExecutorService serialExecutorService;
@Nullable final Executor parallelExecutor;
BaseTaskBuilder(
Worker<InputT, OutputT> worker,
@Nullable ScheduledExecutorService serialExecutorService,
@Nullable Executor parallelExecutor) {
this.worker = worker;
this.serialExecutorService = serialExecutorService;
this.parallelExecutor = parallelExecutor;
}
@NonNull
@Override
public Builder<InputT, OutputT> onSuccess(@NonNull SuccessListener<OutputT> successListener) {
this.successListener = Assert.isNotNull(successListener);
return this;
}
@NonNull
@Override
public Builder<InputT, OutputT> onFailure(@NonNull FailureListener failureListener) {
this.failureListener = Assert.isNotNull(failureListener);
return this;
}
}
/** Convenience class for use by {@link DialerExecutorFactory} implementations. */
public static class UiTaskBuilder<InputT, OutputT> extends BaseTaskBuilder<InputT, OutputT> {
private static final ScheduledExecutorService defaultSerialExecutorService =
Executors.newSingleThreadScheduledExecutor(
new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
LogUtil.i("UiTaskBuilder.newThread", "creating serial thread");
Thread thread = new Thread(runnable, "UiTaskBuilder-Serial");
thread.setPriority(5); // Corresponds to Process.THREAD_PRIORITY_DEFAULT
return thread;
}
});
private static final Executor defaultParallelExecutorService = AsyncTask.THREAD_POOL_EXECUTOR;
private final FragmentManager fragmentManager;
private final String id;
private DialerUiTaskFragment<InputT, OutputT> dialerUiTaskFragment;
UiTaskBuilder(FragmentManager fragmentManager, String id, Worker<InputT, OutputT> worker) {
this(
fragmentManager,
id,
worker,
defaultSerialExecutorService,
defaultParallelExecutorService);
}
public UiTaskBuilder(
FragmentManager fragmentManager,
String id,
Worker<InputT, OutputT> worker,
ScheduledExecutorService serialExecutor,
Executor parallelExecutor) {
super(worker, serialExecutor, parallelExecutor);
this.fragmentManager = fragmentManager;
this.id = id;
}
@NonNull
@Override
public DialerExecutor<InputT> build() {
dialerUiTaskFragment =
DialerUiTaskFragment.create(
fragmentManager,
id,
super.worker,
super.successListener,
super.failureListener,
serialExecutorService,
parallelExecutor);
return new UiDialerExecutor<>(dialerUiTaskFragment);
}
}
/** Convenience class for use by {@link DialerExecutorFactory} implementations. */
public static class NonUiTaskBuilder<InputT, OutputT> extends BaseTaskBuilder<InputT, OutputT> {
private static final ScheduledExecutorService defaultSerialExecutorService =
Executors.newSingleThreadScheduledExecutor(
new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
LogUtil.i("NonUiTaskBuilder.newThread", "creating serial thread");
Thread thread = new Thread(runnable, "NonUiTaskBuilder-Serial");
thread.setPriority(4); // Corresponds to Process.THREAD_PRIORITY_BACKGROUND
return thread;
}
});
private static final Executor defaultParallelExecutor =
Executors.newFixedThreadPool(
5,
new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
LogUtil.i("NonUiTaskBuilder.newThread", "creating parallel thread");
Thread thread = new Thread(runnable, "NonUiTaskBuilder-Parallel");
thread.setPriority(4); // Corresponds to Process.THREAD_PRIORITY_BACKGROUND
return thread;
}
});
NonUiTaskBuilder(Worker<InputT, OutputT> worker) {
this(worker, defaultSerialExecutorService, defaultParallelExecutor);
}
public NonUiTaskBuilder(
Worker<InputT, OutputT> worker,
@NonNull ScheduledExecutorService serialExecutor,
@NonNull Executor parallelExecutor) {
super(worker, Assert.isNotNull(serialExecutor), Assert.isNotNull(parallelExecutor));
}
@NonNull
@Override
public DialerExecutor<InputT> build() {
return new NonUiDialerExecutor<>(
super.worker,
super.successListener,
super.failureListener,
serialExecutorService,
parallelExecutor);
}
}
private static class UiDialerExecutor<InputT, OutputT> implements DialerExecutor<InputT> {
private final DialerUiTaskFragment<InputT, OutputT> dialerUiTaskFragment;
UiDialerExecutor(DialerUiTaskFragment<InputT, OutputT> dialerUiTaskFragment) {
this.dialerUiTaskFragment = dialerUiTaskFragment;
}
@Override
public void executeSerial(@Nullable InputT input) {
dialerUiTaskFragment.executeSerial(input);
}
@Override
public void executeSerialWithWait(@Nullable InputT input, long waitMillis) {
dialerUiTaskFragment.executeSerialWithWait(input, waitMillis);
}
@Override
public void executeParallel(@Nullable InputT input) {
dialerUiTaskFragment.executeParallel(input);
}
@Override
public void executeOnCustomExecutorService(
@NonNull ExecutorService executorService, @Nullable InputT input) {
dialerUiTaskFragment.executeOnCustomExecutor(Assert.isNotNull(executorService), input);
}
}
private static class NonUiDialerExecutor<InputT, OutputT> implements DialerExecutor<InputT> {
private final Worker<InputT, OutputT> worker;
private final SuccessListener<OutputT> successListener;
private final FailureListener failureListener;
private final ScheduledExecutorService serialExecutorService;
private final Executor parallelExecutor;
private ScheduledFuture<?> scheduledFuture;
NonUiDialerExecutor(
Worker<InputT, OutputT> worker,
SuccessListener<OutputT> successListener,
FailureListener failureListener,
ScheduledExecutorService serialExecutorService,
Executor parallelExecutor) {
this.worker = worker;
this.successListener = successListener;
this.failureListener = failureListener;
this.serialExecutorService = serialExecutorService;
this.parallelExecutor = parallelExecutor;
}
@Override
public void executeSerial(@Nullable InputT input) {
serialExecutorService.execute(() -> run(input));
}
@Override
public void executeSerialWithWait(@Nullable InputT input, long waitMillis) {
if (scheduledFuture != null) {
LogUtil.i("NonUiDialerExecutor.executeSerialWithWait", "cancelling waiting task");
scheduledFuture.cancel(false /* mayInterrupt */);
}
scheduledFuture =
serialExecutorService.schedule(() -> run(input), waitMillis, TimeUnit.MILLISECONDS);
}
@Override
public void executeParallel(@Nullable InputT input) {
parallelExecutor.execute(() -> run(input));
}
@Override
public void executeOnCustomExecutorService(
@NonNull ExecutorService executorService, @Nullable InputT input) {
Assert.isNotNull(executorService).execute(() -> run(input));
}
private void run(@Nullable InputT input) {
OutputT output;
try {
output = worker.doInBackground(input);
} catch (Throwable throwable) {
ThreadUtil.postOnUiThread(() -> failureListener.onFailure(throwable));
return;
}
ThreadUtil.postOnUiThread(() -> successListener.onSuccess(output));
}
}
}