1 package com.bumptech.glide.load.engine;
2 
3 import android.os.Looper;
4 
5 import com.bumptech.glide.load.Key;
6 
7 /**
8  * A wrapper resource that allows reference counting a wrapped {@link com.bumptech.glide.load.engine.Resource}
9  * interface.
10  *
11  * @param <Z> The type of data returned by the wrapped {@link Resource}.
12  */
13 class EngineResource<Z> implements Resource<Z> {
14     private final Resource<Z> resource;
15     private final boolean isCacheable;
16     private ResourceListener listener;
17     private Key key;
18     private int acquired;
19     private boolean isRecycled;
20 
21     interface ResourceListener {
onResourceReleased(Key key, EngineResource<?> resource)22         void onResourceReleased(Key key, EngineResource<?> resource);
23     }
24 
EngineResource(Resource<Z> toWrap, boolean isCacheable)25     EngineResource(Resource<Z> toWrap, boolean isCacheable) {
26         if (toWrap == null) {
27             throw new NullPointerException("Wrapped resource must not be null");
28         }
29         resource = toWrap;
30         this.isCacheable = isCacheable;
31     }
32 
setResourceListener(Key key, ResourceListener listener)33     void setResourceListener(Key key, ResourceListener listener) {
34         this.key = key;
35         this.listener = listener;
36     }
37 
isCacheable()38     boolean isCacheable() {
39         return isCacheable;
40     }
41 
42     @Override
get()43     public Z get() {
44         return resource.get();
45     }
46 
47     @Override
getSize()48     public int getSize() {
49         return resource.getSize();
50     }
51 
52     @Override
recycle()53     public void recycle() {
54         if (acquired > 0) {
55             throw new IllegalStateException("Cannot recycle a resource while it is still acquired");
56         }
57         if (isRecycled) {
58             throw new IllegalStateException("Cannot recycle a resource that has already been recycled");
59         }
60         isRecycled = true;
61         resource.recycle();
62     }
63 
64     /**
65      * Increments the number of consumers using the wrapped resource. Must be called on the main thread.
66      *
67      * <p>
68      *     This must be called with a number corresponding to the number of new consumers each time new consumers
69      *     begin using the wrapped resource. It is always safer to call acquire more often than necessary. Generally
70      *     external users should never call this method, the framework will take care of this for you.
71      * </p>
72      */
acquire()73     void acquire() {
74         if (isRecycled) {
75             throw new IllegalStateException("Cannot acquire a recycled resource");
76         }
77         if (!Looper.getMainLooper().equals(Looper.myLooper())) {
78             throw new IllegalThreadStateException("Must call acquire on the main thread");
79         }
80         ++acquired;
81     }
82 
83     /**
84      * Decrements the number of consumers using the wrapped resource. Must be called on the main thread.
85      *
86      * <p>
87      *     This must only be called when a consumer that called the {@link #acquire()} method is now done with the
88      *     resource. Generally external users should never callthis method, the framework will take care of this for
89      *     you.
90      * </p>
91      */
release()92     void release() {
93         if (acquired <= 0) {
94             throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
95         }
96         if (!Looper.getMainLooper().equals(Looper.myLooper())) {
97             throw new IllegalThreadStateException("Must call release on the main thread");
98         }
99         if (--acquired == 0) {
100             listener.onResourceReleased(key, this);
101         }
102     }
103 }
104