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