Home | History | Annotate | Download | only in threadsample
      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      */
     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      */
    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
    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      */
    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
    154     public int getTargetWidth() {
    155         return mTargetWidth;
    156     }
    157 
    158     // Implements PhotoDownloadRunnable.getTargetHeight. Returns the global target height.
    159     @Override
    160     public int getTargetHeight() {
    161         return mTargetHeight;
    162     }
    163 
    164     // Detects the state of caching
    165     boolean isCacheEnabled() {
    166         return mCacheEnabled;
    167     }
    168 
    169     // Implements PhotoDownloadRunnable.getImageURL. Returns the global Image URL.
    170     @Override
    171     public URL getImageURL() {
    172         return mImageURL;
    173     }
    174 
    175     // Implements PhotoDownloadRunnable.setByteBuffer. Sets the image buffer to a buffer object.
    176     @Override
    177     public void setByteBuffer(byte[] imageBuffer) {
    178         mImageBuffer = imageBuffer;
    179     }
    180 
    181     // Delegates handling the current state of the task to the PhotoManager object
    182     void handleState(int state) {
    183         sPhotoManager.handleState(this, state);
    184     }
    185 
    186     // Returns the image that PhotoDecodeRunnable decoded.
    187     Bitmap getImage() {
    188         return mDecodedImage;
    189     }
    190 
    191     // Returns the instance that downloaded the image
    192     Runnable getHTTPDownloadRunnable() {
    193         return mDownloadRunnable;
    194     }
    195 
    196     // Returns the instance that decode the image
    197     Runnable getPhotoDecodeRunnable() {
    198         return mDecodeRunnable;
    199     }
    200 
    201     // Returns the ImageView that's being constructed.
    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      */
    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      */
    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
    233     public void setImage(Bitmap decodedImage) {
    234         mDecodedImage = decodedImage;
    235     }
    236 
    237     // Implements PhotoDownloadRunnable.setHTTPDownloadThread(). Calls setCurrentThread().
    238     @Override
    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
    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
    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
    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