• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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