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