Home | History | Annotate | Download | only in request
      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      */
     58     public RequestFutureTarget(Handler mainHandler, int width, int height) {
     59         this(mainHandler, width, height, true, DEFAULT_WAITER);
     60     }
     61 
     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
     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
     92     public synchronized boolean isCancelled() {
     93         return isCancelled;
     94     }
     95 
     96     /**
     97      * {@inheritDoc}
     98      */
     99     @Override
    100     public synchronized boolean isDone() {
    101         return isCancelled || resultReceived;
    102     }
    103 
    104     /**
    105      * {@inheritDoc}
    106      */
    107     @Override
    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
    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
    128     public void getSize(SizeReadyCallback cb) {
    129         cb.onSizeReady(width, height);
    130     }
    131 
    132     /**
    133      * {@inheritDoc}
    134      */
    135     @Override
    136     public void setRequest(Request request) {
    137         this.request = request;
    138     }
    139 
    140     /**
    141      * {@inheritDoc}
    142      */
    143     @Override
    144     public Request getRequest() {
    145         return request;
    146     }
    147 
    148     /**
    149      * A callback that should never be invoked directly.
    150      */
    151     @Override
    152     public void onLoadCleared(Drawable placeholder) {
    153         // Do nothing.
    154     }
    155 
    156     /**
    157      * A callback that should never be invoked directly.
    158      */
    159     @Override
    160     public void onLoadStarted(Drawable placeholder) {
    161         // Do nothing.
    162     }
    163 
    164     /**
    165      * A callback that should never be invoked directly.
    166      */
    167     @Override
    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
    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 
    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
    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
    231     public void clear() {
    232         mainHandler.post(this);
    233     }
    234 
    235     /**
    236      * {@inheritDoc}
    237      */
    238     @Override
    239     public void onStart() {
    240         // Do nothing.
    241     }
    242 
    243     /**
    244      * {@inheritDoc}
    245      */
    246     @Override
    247     public void onStop() {
    248         // Do nothing.
    249     }
    250 
    251     /**
    252      * {@inheritDoc}
    253      */
    254     @Override
    255     public void onDestroy() {
    256         // Do nothing.
    257     }
    258 
    259     // Visible for testing.
    260     static class Waiter {
    261 
    262         public void waitForTimeout(Object toWaitOn, long timeoutMillis) throws InterruptedException {
    263             toWaitOn.wait(timeoutMillis);
    264         }
    265 
    266         public void notifyAll(Object toNotify) {
    267             toNotify.notifyAll();
    268         }
    269     }
    270 }
    271