• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * @param <InputT> the type of the object sent to the task upon execution
37  * @param <OutputT> 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 <InputT> the type of the object sent to the task upon execution
69    * @param <OutputT> 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
create( FragmentManager fragmentManager, String taskId, Worker<InputT, OutputT> worker, SuccessListener<OutputT> successListener, FailureListener failureListener, @Nullable ExecutorService serialExecutorService, @Nullable ExecutorService parallelExecutorService)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
onCreate(Bundle savedInstanceState)105   public void onCreate(Bundle savedInstanceState) {
106     super.onCreate(savedInstanceState);
107     setRetainInstance(true);
108   }
109 
110   @Override
onDetach()111   public void onDetach() {
112     super.onDetach();
113     LogUtil.enterBlock("DialerUiTaskFragment.onDetach");
114     taskId = null;
115     successListener = null;
116     failureListener = null;
117   }
118 
executeSerial(InputT input)119   void executeSerial(InputT input) {
120     serialExecutor.submit(taskId, new InternalTask(), input);
121   }
122 
executeParallel(InputT input)123   void executeParallel(InputT input) {
124     parallelExecutor.submit(taskId, new InternalTask(), input);
125   }
126 
executeOnCustomExecutor(ExecutorService executor, InputT input)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
doInBackground(InputT... params)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
onPostExecute(InternalTaskResult<OutputT> result)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 
InternalTaskResult(Throwable throwable, OutputT result)165     InternalTaskResult(Throwable throwable, OutputT result) {
166       this.throwable = throwable;
167       this.result = result;
168     }
169   }
170 }
171