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