GenericRequest.java revision 78bad2aa32f824f9e098b5058dfa3506a7ed3f62
1package com.bumptech.glide.request;
2
3import android.content.Context;
4import android.graphics.drawable.Drawable;
5import android.util.Log;
6import com.bumptech.glide.Priority;
7import com.bumptech.glide.load.engine.Resource;
8import com.bumptech.glide.load.ResourceDecoder;
9import com.bumptech.glide.load.ResourceEncoder;
10import com.bumptech.glide.load.Transformation;
11import com.bumptech.glide.load.data.DataFetcher;
12import com.bumptech.glide.load.engine.Engine;
13import com.bumptech.glide.load.model.ModelLoader;
14import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
15import com.bumptech.glide.provider.LoadProvider;
16import com.bumptech.glide.request.target.Target;
17import com.bumptech.glide.util.LogTime;
18
19import java.io.InputStream;
20import java.util.ArrayDeque;
21import java.util.Queue;
22
23/**
24 * A {@link Request} that loads a {@link Resource} into a given {@link Target}.
25 *
26 * @param <A> The type of the model that the resource will be loaded from.
27 * @param <T> The type of the data that the resource will be loaded from.
28 * @param <Z> The type of the resource that will be loaded.
29 */
30public class GenericRequest<A, T, Z, R> implements Request, Target.SizeReadyCallback, ResourceCallback {
31    private static final String TAG = "GenericRequest";
32
33    private int placeholderResourceId;
34    private int errorResourceId;
35    private Context context;
36    private Transformation<Z> transformation;
37    private LoadProvider<A, T, Z, R> loadProvider;
38    private RequestCoordinator requestCoordinator;
39    private A model;
40    private Class<R> transcodeClass;
41    private boolean isMemoryCacheable;
42    private Priority priority;
43    private Target<R> target;
44    private RequestListener<A, R> requestListener;
45    private float sizeMultiplier;
46    private Engine engine;
47    private GlideAnimationFactory<R> animationFactory;
48    private int overrideWidth;
49    private int overrideHeight;
50    private String tag = String.valueOf(hashCode());
51
52    private Drawable placeholderDrawable;
53    private Drawable errorDrawable;
54    private boolean isCancelled;
55    private boolean isError;
56    private boolean loadedFromMemoryCache;
57    private Resource resource;
58    private Engine.LoadStatus loadStatus;
59    private boolean isRunning;
60    private long startTime;
61
62    private static final Queue<GenericRequest> queue = new ArrayDeque<GenericRequest>();
63
64    @SuppressWarnings("unchecked")
65    public static <A, T, Z, R> GenericRequest<A, T, Z, R> obtain(
66            LoadProvider<A, T, Z, R> loadProvider,
67            A model,
68            Context context,
69            Priority priority,
70            Target<R> target,
71            float sizeMultiplier,
72            Drawable placeholderDrawable,
73            int placeholderResourceId,
74            Drawable errorDrawable,
75            int errorResourceId,
76            RequestListener<A, R> requestListener,
77            RequestCoordinator requestCoordinator,
78            Engine engine,
79            Transformation<Z> transformation,
80            Class<R> transcodeClass,
81            boolean isMemoryCacheable,
82            GlideAnimationFactory<R> animationFactory,
83            int overrideWidth,
84            int overrideHeight) {
85        GenericRequest request = queue.poll();
86        if (request == null) {
87            request = new GenericRequest();
88        }
89        request.init(loadProvider,
90                model,
91                context,
92                priority,
93                target,
94                sizeMultiplier,
95                placeholderDrawable,
96                placeholderResourceId,
97                errorDrawable,
98                errorResourceId,
99                requestListener,
100                requestCoordinator,
101                engine,
102                transformation,
103                transcodeClass,
104                isMemoryCacheable,
105                animationFactory,
106                overrideWidth,
107                overrideHeight);
108        return request;
109    }
110
111    private GenericRequest() {
112
113    }
114
115    @Override
116    public void recycle() {
117        loadProvider = null;
118        model = null;
119        context = null;
120        target = null;
121        placeholderDrawable = null;
122        errorDrawable = null;
123        requestListener = null;
124        requestCoordinator = null;
125        engine = null;
126        transformation = null;
127        animationFactory = null;
128        isCancelled = false;
129        isError = false;
130        loadedFromMemoryCache = false;
131        loadStatus = null;
132        isRunning = false;
133        queue.offer(this);
134    }
135
136
137    private void init(
138            LoadProvider<A, T, Z, R> loadProvider,
139            A model,
140            Context context,
141            Priority priority,
142            Target<R> target,
143            float sizeMultiplier,
144            Drawable placeholderDrawable,
145            int placeholderResourceId,
146            Drawable errorDrawable,
147            int errorResourceId,
148            RequestListener<A, R> requestListener,
149            RequestCoordinator requestCoordinator,
150            Engine engine,
151            Transformation<Z> transformation,
152            Class<R> transcodeClass,
153            boolean isMemoryCacheable,
154            GlideAnimationFactory<R> animationFactory,
155            int overrideWidth,
156            int overrideHeight) {
157        this.loadProvider = loadProvider;
158        this.model = model;
159        this.context = context;
160        this.priority = priority;
161        this.target = target;
162        this.sizeMultiplier = sizeMultiplier;
163        this.placeholderDrawable = placeholderDrawable;
164        this.placeholderResourceId = placeholderResourceId;
165        this.errorDrawable = errorDrawable;
166        this.errorResourceId = errorResourceId;
167        this.requestListener = requestListener;
168        this.requestCoordinator = requestCoordinator;
169        this.engine = engine;
170        this.transformation = transformation;
171        this.transcodeClass = transcodeClass;
172        this.isMemoryCacheable = isMemoryCacheable;
173        this.animationFactory = animationFactory;
174        this.overrideWidth = overrideWidth;
175        this.overrideHeight = overrideHeight;
176
177        // We allow null models by just setting an error drawable. Null models will always have empty providers, we
178        // simply skip our sanity checks in that unusual case.
179        if (model != null) {
180            if (loadProvider.getCacheDecoder() == null) {
181                throw new NullPointerException("CacheDecoder must not be null, try .cacheDecoder(ResouceDecoder)");
182            }
183            if (loadProvider.getSourceDecoder() == null) {
184                throw new NullPointerException("SourceDecoder must not be null, try .imageDecoder(ResourceDecoder) " +
185                        "and/or .videoDecoder()");
186            }
187            if (loadProvider.getEncoder() == null) {
188                throw new NullPointerException("Encoder must not be null, try .encode(ResourceEncoder)");
189            }
190            if (loadProvider.getTranscoder() == null) {
191                throw new NullPointerException("Transcoder must not be null, try .as(Class, ResourceTranscoder)");
192            }
193            if (loadProvider.getModelLoader() == null) {
194                throw new NullPointerException("ModelLoader must not be null, try .using(ModelLoader)");
195            }
196        }
197    }
198
199    @Override
200    public void run() {
201        startTime = LogTime.getLogTime();
202        if (model == null) {
203            onException(null);
204            return;
205        }
206
207        if (overrideWidth > 0 && overrideHeight > 0) {
208            onSizeReady(overrideWidth, overrideHeight);
209        } else {
210            target.getSize(this);
211        }
212
213        if (!isComplete() && !isFailed()) {
214            setPlaceHolder();
215            isRunning = true;
216        }
217        if (Log.isLoggable(TAG, Log.VERBOSE)) {
218            logV("finished run method in " + LogTime.getElapsedMillis(startTime));
219        }
220    }
221
222    public void cancel() {
223        isRunning = false;
224        isCancelled = true;
225        if (loadStatus != null) {
226            loadStatus.cancel();
227            loadStatus = null;
228        }
229    }
230
231    @Override
232    public void clear() {
233        cancel();
234        setPlaceHolder();
235        if (resource != null) {
236            resource.release();
237            resource = null;
238        }
239    }
240
241    @Override
242    public boolean isRunning() {
243        return isRunning;
244    }
245
246    @Override
247    public boolean isComplete() {
248        return resource != null;
249    }
250
251    @Override
252    public boolean isFailed() {
253        return isError;
254    }
255
256    private void setPlaceHolder() {
257        if (!canSetPlaceholder()) return;
258
259        target.setPlaceholder(getPlaceholderDrawable());
260    }
261
262    private void setErrorPlaceholder() {
263        if (!canSetPlaceholder()) return;
264
265        Drawable error = getErrorDrawable();
266        if (error != null) {
267            target.setPlaceholder(error);
268        } else {
269            setPlaceHolder();
270        }
271    }
272
273    private Drawable getErrorDrawable() {
274        if (errorDrawable == null && errorResourceId > 0) {
275            errorDrawable = context.getResources().getDrawable(errorResourceId);
276        }
277        return errorDrawable;
278    }
279
280    private Drawable getPlaceholderDrawable() {
281        if (placeholderDrawable == null && placeholderResourceId > 0) {
282            placeholderDrawable = context.getResources().getDrawable(placeholderResourceId);
283        }
284        return placeholderDrawable;
285    }
286
287    @Override
288    public void onSizeReady(int width, int height) {
289        if (Log.isLoggable(TAG, Log.VERBOSE)) {
290            logV("Got onSizeReady in " + LogTime.getElapsedMillis(startTime));
291        }
292        if (isCancelled) {
293            return;
294        }
295
296        width = Math.round(sizeMultiplier * width);
297        height = Math.round(sizeMultiplier * height);
298        ResourceDecoder<InputStream, Z> cacheDecoder = loadProvider.getCacheDecoder();
299        ResourceDecoder<T, Z> decoder = loadProvider.getSourceDecoder();
300        ResourceEncoder <Z> encoder = loadProvider.getEncoder();
301        ResourceTranscoder<Z, R> transcoder = loadProvider.getTranscoder();
302        ModelLoader<A, T> modelLoader = loadProvider.getModelLoader();
303
304        final DataFetcher<T> dataFetcher = modelLoader.getResourceFetcher(model, width, height);
305
306        if (Log.isLoggable(TAG, Log.VERBOSE)) {
307            logV("finished setup for calling load in " + LogTime.getElapsedMillis(startTime));
308        }
309        loadedFromMemoryCache = true;
310        loadStatus = engine.load(width, height, cacheDecoder, dataFetcher, decoder, transformation,
311                encoder, transcoder, priority, isMemoryCacheable, this);
312        loadedFromMemoryCache = resource != null;
313        if (Log.isLoggable(TAG, Log.VERBOSE)) {
314            logV("finished onSizeReady in " + LogTime.getElapsedMillis(startTime));
315        }
316    }
317
318    private boolean canSetImage() {
319        return requestCoordinator == null || requestCoordinator.canSetImage(this);
320    }
321
322    private boolean canSetPlaceholder() {
323        return requestCoordinator == null || requestCoordinator.canSetPlaceholder(this);
324    }
325
326    private boolean isFirstImage() {
327        return requestCoordinator == null || !requestCoordinator.isAnyRequestComplete();
328    }
329
330    @SuppressWarnings("unchecked")
331    @Override
332    public void onResourceReady(Resource resource) {
333        isRunning = false;
334        if (!canSetImage()) {
335            resource.release();
336            return;
337        }
338        if (resource == null || !transcodeClass.isAssignableFrom(resource.get().getClass())) {
339            if (resource != null) {
340                resource.release();
341            }
342            onException(new Exception("Expected to receive an object of " + transcodeClass + " but instead got " +
343                    (resource != null ? resource.get() : null)));
344            return;
345        }
346        R result = (R) resource.get();
347
348        if (requestListener == null || !requestListener.onResourceReady(result, model, target, loadedFromMemoryCache,
349                isFirstImage())) {
350            GlideAnimation<R> animation = animationFactory.build(loadedFromMemoryCache, isFirstImage());
351            target.onResourceReady(result, animation);
352        }
353
354        this.resource = resource;
355
356        if (Log.isLoggable(TAG, Log.VERBOSE)) {
357            logV("Resource ready in " + LogTime.getElapsedMillis(startTime) + " size: "
358                    + (resource.getSize() / (1024d * 1024d)) + " fromCache: " + loadedFromMemoryCache);
359        }
360    }
361
362    @Override
363    public void onException(Exception e) {
364        if (Log.isLoggable(TAG, Log.DEBUG)) {
365            Log.d(TAG, "load failed", e);
366        }
367
368        isRunning = false;
369        isError = true;
370        //TODO: what if this is a thumbnail request?
371        if (requestListener == null || !requestListener.onException(e, model, target, isFirstImage())) {
372            setErrorPlaceholder();
373        }
374    }
375
376    private void logV(String message) {
377        Log.v(TAG, message + " this: " + tag);
378    }
379}
380