/ItemID/src/com/ideal/itemid/UserTask.java

http://eyes-free.googlecode.com/ · Java · 480 lines · 168 code · 45 blank · 267 comment · 4 complexity · 9c00a54bdfea1fd6c3d6d2dded7ce4d5 MD5 · raw file

  1. /*
  2. * Copyright (C) 2008 Google Inc.
  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. package com.ideal.itemid;
  17. import java.util.concurrent.BlockingQueue;
  18. import java.util.concurrent.Callable;
  19. import java.util.concurrent.CancellationException;
  20. import java.util.concurrent.ExecutionException;
  21. import java.util.concurrent.FutureTask;
  22. import java.util.concurrent.LinkedBlockingQueue;
  23. import java.util.concurrent.ThreadFactory;
  24. import java.util.concurrent.ThreadPoolExecutor;
  25. import java.util.concurrent.TimeUnit;
  26. import java.util.concurrent.TimeoutException;
  27. import java.util.concurrent.atomic.AtomicInteger;
  28. import android.os.Handler;
  29. import android.os.Message;
  30. import android.os.Process;
  31. /**
  32. * <p>
  33. * UserTask enables proper and easy use of the UI thread. This class allows to
  34. * perform background operations and publish results on the UI thread without
  35. * having to manipulate threads and/or handlers.
  36. * </p>
  37. * <p>
  38. * A user task is defined by a computation that runs on a background thread and
  39. * whose result is published on the UI thread. A user task is defined by 3
  40. * generic types, called <code>Params</code>, <code>Progress</code> and
  41. * <code>Result</code>, and 4 steps, called <code>begin</code>,
  42. * <code>doInBackground</code>, <code>processProgress<code> and <code>end</code>
  43. * .
  44. * </p>
  45. * <h2>Usage</h2>
  46. * <p>
  47. * UserTask must be subclassed to be used. The subclass will override at least
  48. * one method ({@link #doInBackground(Object[])}), and most often will override
  49. * a second one ({@link #onPostExecute(Object)}.)
  50. * </p>
  51. * <p>
  52. * Here is an example of subclassing:
  53. * </p>
  54. *
  55. * <pre>
  56. * private class DownloadFilesTask extends UserTask&lt;URL, Integer, Long&gt; {
  57. * public File doInBackground(URL... urls) {
  58. * int count = urls.length;
  59. * long totalSize = 0;
  60. * for (int i = 0; i &lt; count; i++) {
  61. * totalSize += Downloader.downloadFile(urls[i]);
  62. * publishProgress((int) ((i / (float) count) * 100));
  63. * }
  64. * }
  65. *
  66. * public void onProgressUpdate(Integer... progress) {
  67. * setProgressPercent(progress[0]);
  68. * }
  69. *
  70. * public void onPostExecute(Long result) {
  71. * showDialog(&quot;Downloaded &quot; + result + &quot; bytes&quot;);
  72. * }
  73. * }
  74. * </pre>
  75. * <p>
  76. * Once created, a task is executed very simply:
  77. * </p>
  78. *
  79. * <pre>
  80. * new DownloadFilesTask().execute(new URL[] { ... });
  81. * </pre>
  82. *
  83. * <h2>User task's generic types</h2>
  84. * <p>
  85. * The three types used by a user task are the following:
  86. * </p>
  87. * <ol>
  88. * <li><code>Params</code>, the type of the parameters sent to the task upon
  89. * execution.</li>
  90. * <li><code>Progress</code>, the type of the progress units published during
  91. * the background computation.</li>
  92. * <li><code>Result</code>, the type of the result of the background
  93. * computation.</li>
  94. * </ol>
  95. * <p>
  96. * Not all types are always used by a user task. To mark a type as unused,
  97. * simply use the type {@link Void}:
  98. * </p>
  99. *
  100. * <pre>
  101. * private class MyTask extends UserTask&lt;Void, Void, Void) { ... }
  102. * </pre>
  103. *
  104. * <h2>The 4 steps</h2>
  105. * <p>
  106. * When a user task is executed, the task goes through 4 steps:
  107. * </p>
  108. * <ol>
  109. * <li>{@link #onPreExecute()}, invoked on the UI thread immediately after the
  110. * task is executed. This step is normally used to setup the task, for instance
  111. * by showing a progress bar in the user interface.</li>
  112. * <li>{@link #doInBackground(Object[])}, invoked on the background thread
  113. * immediately after {@link # onPreExecute ()} finishes executing. This step is
  114. * used to perform background computation that can take a long time. The
  115. * parameters of the user task are passed to this step. The result of the
  116. * computation must be returned by this step and will be passed back to the last
  117. * step. This step can also use {@link #publishProgress(Object[])} to publish
  118. * one or more units of progress. These values are published on the UI thread,
  119. * in the {@link #onProgressUpdate(Object[])} step.</li>
  120. * <li>{@link # onProgressUpdate (Object[])}, invoked on the UI thread after a
  121. * call to {@link #publishProgress(Object[])}. The timing of the execution is
  122. * undefined. This method is used to display any form of progress in the user
  123. * interface while the background computation is still executing. For instance,
  124. * it can be used to animate a progress bar or show logs in a text field.</li>
  125. * <li>{@link # onPostExecute (Object)}, invoked on the UI thread after the
  126. * background computation finishes. The result of the background computation is
  127. * passed to this step as a parameter.</li>
  128. * </ol>
  129. * <h2>Threading rules</h2>
  130. * <p>
  131. * There are a few threading rules that must be followed for this class to work
  132. * properly:
  133. * </p>
  134. * <ul>
  135. * <li>The task instance must be created on the UI thread.</li>
  136. * <li>{@link #execute(Object[])} must be invoked on the UI thread.</li>
  137. * <li>Do not call {@link # onPreExecute ()}, {@link # onPostExecute (Object)},
  138. * {@link #doInBackground(Object[])}, {@link # onProgressUpdate (Object[])}
  139. * manually.</li>
  140. * <li>The task can be executed only once (an exception will be thrown if a
  141. * second execution is attempted.)</li>
  142. * </ul>
  143. */
  144. public abstract class UserTask<Params, Progress, Result> {
  145. private static final String LOG_TAG = "UserTask";
  146. // TODO: get a better understanding of these values and how to tweak them
  147. // for optimal performance...
  148. private static final int CORE_POOL_SIZE = 6;
  149. private static final int MAXIMUM_POOL_SIZE = 10;
  150. private static final int KEEP_ALIVE = 10;
  151. private static final BlockingQueue<Runnable> sWorkQueue = new LinkedBlockingQueue<Runnable>(
  152. MAXIMUM_POOL_SIZE);
  153. private static final ThreadFactory sThreadFactory = new ThreadFactory() {
  154. private final AtomicInteger mCount = new AtomicInteger(1);
  155. public Thread newThread(Runnable r) {
  156. return new Thread(r, "UserTask #" + mCount.getAndIncrement());
  157. }
  158. };
  159. private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
  160. MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
  161. private static final int MESSAGE_POST_RESULT = 0x1;
  162. private static final int MESSAGE_POST_PROGRESS = 0x2;
  163. private static final int MESSAGE_POST_CANCEL = 0x3;
  164. private static final InternalHandler sHandler = new InternalHandler();
  165. private final WorkerRunnable<Params, Result> mWorker;
  166. private final FutureTask<Result> mFuture;
  167. private volatile Status mStatus = Status.PENDING;
  168. /**
  169. * Indicates the current status of the task. Each status will be set only
  170. * once during the lifetime of a task.
  171. */
  172. public enum Status {
  173. /**
  174. * Indicates that the task has not been executed yet.
  175. */
  176. PENDING,
  177. /**
  178. * Indicates that the task is running.
  179. */
  180. RUNNING,
  181. /**
  182. * Indicates that {@link UserTask#onPostExecute(Object)} has finished.
  183. */
  184. FINISHED,
  185. }
  186. /**
  187. * Creates a new user task. This constructor must be invoked on the UI
  188. * thread.
  189. */
  190. public UserTask() {
  191. mWorker = new WorkerRunnable<Params, Result>() {
  192. public Result call() throws Exception {
  193. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  194. return doInBackground(mParams);
  195. }
  196. };
  197. mFuture = new FutureTask<Result>(mWorker) {
  198. @Override
  199. protected void done() {
  200. Message message;
  201. Result result = null;
  202. try {
  203. result = get();
  204. } catch (InterruptedException e) {
  205. android.util.Log.w(LOG_TAG, e);
  206. } catch (ExecutionException e) {
  207. throw new RuntimeException("An error occured while executing doInBackground()",
  208. e.getCause());
  209. } catch (CancellationException e) {
  210. message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
  211. new UserTaskResult<Result>(UserTask.this, (Result[]) null));
  212. message.sendToTarget();
  213. return;
  214. } catch (Throwable t) {
  215. throw new RuntimeException("An error occured while executing "
  216. + "doInBackground()", t);
  217. }
  218. message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new UserTaskResult<Result>(
  219. UserTask.this, result));
  220. message.sendToTarget();
  221. }
  222. };
  223. }
  224. /**
  225. * Returns the current status of this task.
  226. *
  227. * @return The current status.
  228. */
  229. public final Status getStatus() {
  230. return mStatus;
  231. }
  232. /**
  233. * Override this method to perform a computation on a background thread. The
  234. * specified parameters are the parameters passed to
  235. * {@link #execute(Object[])} by the caller of this task. This method can
  236. * call {@link #publishProgress(Object[])} to publish updates on the UI
  237. * thread.
  238. *
  239. * @param params The parameters of the task.
  240. * @return A result, defined by the subclass of this task.
  241. * @see #onPreExecute()
  242. * @see #onPostExecute(Object)
  243. * @see #publishProgress(Object[])
  244. */
  245. public abstract Result doInBackground(Params... params);
  246. /**
  247. * Runs on the UI thread before {@link #doInBackground(Object[])}.
  248. *
  249. * @see #onPostExecute(Object)
  250. * @see #doInBackground(Object[])
  251. */
  252. public void onPreExecute() {
  253. }
  254. /**
  255. * Runs on the UI thread after {@link #doInBackground(Object[])}. The
  256. * specified result is the value returned by
  257. * {@link #doInBackground(Object[])} or null if the task was cancelled or an
  258. * exception occured.
  259. *
  260. * @param result The result of the operation computed by
  261. * {@link #doInBackground(Object[])}.
  262. * @see #onPreExecute()
  263. * @see #doInBackground(Object[])
  264. */
  265. @SuppressWarnings( {
  266. "UnusedDeclaration"
  267. })
  268. public void onPostExecute(Result result) {
  269. }
  270. /**
  271. * Runs on the UI thread after {@link #publishProgress(Object[])} is
  272. * invoked. The specified values are the values passed to
  273. * {@link #publishProgress(Object[])}.
  274. *
  275. * @param values The values indicating progress.
  276. * @see #publishProgress(Object[])
  277. * @see #doInBackground(Object[])
  278. */
  279. @SuppressWarnings( {
  280. "UnusedDeclaration"
  281. })
  282. public void onProgressUpdate(Progress... values) {
  283. }
  284. /**
  285. * Runs on the UI thread after {@link #cancel(boolean)} is invoked.
  286. *
  287. * @see #cancel(boolean)
  288. * @see #isCancelled()
  289. */
  290. public void onCancelled() {
  291. }
  292. /**
  293. * Returns <tt>true</tt> if this task was cancelled before it completed
  294. * normally.
  295. *
  296. * @return <tt>true</tt> if task was cancelled before it completed
  297. * @see #cancel(boolean)
  298. */
  299. public final boolean isCancelled() {
  300. return mFuture.isCancelled();
  301. }
  302. /**
  303. * Attempts to cancel execution of this task. This attempt will fail if the
  304. * task has already completed, already been cancelled, or could not be
  305. * cancelled for some other reason. If successful, and this task has not
  306. * started when <tt>cancel</tt> is called, this task should never run. If
  307. * the task has already started, then the <tt>mayInterruptIfRunning</tt>
  308. * parameter determines whether the thread executing this task should be
  309. * interrupted in an attempt to stop the task.
  310. *
  311. * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
  312. * task should be interrupted; otherwise, in-progress tasks are
  313. * allowed to complete.
  314. * @return <tt>false</tt> if the task could not be cancelled, typically
  315. * because it has already completed normally; <tt>true</tt>
  316. * otherwise
  317. * @see #isCancelled()
  318. * @see #onCancelled()
  319. */
  320. public final boolean cancel(boolean mayInterruptIfRunning) {
  321. return mFuture.cancel(mayInterruptIfRunning);
  322. }
  323. /**
  324. * Waits if necessary for the computation to complete, and then retrieves
  325. * its result.
  326. *
  327. * @return The computed result.
  328. * @throws CancellationException If the computation was cancelled.
  329. * @throws ExecutionException If the computation threw an exception.
  330. * @throws InterruptedException If the current thread was interrupted while
  331. * waiting.
  332. */
  333. public final Result get() throws InterruptedException, ExecutionException {
  334. return mFuture.get();
  335. }
  336. /**
  337. * Waits if necessary for at most the given time for the computation to
  338. * complete, and then retrieves its result.
  339. *
  340. * @param timeout Time to wait before cancelling the operation.
  341. * @param unit The time unit for the timeout.
  342. * @return The computed result.
  343. * @throws CancellationException If the computation was cancelled.
  344. * @throws ExecutionException If the computation threw an exception.
  345. * @throws InterruptedException If the current thread was interrupted while
  346. * waiting.
  347. * @throws TimeoutException If the wait timed out.
  348. */
  349. public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
  350. ExecutionException, TimeoutException {
  351. return mFuture.get(timeout, unit);
  352. }
  353. /**
  354. * Executes the task with the specified parameters. The task returns itself
  355. * (this) so that the caller can keep a reference to it. This method must be
  356. * invoked on the UI thread.
  357. *
  358. * @param params The parameters of the task.
  359. * @return This instance of UserTask.
  360. * @throws IllegalStateException If {@link #getStatus()} returns either
  361. * {@link UserTask.Status#RUNNING} or
  362. * {@link UserTask.Status#FINISHED}.
  363. */
  364. public final UserTask<Params, Progress, Result> execute(Params... params) {
  365. if (mStatus != Status.PENDING) {
  366. switch (mStatus) {
  367. case RUNNING:
  368. throw new IllegalStateException("Cannot execute task:"
  369. + " the task is already running.");
  370. case FINISHED:
  371. throw new IllegalStateException("Cannot execute task:"
  372. + " the task has already been executed "
  373. + "(a task can be executed only once)");
  374. }
  375. }
  376. mStatus = Status.RUNNING;
  377. onPreExecute();
  378. mWorker.mParams = params;
  379. sExecutor.execute(mFuture);
  380. return this;
  381. }
  382. /**
  383. * This method can be invoked from {@link #doInBackground(Object[])} to
  384. * publish updates on the UI thread while the background computation is
  385. * still running. Each call to this method will trigger the execution of
  386. * {@link #onProgressUpdate(Object[])} on the UI thread.
  387. *
  388. * @param values The progress values to update the UI with.
  389. * @see # onProgressUpdate (Object[])
  390. * @see #doInBackground(Object[])
  391. */
  392. protected final void publishProgress(Progress... values) {
  393. sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new UserTaskResult<Progress>(this, values))
  394. .sendToTarget();
  395. }
  396. private void finish(Result result) {
  397. onPostExecute(result);
  398. mStatus = Status.FINISHED;
  399. }
  400. private static class InternalHandler extends Handler {
  401. @SuppressWarnings( {
  402. "unchecked", "RawUseOfParameterizedType"
  403. })
  404. @Override
  405. public void handleMessage(Message msg) {
  406. UserTaskResult result = (UserTaskResult) msg.obj;
  407. switch (msg.what) {
  408. case MESSAGE_POST_RESULT:
  409. // There is only one result
  410. result.mTask.finish(result.mData[0]);
  411. break;
  412. case MESSAGE_POST_PROGRESS:
  413. result.mTask.onProgressUpdate(result.mData);
  414. break;
  415. case MESSAGE_POST_CANCEL:
  416. result.mTask.onCancelled();
  417. break;
  418. }
  419. }
  420. }
  421. private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
  422. Params[] mParams;
  423. }
  424. @SuppressWarnings( {
  425. "RawUseOfParameterizedType"
  426. })
  427. private static class UserTaskResult<Data> {
  428. final UserTask mTask;
  429. final Data[] mData;
  430. UserTaskResult(UserTask task, Data... data) {
  431. mTask = task;
  432. mData = data;
  433. }
  434. }
  435. }