1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.threadsample; 18 19 import com.example.android.threadsample.PhotoDecodeRunnable.TaskRunnableDecodeMethods; 20 import com.example.android.threadsample.PhotoDownloadRunnable.TaskRunnableDownloadMethods; 21 22 import android.graphics.Bitmap; 23 24 import java.lang.ref.WeakReference; 25 import java.net.URL; 26 27 /** 28 * This class manages PhotoDownloadRunnable and PhotoDownloadRunnable objects. It does't perform 29 * the download or decode; instead, it manages persistent storage for the tasks that do the work. 30 * It does this by implementing the interfaces that the download and decode classes define, and 31 * then passing itself as an argument to the constructor of a download or decode object. In effect, 32 * this allows PhotoTask to start on a Thread, run a download in a delegate object, then 33 * run a decode, and then start over again. This class can be pooled and reused as necessary. 34 */ 35 public class PhotoTask implements 36 TaskRunnableDownloadMethods, TaskRunnableDecodeMethods { 37 38 /* 39 * Creates a weak reference to the ImageView that this Task will populate. 40 * The weak reference prevents memory leaks and crashes, because it 41 * automatically tracks the "state" of the variable it backs. If the 42 * reference becomes invalid, the weak reference is garbage- collected. This 43 * technique is important for referring to objects that are part of a 44 * component lifecycle. Using a hard reference may cause memory leaks as the 45 * value continues to change; even worse, it can cause crashes if the 46 * underlying component is destroyed. Using a weak reference to a View 47 * ensures that the reference is more transitory in nature. 48 */ 49 private WeakReference<PhotoView> mImageWeakRef; 50 51 // The image's URL 52 private URL mImageURL; 53 54 // The width and height of the decoded image 55 private int mTargetHeight; 56 private int mTargetWidth; 57 58 // Is the cache enabled for this transaction? 59 private boolean mCacheEnabled; 60 61 /* 62 * Field containing the Thread this task is running on. 63 */ 64 Thread mThreadThis; 65 66 /* 67 * Fields containing references to the two runnable objects that handle downloading and 68 * decoding of the image. 69 */ 70 private Runnable mDownloadRunnable; 71 private Runnable mDecodeRunnable; 72 73 // A buffer for containing the bytes that make up the image 74 byte[] mImageBuffer; 75 76 // The decoded image 77 private Bitmap mDecodedImage; 78 79 // The Thread on which this task is currently running. 80 private Thread mCurrentThread; 81 82 /* 83 * An object that contains the ThreadPool singleton. 84 */ 85 private static PhotoManager sPhotoManager; 86 87 /** 88 * Creates an PhotoTask containing a download object and a decoder object. 89 */ PhotoTask()90 PhotoTask() { 91 // Create the runnables 92 mDownloadRunnable = new PhotoDownloadRunnable(this); 93 mDecodeRunnable = new PhotoDecodeRunnable(this); 94 sPhotoManager = PhotoManager.getInstance(); 95 } 96 97 /** 98 * Initializes the Task 99 * 100 * @param photoManager A ThreadPool object 101 * @param photoView An ImageView instance that shows the downloaded image 102 * @param cacheFlag Whether caching is enabled 103 */ initializeDownloaderTask( PhotoManager photoManager, PhotoView photoView, boolean cacheFlag)104 void initializeDownloaderTask( 105 PhotoManager photoManager, 106 PhotoView photoView, 107 boolean cacheFlag) 108 { 109 // Sets this object's ThreadPool field to be the input argument 110 sPhotoManager = photoManager; 111 112 // Gets the URL for the View 113 mImageURL = photoView.getLocation(); 114 115 // Instantiates the weak reference to the incoming view 116 mImageWeakRef = new WeakReference<PhotoView>(photoView); 117 118 // Sets the cache flag to the input argument 119 mCacheEnabled = cacheFlag; 120 121 // Gets the width and height of the provided ImageView 122 mTargetWidth = photoView.getWidth(); 123 mTargetHeight = photoView.getHeight(); 124 125 } 126 127 // Implements HTTPDownloaderRunnable.getByteBuffer 128 @Override getByteBuffer()129 public byte[] getByteBuffer() { 130 131 // Returns the global field 132 return mImageBuffer; 133 } 134 135 /** 136 * Recycles an PhotoTask object before it's put back into the pool. One reason to do 137 * this is to avoid memory leaks. 138 */ recycle()139 void recycle() { 140 141 // Deletes the weak reference to the imageView 142 if ( null != mImageWeakRef ) { 143 mImageWeakRef.clear(); 144 mImageWeakRef = null; 145 } 146 147 // Releases references to the byte buffer and the BitMap 148 mImageBuffer = null; 149 mDecodedImage = null; 150 } 151 152 // Implements PhotoDownloadRunnable.getTargetWidth. Returns the global target width. 153 @Override getTargetWidth()154 public int getTargetWidth() { 155 return mTargetWidth; 156 } 157 158 // Implements PhotoDownloadRunnable.getTargetHeight. Returns the global target height. 159 @Override getTargetHeight()160 public int getTargetHeight() { 161 return mTargetHeight; 162 } 163 164 // Detects the state of caching isCacheEnabled()165 boolean isCacheEnabled() { 166 return mCacheEnabled; 167 } 168 169 // Implements PhotoDownloadRunnable.getImageURL. Returns the global Image URL. 170 @Override getImageURL()171 public URL getImageURL() { 172 return mImageURL; 173 } 174 175 // Implements PhotoDownloadRunnable.setByteBuffer. Sets the image buffer to a buffer object. 176 @Override setByteBuffer(byte[] imageBuffer)177 public void setByteBuffer(byte[] imageBuffer) { 178 mImageBuffer = imageBuffer; 179 } 180 181 // Delegates handling the current state of the task to the PhotoManager object handleState(int state)182 void handleState(int state) { 183 sPhotoManager.handleState(this, state); 184 } 185 186 // Returns the image that PhotoDecodeRunnable decoded. getImage()187 Bitmap getImage() { 188 return mDecodedImage; 189 } 190 191 // Returns the instance that downloaded the image getHTTPDownloadRunnable()192 Runnable getHTTPDownloadRunnable() { 193 return mDownloadRunnable; 194 } 195 196 // Returns the instance that decode the image getPhotoDecodeRunnable()197 Runnable getPhotoDecodeRunnable() { 198 return mDecodeRunnable; 199 } 200 201 // Returns the ImageView that's being constructed. getPhotoView()202 public PhotoView getPhotoView() { 203 if ( null != mImageWeakRef ) { 204 return mImageWeakRef.get(); 205 } 206 return null; 207 } 208 209 /* 210 * Returns the Thread that this Task is running on. The method must first get a lock on a 211 * static field, in this case the ThreadPool singleton. The lock is needed because the 212 * Thread object reference is stored in the Thread object itself, and that object can be 213 * changed by processes outside of this app. 214 */ getCurrentThread()215 public Thread getCurrentThread() { 216 synchronized(sPhotoManager) { 217 return mCurrentThread; 218 } 219 } 220 221 /* 222 * Sets the identifier for the current Thread. This must be a synchronized operation; see the 223 * notes for getCurrentThread() 224 */ setCurrentThread(Thread thread)225 public void setCurrentThread(Thread thread) { 226 synchronized(sPhotoManager) { 227 mCurrentThread = thread; 228 } 229 } 230 231 // Implements ImageCoderRunnable.setImage(). Sets the Bitmap for the current image. 232 @Override setImage(Bitmap decodedImage)233 public void setImage(Bitmap decodedImage) { 234 mDecodedImage = decodedImage; 235 } 236 237 // Implements PhotoDownloadRunnable.setHTTPDownloadThread(). Calls setCurrentThread(). 238 @Override setDownloadThread(Thread currentThread)239 public void setDownloadThread(Thread currentThread) { 240 setCurrentThread(currentThread); 241 } 242 243 /* 244 * Implements PhotoDownloadRunnable.handleHTTPState(). Passes the download state to the 245 * ThreadPool object. 246 */ 247 248 @Override handleDownloadState(int state)249 public void handleDownloadState(int state) { 250 int outState; 251 252 // Converts the download state to the overall state 253 switch(state) { 254 case PhotoDownloadRunnable.HTTP_STATE_COMPLETED: 255 outState = PhotoManager.DOWNLOAD_COMPLETE; 256 break; 257 case PhotoDownloadRunnable.HTTP_STATE_FAILED: 258 outState = PhotoManager.DOWNLOAD_FAILED; 259 break; 260 default: 261 outState = PhotoManager.DOWNLOAD_STARTED; 262 break; 263 } 264 // Passes the state to the ThreadPool object. 265 handleState(outState); 266 } 267 268 // Implements PhotoDecodeRunnable.setImageDecodeThread(). Calls setCurrentThread(). 269 @Override setImageDecodeThread(Thread currentThread)270 public void setImageDecodeThread(Thread currentThread) { 271 setCurrentThread(currentThread); 272 } 273 274 /* 275 * Implements PhotoDecodeRunnable.handleDecodeState(). Passes the decoding state to the 276 * ThreadPool object. 277 */ 278 @Override handleDecodeState(int state)279 public void handleDecodeState(int state) { 280 int outState; 281 282 // Converts the decode state to the overall state. 283 switch(state) { 284 case PhotoDecodeRunnable.DECODE_STATE_COMPLETED: 285 outState = PhotoManager.TASK_COMPLETE; 286 break; 287 case PhotoDecodeRunnable.DECODE_STATE_FAILED: 288 outState = PhotoManager.DOWNLOAD_FAILED; 289 break; 290 default: 291 outState = PhotoManager.DECODE_STARTED; 292 break; 293 } 294 295 // Passes the state to the ThreadPool object. 296 handleState(outState); 297 } 298 } 299