1package com.xtremelabs.robolectric.shadows; 2 3import android.os.AsyncTask; 4import android.os.ShadowAsyncTaskBridge; 5import com.xtremelabs.robolectric.Robolectric; 6import com.xtremelabs.robolectric.internal.Implementation; 7import com.xtremelabs.robolectric.internal.Implements; 8import com.xtremelabs.robolectric.internal.RealObject; 9 10import java.util.concurrent.Callable; 11import java.util.concurrent.CancellationException; 12import java.util.concurrent.ExecutionException; 13import java.util.concurrent.FutureTask; 14import java.util.concurrent.TimeUnit; 15import java.util.concurrent.TimeoutException; 16 17@Implements(AsyncTask.class) 18public class ShadowAsyncTask<Params, Progress, Result> { 19 20 @RealObject private AsyncTask<Params, Progress, Result> realAsyncTask; 21 22 private final FutureTask<Result> future; 23 private final BackgroundWorker worker; 24 private AsyncTask.Status status = AsyncTask.Status.PENDING; 25 26 public ShadowAsyncTask() { 27 worker = new BackgroundWorker(); 28 future = new FutureTask<Result>(worker) { 29 @Override 30 protected void done() { 31 status = AsyncTask.Status.FINISHED; 32 try { 33 final Result result = get(); 34 Robolectric.getUiThreadScheduler().post(new Runnable() { 35 @Override public void run() { 36 getBridge().onPostExecute(result); 37 } 38 }); 39 } catch (CancellationException e) { 40 Robolectric.getUiThreadScheduler().post(new Runnable() { 41 @Override public void run() { 42 getBridge().onCancelled(); 43 } 44 }); 45 } catch (InterruptedException e) { 46 // Ignore. 47 } catch (Throwable t) { 48 throw new RuntimeException("An error occured while executing doInBackground()", 49 t); 50 } 51 } 52 }; 53 } 54 55 @Implementation 56 public boolean isCancelled() { 57 return future.isCancelled(); 58 } 59 60 @Implementation 61 public boolean cancel(boolean mayInterruptIfRunning) { 62 return future.cancel(mayInterruptIfRunning); 63 } 64 65 @Implementation 66 public Result get() throws InterruptedException, ExecutionException { 67 return future.get(); 68 } 69 70 @Implementation 71 public Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 72 return future.get(timeout, unit); 73 } 74 75 @Implementation 76 public AsyncTask<Params, Progress, Result> execute(final Params... params) { 77 status = AsyncTask.Status.RUNNING; 78 getBridge().onPreExecute(); 79 80 worker.params = params; 81 82 Robolectric.getBackgroundScheduler().post(new Runnable() { 83 @Override public void run() { 84 future.run(); 85 } 86 }); 87 88 return realAsyncTask; 89 } 90 91 @Implementation 92 public AsyncTask.Status getStatus() { 93 return status; 94 } 95 96 /** 97 * Enqueue a call to {@link AsyncTask#onProgressUpdate(Object[])} on UI looper (or run it immediately 98 * if the looper it is not paused). 99 * 100 * @param values The progress values to update the UI with. 101 * @see AsyncTask#publishProgress(Object[]) 102 */ 103 @Implementation 104 public void publishProgress(final Progress... values) { 105 Robolectric.getUiThreadScheduler().post(new Runnable() { 106 @Override public void run() { 107 getBridge().onProgressUpdate(values); 108 } 109 }); 110 } 111 112 private ShadowAsyncTaskBridge<Params, Progress, Result> getBridge() { 113 return new ShadowAsyncTaskBridge<Params, Progress, Result>(realAsyncTask); 114 } 115 116 private final class BackgroundWorker implements Callable<Result> { 117 Params[] params; 118 @Override 119 public Result call() throws Exception { 120 return getBridge().doInBackground(params); 121 } 122 } 123} 124