summaryrefslogtreecommitdiffstats
path: root/java/com/android/dialer/common
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/dialer/common')
-rw-r--r--java/com/android/dialer/common/Assert.java4
-rw-r--r--java/com/android/dialer/common/concurrent/AsyncTaskExecutors.java2
-rw-r--r--java/com/android/dialer/common/concurrent/DefaultDialerExecutorFactory.java255
-rw-r--r--java/com/android/dialer/common/concurrent/DialerExecutor.java100
-rw-r--r--java/com/android/dialer/common/concurrent/DialerExecutorFactory.java63
-rw-r--r--java/com/android/dialer/common/concurrent/DialerExecutors.java134
-rw-r--r--java/com/android/dialer/common/concurrent/DialerUiTaskFragment.java (renamed from java/com/android/dialer/common/concurrent/DialerAsyncTaskHelper.java)142
-rw-r--r--java/com/android/dialer/common/concurrent/ThreadUtil.java38
8 files changed, 633 insertions, 105 deletions
diff --git a/java/com/android/dialer/common/Assert.java b/java/com/android/dialer/common/Assert.java
index 943e1ddcf..189d209c8 100644
--- a/java/com/android/dialer/common/Assert.java
+++ b/java/com/android/dialer/common/Assert.java
@@ -30,6 +30,10 @@ public class Assert {
Assert.areThreadAssertsEnabled = areThreadAssertsEnabled;
}
+ public static boolean areThreadAssertsEnabled() {
+ return areThreadAssertsEnabled;
+ }
+
/**
* Called when a truly exceptional case occurs.
*
diff --git a/java/com/android/dialer/common/concurrent/AsyncTaskExecutors.java b/java/com/android/dialer/common/concurrent/AsyncTaskExecutors.java
index fd358328e..a25e9fbf5 100644
--- a/java/com/android/dialer/common/concurrent/AsyncTaskExecutors.java
+++ b/java/com/android/dialer/common/concurrent/AsyncTaskExecutors.java
@@ -74,7 +74,7 @@ public final class AsyncTaskExecutors {
AsyncTaskExecutor createAsyncTaskExeuctor();
}
- private static class SimpleAsyncTaskExecutor implements AsyncTaskExecutor {
+ static class SimpleAsyncTaskExecutor implements AsyncTaskExecutor {
private final Executor mExecutor;
diff --git a/java/com/android/dialer/common/concurrent/DefaultDialerExecutorFactory.java b/java/com/android/dialer/common/concurrent/DefaultDialerExecutorFactory.java
new file mode 100644
index 000000000..80ed725bc
--- /dev/null
+++ b/java/com/android/dialer/common/concurrent/DefaultDialerExecutorFactory.java
@@ -0,0 +1,255 @@
+/*
+ * 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.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.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+/** The production {@link DialerExecutorFactory}. */
+public class DefaultDialerExecutorFactory implements DialerExecutorFactory {
+
+ @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 ExecutorService serialExecutorService;
+ @Nullable final ExecutorService parallelExecutorService;
+
+ BaseTaskBuilder(
+ Worker<InputT, OutputT> worker,
+ @Nullable ExecutorService serialExecutorService,
+ @Nullable ExecutorService parallelExecutorService) {
+ this.worker = worker;
+ this.serialExecutorService = serialExecutorService;
+ this.parallelExecutorService = parallelExecutorService;
+ }
+
+ @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 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,
+ null /* serialExecutorService */,
+ null /* parallelExecutorService */);
+ }
+
+ public UiTaskBuilder(
+ FragmentManager fragmentManager,
+ String id,
+ Worker<InputT, OutputT> worker,
+ ExecutorService serialExecutor,
+ ExecutorService 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,
+ parallelExecutorService);
+ 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 ExecutorService defaultSerialExecutorService =
+ Executors.newSingleThreadExecutor(
+ new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable runnable) {
+ LogUtil.i("NonUiTaskBuilder.newThread", "creating serial thread");
+ Thread thread = new Thread(runnable, "NonUiTaskBuilder");
+ thread.setPriority(4); // Corresponds to Process.THREAD_PRIORITY_BACKGROUND
+ return thread;
+ }
+ });
+
+ private static final ExecutorService defaultParallelExecutorService =
+ Executors.newFixedThreadPool(
+ 5,
+ new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable runnable) {
+ LogUtil.i("NonUiTaskBuilder.newThread", "creating parallel thread");
+ Thread thread = new Thread(runnable, "NonUiTaskBuilder");
+ thread.setPriority(4); // Corresponds to Process.THREAD_PRIORITY_BACKGROUND
+ return thread;
+ }
+ });
+
+ NonUiTaskBuilder(Worker<InputT, OutputT> worker) {
+ this(worker, defaultSerialExecutorService, defaultParallelExecutorService);
+ }
+
+ public NonUiTaskBuilder(
+ Worker<InputT, OutputT> worker,
+ @NonNull ExecutorService serialExecutor,
+ @NonNull ExecutorService 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,
+ parallelExecutorService);
+ }
+ }
+
+ 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 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 ExecutorService serialExecutorService;
+ private final ExecutorService parallelExecutorService;
+
+ NonUiDialerExecutor(
+ Worker<InputT, OutputT> worker,
+ SuccessListener<OutputT> successListener,
+ FailureListener failureListener,
+ ExecutorService serialExecutorService,
+ ExecutorService parallelExecutorService) {
+ this.worker = worker;
+ this.successListener = successListener;
+ this.failureListener = failureListener;
+ this.serialExecutorService = serialExecutorService;
+ this.parallelExecutorService = parallelExecutorService;
+ }
+
+ @Override
+ public void executeSerial(@Nullable InputT input) {
+ executeOnCustomExecutorService(serialExecutorService, input);
+ }
+
+ @Override
+ public void executeParallel(@Nullable InputT input) {
+ executeOnCustomExecutorService(parallelExecutorService, input);
+ }
+
+ @Override
+ public void executeOnCustomExecutorService(
+ @NonNull ExecutorService executorService, @Nullable InputT input) {
+ Assert.isNotNull(executorService)
+ .execute(
+ () -> {
+ OutputT output;
+ try {
+ output = worker.doInBackground(input);
+ } catch (Throwable throwable) {
+ ThreadUtil.postOnUiThread(() -> failureListener.onFailure(throwable));
+ return;
+ }
+ ThreadUtil.postOnUiThread(() -> successListener.onSuccess(output));
+ });
+ }
+ }
+}
diff --git a/java/com/android/dialer/common/concurrent/DialerExecutor.java b/java/com/android/dialer/common/concurrent/DialerExecutor.java
new file mode 100644
index 000000000..b0d1eac66
--- /dev/null
+++ b/java/com/android/dialer/common/concurrent/DialerExecutor.java
@@ -0,0 +1,100 @@
+/*
+ * 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.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.WorkerThread;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Provides a consistent interface for doing background work in either UI or non-UI contexts.
+ *
+ * <p>See {@link DialerExecutors} for usage examples.
+ */
+public interface DialerExecutor<InputT> {
+
+ /** Functional interface for doing work in the background. */
+ interface Worker<InputT, OutputT> {
+ @WorkerThread
+ @Nullable
+ OutputT doInBackground(@Nullable InputT input);
+ }
+
+ /** Functional interface for handling the result of background work. */
+ interface SuccessListener<OutputT> {
+ @MainThread
+ void onSuccess(@Nullable OutputT output);
+ }
+
+ /** Functional interface for handling an error produced while performing background work. */
+ interface FailureListener {
+ @MainThread
+ void onFailure(@NonNull Throwable throwable);
+ }
+
+ /** Builder for {@link DialerExecutor}. */
+ interface Builder<InputT, OutputT> {
+
+ /**
+ * Optional. Default is no-op.
+ *
+ * @param successListener a function executed on the main thread upon task success. There are no
+ * restraints on this as it is executed on the main thread, so lambdas, anonymous, or inner
+ * classes of your activity or fragment are all fine.
+ */
+ @NonNull
+ Builder<InputT, OutputT> onSuccess(@NonNull SuccessListener<OutputT> successListener);
+
+ /**
+ * Optional. If this is not set and your worker throws an exception, the application will crash.
+ *
+ * @param failureListener a function executed on the main thread upon task failure. There are no
+ * restraints on this as it is executed on the main thread, so lambdas, anonymous, or inner
+ * classes of your activity or fragment are all fine.
+ */
+ @NonNull
+ Builder<InputT, OutputT> onFailure(@NonNull FailureListener failureListener);
+
+ /**
+ * Builds the {@link DialerExecutor} which can be used to execute your task (repeatedly with
+ * differing inputs if desired).
+ */
+ @NonNull
+ DialerExecutor<InputT> build();
+ }
+
+ /** Executes the task such that repeated executions for this executor are serialized. */
+ @MainThread
+ void executeSerial(@Nullable InputT input);
+
+ /**
+ * Executes the task on a thread pool shared across the application. Multiple calls using this
+ * method may result in tasks being executed in parallel.
+ */
+ @MainThread
+ void executeParallel(@Nullable InputT input);
+
+ /**
+ * Executes the task on a custom executor service. This should rarely be used; instead prefer
+ * {@link #executeSerial(Object)} or {@link #executeParallel(Object)}.
+ */
+ @MainThread
+ void executeOnCustomExecutorService(
+ @NonNull ExecutorService executorService, @Nullable InputT input);
+}
diff --git a/java/com/android/dialer/common/concurrent/DialerExecutorFactory.java b/java/com/android/dialer/common/concurrent/DialerExecutorFactory.java
new file mode 100644
index 000000000..82f8c7c3d
--- /dev/null
+++ b/java/com/android/dialer/common/concurrent/DialerExecutorFactory.java
@@ -0,0 +1,63 @@
+/*
+ * 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.support.annotation.NonNull;
+import com.android.dialer.common.concurrent.DialerExecutor.Worker;
+
+/**
+ * Factory interface for creating {@link DialerExecutor} objects.
+ *
+ * <p>Factory instances may be used instead of the static methods in {@link DialerExecutors} in
+ * order to improve testability.
+ *
+ * @see DialerExecutors
+ */
+public interface DialerExecutorFactory {
+
+ /**
+ * Must be called from onCreate of your activity or fragment.
+ *
+ * @param taskId used for the headless fragment ID and task ID
+ * @param worker a function executed on a worker thread which accepts an {@link InputT} and
+ * returns an {@link OutputT}. It should ideally not be an inner class of your (meaning it
+ * should not be a lambda, anonymous, or non-static) but it can be a static nested class. The
+ * static nested class should not contain any reference to UI, including any activity or
+ * fragment or activity context, though it may reference some threadsafe system objects such
+ * as the application context.
+ */
+ @NonNull
+ <InputT, OutputT> DialerExecutor.Builder<InputT, OutputT> createUiTaskBuilder(
+ @NonNull FragmentManager fragmentManager,
+ @NonNull String taskId,
+ @NonNull Worker<InputT, OutputT> worker);
+
+ /**
+ * Create a task from a non-UI context.
+ *
+ * @param worker a function executed on a worker thread which accepts an {@link InputT} and
+ * returns an {@link OutputT}. It should ideally not be an inner class of your (meaning it
+ * should not be a lambda, anonymous, or non-static) but it can be a static nested class. The
+ * static nested class should not contain any reference to UI, including any activity or
+ * fragment or activity context, though it may reference some threadsafe system objects such
+ * as the application context.
+ */
+ @NonNull
+ <InputT, OutputT> DialerExecutor.Builder<InputT, OutputT> createNonUiTaskBuilder(
+ @NonNull Worker<InputT, OutputT> worker);
+}
diff --git a/java/com/android/dialer/common/concurrent/DialerExecutors.java b/java/com/android/dialer/common/concurrent/DialerExecutors.java
new file mode 100644
index 000000000..148d8660c
--- /dev/null
+++ b/java/com/android/dialer/common/concurrent/DialerExecutors.java
@@ -0,0 +1,134 @@
+/*
+ * 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.support.annotation.NonNull;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.concurrent.DialerExecutor.Worker;
+
+/**
+ * Factory methods for creating {@link DialerExecutor} objects for doing background work.
+ *
+ * <p>You may create an executor from a UI component (activity or fragment) or a non-UI component.
+ * Using this class provides a number of benefits:
+ *
+ * <ul>
+ * <li>Ensures that UI tasks keep running across configuration changes by using a headless
+ * fragment.
+ * <li>Forces exceptions to crash the application, unless the user implements their own onFailure
+ * method.
+ * <li>Checks for dead UI components which can be encountered if a UI task runs longer than its
+ * UI. If a dead UI component is encountered, onSuccess/onFailure are not called (because they
+ * can't be) but a message is logged.
+ * <li>Helps prevents memory leaks in UI tasks by ensuring that callbacks are nulled out when the
+ * headless fragment is detached.
+ * <li>UI and non-UI threads are shared across the application and run at reasonable priorities
+ * </ul>
+ *
+ * <p>Executors accept a single input and output parameter which should be immutable data objects.
+ * If you don't require an input or output, use Void and null as needed.
+ *
+ * <p>You may optionally specify onSuccess and onFailure listeners; the default behavior on success
+ * is a no-op and the default behavior on failure is to crash the application.
+ *
+ * <p>To use an executor from a UI component, you must create it in your onCreate method and then
+ * use it from anywhere:
+ *
+ * <pre><code>
+ *
+ * public class MyActivity extends Activity {
+ *
+ * private final DialerExecutor&lt;MyInputType&gt; myExecutor;
+ *
+ * public void onCreate(Bundle state) {
+ * super.onCreate(bundle);
+ *
+ * // Must be called in onCreate; don't use non-static or anonymous inner classes for worker!
+ * myExecutor = DialerExecutors.createUiTaskBuilder(fragmentManager, taskId, worker)
+ * .onSuccess(this::onSuccess) // Lambdas, anonymous, or non-static inner classes all fine
+ * .onFailure(this::onFailure) // Lambdas, anonymous, or non-static inner classes all fine
+ * .build();
+ * );
+ * }
+ *
+ * private static class MyWorker implements Worker&lt;MyInputType, MyOutputType&gt; {
+ * MyOutputType doInBackground(MyInputType input) { ... }
+ * }
+ * private void onSuccess(MyOutputType output) { ... }
+ * private void onFailure(Throwable throwable) { ... }
+ *
+ * private void userDidSomething() { myExecutor.executeParallel(input); }
+ * }
+ * </code></pre>
+ *
+ * <p>Usage for non-UI tasks is the same, except that tasks can be created from anywhere instead of
+ * in onCreate. Non-UI tasks use low-priority threads separate from the UI task threads so as not to
+ * compete with more critical UI tasks.
+ *
+ * <pre><code>
+ *
+ * public class MyManager {
+ *
+ * private final DialerExecutor&lt;MyInputType&gt; myExecutor;
+ *
+ * public void init() {
+ * // Don't use non-static or anonymous inner classes for worker!
+ * myExecutor = DialerExecutors.createNonUiTaskBuilder(worker)
+ * .onSuccess(this::onSuccess) // Lambdas, anonymous, or non-static inner classes all fine
+ * .onFailure(this::onFailure) // Lambdas, anonymous, or non-static inner classes all fine
+ * .build();
+ * );
+ * }
+ *
+ * private static class MyWorker implements Worker&lt;MyInputType, MyOutputType&gt; {
+ * MyOutputType doInBackground(MyInputType input) { ... }
+ * }
+ * private void onSuccess(MyOutputType output) { ... }
+ * private void onFailure(Throwable throwable) { ... }
+ *
+ * private void userDidSomething() { myExecutor.executeParallel(input); }
+ * }
+ * </code></pre>
+ *
+ * Note that non-UI tasks are intended to be relatively quick; for example reading/writing shared
+ * preferences or doing simple database work. If you submit long running non-UI tasks you may
+ * saturate the shared application threads and block other tasks. Also, this class does not create
+ * any wakelocks, so a long running task could be killed if the device goes to sleep while your task
+ * is still running. If you have to do long running or periodic work, consider using a job
+ * scheduler.
+ */
+public final class DialerExecutors {
+
+ /** @see DialerExecutorFactory#createUiTaskBuilder(FragmentManager, String, Worker) */
+ @NonNull
+ public static <InputT, OutputT> DialerExecutor.Builder<InputT, OutputT> createUiTaskBuilder(
+ @NonNull FragmentManager fragmentManager,
+ @NonNull String taskId,
+ @NonNull Worker<InputT, OutputT> worker) {
+ return new DefaultDialerExecutorFactory()
+ .createUiTaskBuilder(
+ Assert.isNotNull(fragmentManager), Assert.isNotNull(taskId), Assert.isNotNull(worker));
+ }
+
+ /** @see DialerExecutorFactory#createNonUiTaskBuilder(Worker) */
+ @NonNull
+ public static <InputT, OutputT> DialerExecutor.Builder<InputT, OutputT> createNonUiTaskBuilder(
+ @NonNull Worker<InputT, OutputT> worker) {
+ return new DefaultDialerExecutorFactory().createNonUiTaskBuilder(Assert.isNotNull(worker));
+ }
+}
diff --git a/java/com/android/dialer/common/concurrent/DialerAsyncTaskHelper.java b/java/com/android/dialer/common/concurrent/DialerUiTaskFragment.java
index 65b408094..627336895 100644
--- a/java/com/android/dialer/common/concurrent/DialerAsyncTaskHelper.java
+++ b/java/com/android/dialer/common/concurrent/DialerUiTaskFragment.java
@@ -21,89 +21,33 @@ import android.app.FragmentManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.MainThread;
-import android.support.annotation.WorkerThread;
+import android.support.annotation.Nullable;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.AsyncTaskExecutors.SimpleAsyncTaskExecutor;
+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.ExecutorService;
/**
- * Helps use async task properly.
- *
- * <p>This provides a number of benefits over using AsyncTask directly:
- *
- * <ul>
- * <li>Ensures that tasks keep running across configuration changes by using a headless fragment.
- * <li>Propagates exceptions to users, who must implement both onSuccess and onFailure methods.
- * <li>Checks for dead UI components which can be encountered if a task runs longer than its UI.
- * If a dead UI component is encountered, onSuccess/onFailure are not called (because they
- * can't be) but a message is logged.
- * <li>Helps prevents memory leaks by ensuring that callbacks are nulled out when the headless
- * fragment is detached.
- * </ul>
- *
- * <p>In your activity or fragment:
- *
- * <pre><code>
- *
- * public class MyActivity extends Activity {
- *
- * private final DialerAsyncTaskHelper&lt;MyInputType, MyOutputType&gt; myTaskHelper;
- *
- * public void onCreate(Bundle state) {
- * super.onCreate(bundle);
- *
- * // Must be called in onCreate
- * myTaskHelper = DialerAsyncTaskHelper.create(
- * fragmentManager,
- * taskId,
- * new MyWorker(), # Don't use non-static or anonymous inner classes!
- * this::onSuccess, # Lambdas, anonymous, or non-static inner classes all fine
- * this::onFailure # Lambdas, anonymous, or non-static inner classes all fine
- * );
- * }
- *
- * private static class MyWorker implements Worker&lt;MyInputType, MyOutputType&gt; {
- * MyOutputType doInBackground(MyInputType input) { ... }
- * }
- * private void onSuccess(MyOutputType output) { ... }
- * private void onFailure(Throwable throwable) { ... }
- *
- * private void userDidSomething() { myTaskHelper.execute(executor, input); }
- * }
- * </code></pre>
+ * Do not use this class directly. Instead use {@link DialerExecutors}.
*
* @param <InputT> the type of the object sent to the task upon execution
* @param <OutputT> the type of the result of the background computation
*/
-public final class DialerAsyncTaskHelper<InputT, OutputT> extends Fragment {
+public final class DialerUiTaskFragment<InputT, OutputT> extends Fragment {
private String taskId;
private Worker<InputT, OutputT> worker;
private SuccessListener<OutputT> successListener;
private FailureListener failureListener;
- private final AsyncTaskExecutor serialExecutor = AsyncTaskExecutors.createAsyncTaskExecutor();
- private final AsyncTaskExecutor parallelExecutor = AsyncTaskExecutors.createThreadPoolExecutor();
-
- /** Functional interface for doing work in the background. */
- public interface Worker<InputT, OutputT> {
- @WorkerThread
- OutputT doInBackground(InputT input);
- }
-
- /** Functional interface for handling the result of background work. */
- public interface SuccessListener<OutputT> {
- @MainThread
- void onSuccess(OutputT output);
- }
-
- /** Functional interface for handling an error produced while performing background work. */
- public interface FailureListener {
- @MainThread
- void onFailure(Throwable throwable);
- }
+ private AsyncTaskExecutor serialExecutor = AsyncTaskExecutors.createAsyncTaskExecutor();
+ private AsyncTaskExecutor parallelExecutor = AsyncTaskExecutors.createThreadPoolExecutor();
/**
- * Creates a new {@link DialerAsyncTaskHelper} or gets an existing one in the event that a
+ * Creates a new {@link DialerUiTaskFragment} or gets an existing one in the event that a
* configuration change occurred while the previous activity's task was still running. Must be
* called from onCreate of your activity or fragment.
*
@@ -123,30 +67,38 @@ public final class DialerAsyncTaskHelper<InputT, OutputT> extends Fragment {
* fragment are all fine.
* @param <InputT> the type of the object sent to the task upon execution
* @param <OutputT> the type of the result of the background computation
- * @return a {@link DialerAsyncTaskHelper} which may be used to call the "execute*" methods
+ * @return a {@link DialerUiTaskFragment} which may be used to call the "execute*" methods
*/
@MainThread
- public static <InputT, OutputT> DialerAsyncTaskHelper<InputT, OutputT> create(
+ static <InputT, OutputT> DialerUiTaskFragment<InputT, OutputT> create(
FragmentManager fragmentManager,
String taskId,
Worker<InputT, OutputT> worker,
SuccessListener<OutputT> successListener,
- FailureListener failureListener) {
+ FailureListener failureListener,
+ @Nullable ExecutorService serialExecutorService,
+ @Nullable ExecutorService parallelExecutorService) {
Assert.isMainThread();
- DialerAsyncTaskHelper<InputT, OutputT> helperFragment =
- (DialerAsyncTaskHelper<InputT, OutputT>) fragmentManager.findFragmentByTag(taskId);
+ DialerUiTaskFragment<InputT, OutputT> fragment =
+ (DialerUiTaskFragment<InputT, OutputT>) fragmentManager.findFragmentByTag(taskId);
- if (helperFragment == null) {
- LogUtil.i("DialerAsyncTaskHelper.create", "creating new helper fragment");
- helperFragment = new DialerAsyncTaskHelper<>();
- fragmentManager.beginTransaction().add(helperFragment, taskId).commit();
+ if (fragment == null) {
+ LogUtil.i("DialerUiTaskFragment.create", "creating new DialerUiTaskFragment");
+ fragment = new DialerUiTaskFragment<>();
+ fragmentManager.beginTransaction().add(fragment, taskId).commit();
+ }
+ fragment.taskId = taskId;
+ fragment.worker = worker;
+ fragment.successListener = successListener;
+ fragment.failureListener = failureListener;
+ if (serialExecutorService != null) {
+ fragment.serialExecutor = new SimpleAsyncTaskExecutor(serialExecutorService);
}
- helperFragment.taskId = taskId;
- helperFragment.worker = worker;
- helperFragment.successListener = successListener;
- helperFragment.failureListener = failureListener;
- return helperFragment;
+ if (parallelExecutorService != null) {
+ fragment.parallelExecutor = new SimpleAsyncTaskExecutor(parallelExecutorService);
+ }
+ return fragment;
}
@Override
@@ -158,40 +110,22 @@ public final class DialerAsyncTaskHelper<InputT, OutputT> extends Fragment {
@Override
public void onDetach() {
super.onDetach();
- LogUtil.enterBlock("DialerAsyncTaskHelper.onDetach");
+ LogUtil.enterBlock("DialerUiTaskFragment.onDetach");
taskId = null;
successListener = null;
failureListener = null;
}
- /**
- * Executes the task on a single thread global to the process. Multiple calls using this method
- * will result in tasks being executed serially.
- */
- @MainThread
- public void executeSerial(InputT input) {
- Assert.isMainThread();
+ void executeSerial(InputT input) {
serialExecutor.submit(taskId, new InternalTask(), input);
}
- /**
- * Executes the task on a thread pool shared across the application. Multiple calls using this
- * method may result in tasks being executed in parallel.
- */
- @MainThread
- public void executeParallel(InputT input) {
- Assert.isMainThread();
+ void executeParallel(InputT input) {
parallelExecutor.submit(taskId, new InternalTask(), input);
}
- /**
- * Executes the task on a custom executor. This should rarely be used; instead prefer {@link
- * #executeSerial(Object)} or {@link #executeParallel(Object)}.
- */
- @MainThread
- public void executeOnCustomExecutor(AsyncTaskExecutor executor, InputT input) {
- Assert.isMainThread();
- executor.submit(taskId, new InternalTask(), input);
+ void executeOnCustomExecutor(ExecutorService executor, InputT input) {
+ new SimpleAsyncTaskExecutor(executor).submit(taskId, new InternalTask(), input);
}
private final class InternalTask extends AsyncTask<InputT, Void, InternalTaskResult<OutputT>> {
diff --git a/java/com/android/dialer/common/concurrent/ThreadUtil.java b/java/com/android/dialer/common/concurrent/ThreadUtil.java
new file mode 100644
index 000000000..21cf4634e
--- /dev/null
+++ b/java/com/android/dialer/common/concurrent/ThreadUtil.java
@@ -0,0 +1,38 @@
+/*
+ * 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.os.Handler;
+import android.os.Looper;
+
+/** Application-wide utility methods for working with threads. */
+public class ThreadUtil {
+ private static volatile Handler mainThreadHandler;
+
+ /** Posts a runnable to the UI thread. */
+ public static void postOnUiThread(Runnable runnable) {
+ getUiThreadHandler().post(runnable);
+ }
+
+ /** Gets a handler which uses the main looper. */
+ public static Handler getUiThreadHandler() {
+ if (mainThreadHandler == null) {
+ mainThreadHandler = new Handler(Looper.getMainLooper());
+ }
+ return mainThreadHandler;
+ }
+}