• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.bumptech.glide.request;
2 
3 import android.graphics.drawable.Drawable;
4 import android.os.Handler;
5 
6 import com.bumptech.glide.request.animation.GlideAnimation;
7 import com.bumptech.glide.request.target.SizeReadyCallback;
8 import com.bumptech.glide.util.Util;
9 
10 import java.util.concurrent.CancellationException;
11 import java.util.concurrent.ExecutionException;
12 import java.util.concurrent.TimeUnit;
13 import java.util.concurrent.TimeoutException;
14 
15 /**
16  * A {@link java.util.concurrent.Future} implementation for Glide that can be used to load resources in a blocking
17  * manner on background threads.
18  *
19  * <p>
20  *     Note - Unlike most targets, RequestFutureTargets can be used once and only once. Attempting to reuse a
21  *     RequestFutureTarget will probably result in undesirable behavior or exceptions. Instead of reusing
22  *     objects of this class, the pattern should be:
23  *
24  *     <pre>
25  *     {@code
26  *      RequestFutureTarget target = Glide.load("")...
27  *     Object resource = target.get();
28  *     // Do something with resource, and when finished:
29  *     Glide.clear(target);
30  *     }
31  *     </pre>
32  *     The {@link com.bumptech.glide.Glide#clear(FutureTarget)} call will make sure any resources used are recycled.
33  * </p>
34  *
35  * @param <T> The type of the data to load.
36  * @param <R> The type of the resource that will be loaded.
37  */
38 public class RequestFutureTarget<T, R> implements FutureTarget<R>, Runnable {
39     private static final Waiter DEFAULT_WAITER = new Waiter();
40 
41     private final Handler mainHandler;
42     private final int width;
43     private final int height;
44     // Exists for testing only.
45     private final boolean assertBackgroundThread;
46     private final Waiter waiter;
47 
48     private R resource;
49     private Request request;
50     private boolean isCancelled;
51     private Exception exception;
52     private boolean resultReceived;
53     private boolean exceptionReceived;
54 
55     /**
56      * Constructor for a RequestFutureTarget. Should not be used directly.
57      */
RequestFutureTarget(Handler mainHandler, int width, int height)58     public RequestFutureTarget(Handler mainHandler, int width, int height) {
59         this(mainHandler, width, height, true, DEFAULT_WAITER);
60     }
61 
RequestFutureTarget(Handler mainHandler, int width, int height, boolean assertBackgroundThread, Waiter waiter)62     RequestFutureTarget(Handler mainHandler, int width, int height, boolean assertBackgroundThread, Waiter waiter) {
63         this.mainHandler = mainHandler;
64         this.width = width;
65         this.height = height;
66         this.assertBackgroundThread = assertBackgroundThread;
67         this.waiter = waiter;
68     }
69 
70     /**
71      * {@inheritDoc}
72      */
73     @Override
cancel(boolean b)74     public synchronized boolean cancel(boolean b) {
75         if (isCancelled) {
76             return true;
77         }
78 
79         final boolean result = !isDone();
80         if (result) {
81             isCancelled = true;
82             clear();
83             waiter.notifyAll(this);
84         }
85         return result;
86     }
87 
88     /**
89      * {@inheritDoc}
90      */
91     @Override
isCancelled()92     public synchronized boolean isCancelled() {
93         return isCancelled;
94     }
95 
96     /**
97      * {@inheritDoc}
98      */
99     @Override
isDone()100     public synchronized boolean isDone() {
101         return isCancelled || resultReceived;
102     }
103 
104     /**
105      * {@inheritDoc}
106      */
107     @Override
get()108     public R get() throws InterruptedException, ExecutionException {
109         try {
110             return doGet(null);
111         } catch (TimeoutException e) {
112             throw new AssertionError(e);
113         }
114     }
115 
116     /**
117      * {@inheritDoc}
118      */
119     @Override
get(long time, TimeUnit timeUnit)120     public R get(long time, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
121         return doGet(timeUnit.toMillis(time));
122     }
123 
124     /**
125      * A callback that should never be invoked directly.
126      */
127     @Override
getSize(SizeReadyCallback cb)128     public void getSize(SizeReadyCallback cb) {
129         cb.onSizeReady(width, height);
130     }
131 
132     /**
133      * {@inheritDoc}
134      */
135     @Override
setRequest(Request request)136     public void setRequest(Request request) {
137         this.request = request;
138     }
139 
140     /**
141      * {@inheritDoc}
142      */
143     @Override
getRequest()144     public Request getRequest() {
145         return request;
146     }
147 
148     /**
149      * A callback that should never be invoked directly.
150      */
151     @Override
onLoadCleared(Drawable placeholder)152     public void onLoadCleared(Drawable placeholder) {
153         // Do nothing.
154     }
155 
156     /**
157      * A callback that should never be invoked directly.
158      */
159     @Override
onLoadStarted(Drawable placeholder)160     public void onLoadStarted(Drawable placeholder) {
161         // Do nothing.
162     }
163 
164     /**
165      * A callback that should never be invoked directly.
166      */
167     @Override
onLoadFailed(Exception e, Drawable errorDrawable)168     public synchronized void onLoadFailed(Exception e, Drawable errorDrawable) {
169          // We might get a null exception.
170         exceptionReceived = true;
171         this.exception = e;
172         waiter.notifyAll(this);
173     }
174 
175     /**
176      * A callback that should never be invoked directly.
177      */
178     @Override
onResourceReady(R resource, GlideAnimation<? super R> glideAnimation)179     public synchronized void onResourceReady(R resource, GlideAnimation<? super R> glideAnimation) {
180         // We might get a null result.
181         resultReceived = true;
182         this.resource = resource;
183         waiter.notifyAll(this);
184     }
185 
doGet(Long timeoutMillis)186     private synchronized R doGet(Long timeoutMillis) throws ExecutionException, InterruptedException, TimeoutException {
187         if (assertBackgroundThread) {
188             Util.assertBackgroundThread();
189         }
190 
191         if (isCancelled) {
192             throw new CancellationException();
193         } else if (exceptionReceived) {
194             throw new ExecutionException(exception);
195         } else if (resultReceived) {
196             return resource;
197         }
198 
199         if (timeoutMillis == null) {
200             waiter.waitForTimeout(this, 0);
201         } else if (timeoutMillis > 0) {
202             waiter.waitForTimeout(this, timeoutMillis);
203         }
204 
205         if (Thread.interrupted()) {
206             throw new InterruptedException();
207         } else if (exceptionReceived) {
208             throw new ExecutionException(exception);
209         } else if (isCancelled) {
210             throw new CancellationException();
211         } else if (!resultReceived) {
212             throw new TimeoutException();
213         }
214 
215         return resource;
216     }
217 
218     /**
219      * A callback that should never be invoked directly.
220      */
221     @Override
run()222     public void run() {
223         request.clear();
224     }
225 
226     /**
227      * Can be safely called from either the main thread or a background thread to cleanup the resources used by this
228      * target.
229      */
230     @Override
clear()231     public void clear() {
232         mainHandler.post(this);
233     }
234 
235     /**
236      * {@inheritDoc}
237      */
238     @Override
onStart()239     public void onStart() {
240         // Do nothing.
241     }
242 
243     /**
244      * {@inheritDoc}
245      */
246     @Override
onStop()247     public void onStop() {
248         // Do nothing.
249     }
250 
251     /**
252      * {@inheritDoc}
253      */
254     @Override
onDestroy()255     public void onDestroy() {
256         // Do nothing.
257     }
258 
259     // Visible for testing.
260     static class Waiter {
261 
waitForTimeout(Object toWaitOn, long timeoutMillis)262         public void waitForTimeout(Object toWaitOn, long timeoutMillis) throws InterruptedException {
263             toWaitOn.wait(timeoutMillis);
264         }
265 
notifyAll(Object toNotify)266         public void notifyAll(Object toNotify) {
267             toNotify.notifyAll();
268         }
269     }
270 }
271