Home | History | Annotate | Download | only in shadows
      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 
     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
     63   public boolean isCancelled() {
     64     return future.isCancelled();
     65   }
     66 
     67   @Implementation
     68   public boolean cancel(boolean mayInterruptIfRunning) {
     69     return future.cancel(mayInterruptIfRunning);
     70   }
     71 
     72   @Implementation
     73   public Result get() throws InterruptedException, ExecutionException {
     74     return future.get();
     75   }
     76 
     77   @Implementation
     78   public Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
     79     return future.get(timeout, unit);
     80   }
     81 
     82   @Implementation
     83   public AsyncTask<Params, Progress, Result> execute(final Params... params) {
     84     status = AsyncTask.Status.RUNNING;
     85     getBridge().onPreExecute();
     86 
     87     worker.params = params;
     88 
     89     ShadowApplication.getInstance().getBackgroundThreadScheduler().post(new Runnable() {
     90       @Override
     91       public void run() {
     92         future.run();
     93       }
     94     });
     95 
     96     return realAsyncTask;
     97   }
     98 
     99   @Implementation
    100   public AsyncTask<Params, Progress, Result> executeOnExecutor(Executor executor, Params... params) {
    101     status = AsyncTask.Status.RUNNING;
    102     getBridge().onPreExecute();
    103 
    104     worker.params = params;
    105     executor.execute(new Runnable() {
    106       @Override
    107       public void run() {
    108         future.run();
    109       }
    110     });
    111 
    112     return realAsyncTask;
    113   }
    114 
    115   @Implementation
    116   public AsyncTask.Status getStatus() {
    117     return status;
    118   }
    119 
    120   /**
    121    * Enqueue a call to {@link AsyncTask#onProgressUpdate(Object[])} on UI looper (or run it immediately
    122    * if the looper it is not paused).
    123    *
    124    * @param values The progress values to update the UI with.
    125    * @see AsyncTask#publishProgress(Object[])
    126    */
    127   @Implementation
    128   public void publishProgress(final Progress... values) {
    129     ShadowApplication.getInstance().getForegroundThreadScheduler().post(new Runnable() {
    130       @Override
    131       public void run() {
    132         getBridge().onProgressUpdate(values);
    133       }
    134     });
    135   }
    136 
    137   private ShadowAsyncTaskBridge<Params, Progress, Result> getBridge() {
    138     return new ShadowAsyncTaskBridge<>(realAsyncTask);
    139   }
    140 
    141   private final class BackgroundWorker implements Callable<Result> {
    142     Params[] params;
    143 
    144     @Override
    145     public Result call() throws Exception {
    146       return getBridge().doInBackground(params);
    147     }
    148   }
    149 
    150   private static class OnPostExecuteException extends Exception {
    151     public OnPostExecuteException(Throwable throwable) {
    152       super(throwable);
    153     }
    154   }
    155 }
    156