1package com.bumptech.glide.load.engine;
2
3import android.os.Handler;
4import android.util.Log;
5import com.bumptech.glide.load.Key;
6import com.bumptech.glide.request.ResourceCallback;
7import com.bumptech.glide.util.LogTime;
8
9import java.util.ArrayList;
10import java.util.List;
11
12public class EngineJob implements ResourceCallback {
13    private static final String TAG = "EngineJob";
14    private boolean isCacheable;
15    private final EngineJobListener listener;
16    private Key key;
17    private Handler mainHandler;
18    private List<ResourceCallback> cbs;
19    private ResourceCallback cb;
20    private boolean isCancelled;
21    private boolean isComplete;
22
23    public EngineJob(Key key, Handler mainHandler, boolean isCacheable, EngineJobListener listener) {
24        this.key = key;
25        this.isCacheable = isCacheable;
26        this.listener = listener;
27        this.mainHandler = mainHandler;
28    }
29
30    public void addCallback(ResourceCallback cb) {
31        if (this.cb == null) {
32            this.cb = cb;
33        } else {
34            if (cbs == null) {
35                cbs = new ArrayList<ResourceCallback>(2);
36                cbs.add(this.cb);
37            }
38            cbs.add(cb);
39        }
40    }
41
42    public void removeCallback(ResourceCallback cb) {
43        if (cbs != null) {
44            cbs.remove(cb);
45            if (cbs.size() == 0) {
46                cancel();
47            }
48        } else if (this.cb == cb) {
49            this.cb = null;
50            cancel();
51        }
52    }
53
54    // Exposed for testing.
55    void cancel() {
56        if (isComplete || isCancelled) {
57            return;
58        }
59        isCancelled = true;
60        listener.onEngineJobCancelled(key);
61    }
62
63    // Exposed for testing.
64    boolean isCancelled() {
65        return isCancelled;
66    }
67
68    @Override
69    public void onResourceReady(final Resource resource) {
70        final long start = LogTime.getLogTime();
71        mainHandler.post(new Runnable() {
72            @Override
73            public void run() {
74                if (Log.isLoggable(TAG, Log.VERBOSE)) {
75                    Log.v(TAG, "Posted to main thread in onResourceReady in " + LogTime.getElapsedMillis(start)
76                            + " cancelled: " + isCancelled);
77                }
78                if (isCancelled) {
79                    resource.recycle();
80                    return;
81                }
82                resource.setCacheable(isCacheable);
83                isComplete = true;
84
85                // Hold on to resource for duration of request so we don't recycle it in the middle of notifying if it
86                // synchronously released by one of the callbacks.
87                resource.acquire(1);
88                listener.onEngineJobComplete(key, resource);
89                if (cbs != null) {
90                    resource.acquire(cbs.size());
91                    for (ResourceCallback cb : cbs) {
92                        cb.onResourceReady(resource);
93                    }
94                } else {
95                    resource.acquire(1);
96                    cb.onResourceReady(resource);
97                }
98                // Our request is complete, so we can release the resource.
99                resource.release();
100                if (Log.isLoggable(TAG, Log.VERBOSE)) {
101                    Log.v(TAG, "Finished resource ready in " + LogTime.getElapsedMillis(start));
102                }
103            }
104        });
105    }
106
107    @Override
108    public void onException(final Exception e) {
109        final long start = LogTime.getLogTime();
110        mainHandler.post(new Runnable() {
111            @Override
112            public void run() {
113                if (Log.isLoggable(TAG, Log.VERBOSE)) {
114                    Log.v(TAG, "posted to main thread in onException in " + LogTime.getElapsedMillis(start)
115                            + " cancelled: " + isCancelled);
116                }
117                if (isCancelled) {
118                    return;
119                }
120                isComplete = true;
121
122                listener.onEngineJobComplete(key, null);
123                if (cbs != null) {
124                    for (ResourceCallback cb : cbs) {
125                        cb.onException(e);
126                    }
127                } else {
128                    cb.onException(e);
129                }
130                if (Log.isLoggable(TAG, Log.VERBOSE)) {
131                    Log.v(TAG, "finished onException in " + LogTime.getElapsedMillis(start));
132                }
133            }
134        });
135    }
136}
137