Android编程之图片加载类

openkk 12年前

关于图片加载类。其实,网上有很多这样的类,而且比较推崇的是来自google中开源中的一篇。他写的比较好了,而且注意了内存优化,下面贴出它的图片下载类:

/*   * Copyright (C) 2010 The Android Open Source Project   *   * Licensed under the Apache License, Version 2.0 (the "License");   * you may not use this file except in compliance with the License.   * You may obtain a copy of the License at   *   *      http://www.apache.org/licenses/LICENSE-2.0   *   * Unless required by applicable law or agreed to in writing, software   * distributed under the License is distributed on an "AS IS" BASIS,   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   * See the License for the specific language governing permissions and   * limitations under the License.   */    package com.example.android.imagedownloader;    import org.apache.http.HttpEntity;  import org.apache.http.HttpResponse;  import org.apache.http.HttpStatus;  import org.apache.http.client.HttpClient;  import org.apache.http.client.methods.HttpGet;  import org.apache.http.impl.client.DefaultHttpClient;    import android.graphics.Bitmap;  import android.graphics.BitmapFactory;  import android.graphics.Color;  import android.graphics.drawable.ColorDrawable;  import android.graphics.drawable.Drawable;  import android.net.http.AndroidHttpClient;  import android.os.AsyncTask;  import android.os.Handler;  import android.util.Log;  import android.widget.ImageView;    import java.io.FilterInputStream;  import java.io.IOException;  import java.io.InputStream;  import java.lang.ref.SoftReference;  import java.lang.ref.WeakReference;  import java.util.HashMap;  import java.util.LinkedHashMap;  import java.util.concurrent.ConcurrentHashMap;    /**   * This helper class download images from the Internet and binds those with the provided ImageView.   *   * <p>It requires the INTERNET permission, which should be added to your application's manifest   * file.</p>   *   * A local cache of downloaded images is maintained internally to improve performance.   */  public class ImageDownloader {      private static final String LOG_TAG = "ImageDownloader";        public enum Mode { NO_ASYNC_TASK, NO_DOWNLOADED_DRAWABLE, CORRECT }      private Mode mode = Mode.NO_ASYNC_TASK;            /**       * Download the specified image from the Internet and binds it to the provided ImageView. The       * binding is immediate if the image is found in the cache and will be done asynchronously       * otherwise. A null bitmap will be associated to the ImageView if an error occurs.       *       * @param url The URL of the image to download.       * @param imageView The ImageView to bind the downloaded image to.       */      public void download(String url, ImageView imageView) {          resetPurgeTimer();          Bitmap bitmap = getBitmapFromCache(url);            if (bitmap == null) {              forceDownload(url, imageView);          } else {              cancelPotentialDownload(url, imageView);              imageView.setImageBitmap(bitmap);          }      }        /*       * Same as download but the image is always downloaded and the cache is not used.       * Kept private at the moment as its interest is not clear.         private void forceDownload(String url, ImageView view) {            forceDownload(url, view, null);         }       */        /**       * Same as download but the image is always downloaded and the cache is not used.       * Kept private at the moment as its interest is not clear.       */      private void forceDownload(String url, ImageView imageView) {          // State sanity: url is guaranteed to never be null in DownloadedDrawable and cache keys.          if (url == null) {              imageView.setImageDrawable(null);              return;          }            if (cancelPotentialDownload(url, imageView)) {              switch (mode) {                  case NO_ASYNC_TASK:                      Bitmap bitmap = downloadBitmap(url);                      addBitmapToCache(url, bitmap);                      imageView.setImageBitmap(bitmap);                      break;                    case NO_DOWNLOADED_DRAWABLE:                      imageView.setMinimumHeight(156);                      BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);                      task.execute(url);                      break;                    case CORRECT:                      task = new BitmapDownloaderTask(imageView);                      DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);                      imageView.setImageDrawable(downloadedDrawable);                      imageView.setMinimumHeight(156);                      task.execute(url);                      break;              }          }      }        /**       * Returns true if the current download has been canceled or if there was no download in       * progress on this image view.       * Returns false if the download in progress deals with the same url. The download is not       * stopped in that case.       */      private static boolean cancelPotentialDownload(String url, ImageView imageView) {          BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);            if (bitmapDownloaderTask != null) {              String bitmapUrl = bitmapDownloaderTask.url;              if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {                  bitmapDownloaderTask.cancel(true);              } else {                  // The same URL is already being downloaded.                  return false;              }          }          return true;      }        /**       * @param imageView Any imageView       * @return Retrieve the currently active download task (if any) associated with this imageView.       * null if there is no such task.       */      private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {          if (imageView != null) {              Drawable drawable = imageView.getDrawable();              if (drawable instanceof DownloadedDrawable) {                  DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;                  return downloadedDrawable.getBitmapDownloaderTask();              }          }          return null;      }        Bitmap downloadBitmap(String url) {          final int IO_BUFFER_SIZE = 4 * 1024;            // AndroidHttpClient is not allowed to be used from the main thread          final HttpClient client = (mode == Mode.NO_ASYNC_TASK) ? new DefaultHttpClient() :              AndroidHttpClient.newInstance("Android");          final HttpGet getRequest = new HttpGet(url);            try {              HttpResponse response = client.execute(getRequest);              final int statusCode = response.getStatusLine().getStatusCode();              if (statusCode != HttpStatus.SC_OK) {                  Log.w("ImageDownloader", "Error " + statusCode +                          " while retrieving bitmap from " + url);                  return null;              }                final HttpEntity entity = response.getEntity();              if (entity != null) {                  InputStream inputStream = null;                  try {                      inputStream = entity.getContent();                      // return BitmapFactory.decodeStream(inputStream);                      // Bug on slow connections, fixed in future release.                      return BitmapFactory.decodeStream(new FlushedInputStream(inputStream));                  } finally {                      if (inputStream != null) {                          inputStream.close();                      }                      entity.consumeContent();                  }              }          } catch (IOException e) {              getRequest.abort();              Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);          } catch (IllegalStateException e) {              getRequest.abort();              Log.w(LOG_TAG, "Incorrect URL: " + url);          } catch (Exception e) {              getRequest.abort();              Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);          } finally {              if ((client instanceof AndroidHttpClient)) {                  ((AndroidHttpClient) client).close();              }          }          return null;      }        /*       * An InputStream that skips the exact number of bytes provided, unless it reaches EOF.       */      static class FlushedInputStream extends FilterInputStream {          public FlushedInputStream(InputStream inputStream) {              super(inputStream);          }            @Override          public long skip(long n) throws IOException {              long totalBytesSkipped = 0L;              while (totalBytesSkipped < n) {                  long bytesSkipped = in.skip(n - totalBytesSkipped);                  if (bytesSkipped == 0L) {                      int b = read();                      if (b < 0) {                          break;  // we reached EOF                      } else {                          bytesSkipped = 1; // we read one byte                      }                  }                  totalBytesSkipped += bytesSkipped;              }              return totalBytesSkipped;          }      }        /**       * The actual AsyncTask that will asynchronously download the image.       */      class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {          private String url;          private final WeakReference<ImageView> imageViewReference;            public BitmapDownloaderTask(ImageView imageView) {              imageViewReference = new WeakReference<ImageView>(imageView);          }            /**           * Actual download method.           */          @Override          protected Bitmap doInBackground(String... params) {              url = params[0];              return downloadBitmap(url);          }            /**           * Once the image is downloaded, associates it to the imageView           */          @Override          protected void onPostExecute(Bitmap bitmap) {              if (isCancelled()) {                  bitmap = null;              }                addBitmapToCache(url, bitmap);                if (imageViewReference != null) {                  ImageView imageView = imageViewReference.get();                  BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);                  // Change bitmap only if this process is still associated with it                  // Or if we don't use any bitmap to task association (NO_DOWNLOADED_DRAWABLE mode)                  if ((this == bitmapDownloaderTask) || (mode != Mode.CORRECT)) {                      imageView.setImageBitmap(bitmap);                  }              }          }      }          /**       * A fake Drawable that will be attached to the imageView while the download is in progress.       *       * <p>Contains a reference to the actual download task, so that a download task can be stopped       * if a new binding is required, and makes sure that only the last started download process can       * bind its result, independently of the download finish order.</p>       */      static class DownloadedDrawable extends ColorDrawable {          private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;            public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {              super(Color.BLACK);              bitmapDownloaderTaskReference =                  new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);          }            public BitmapDownloaderTask getBitmapDownloaderTask() {              return bitmapDownloaderTaskReference.get();          }      }        public void setMode(Mode mode) {          this.mode = mode;          clearCache();      }              /*       * Cache-related fields and methods.       *        * We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the       * Garbage Collector.       */            private static final int HARD_CACHE_CAPACITY = 10;      private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds        // Hard cache, with a fixed maximum capacity and a life duration      private final HashMap<String, Bitmap> sHardBitmapCache =          new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {          @Override          protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {              if (size() > HARD_CACHE_CAPACITY) {                  // Entries push-out of hard reference cache are transferred to soft reference cache                  sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));                  return true;              } else                  return false;          }      };        // Soft cache for bitmaps kicked out of hard cache      private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache =          new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);        private final Handler purgeHandler = new Handler();        private final Runnable purger = new Runnable() {          public void run() {              clearCache();          }      };        /**       * Adds this bitmap to the cache.       * @param bitmap The newly downloaded bitmap.       */      private void addBitmapToCache(String url, Bitmap bitmap) {          if (bitmap != null) {              synchronized (sHardBitmapCache) {                  sHardBitmapCache.put(url, bitmap);              }          }      }        /**       * @param url The URL of the image that will be retrieved from the cache.       * @return The cached bitmap or null if it was not found.       */      private Bitmap getBitmapFromCache(String url) {          // First try the hard reference cache          synchronized (sHardBitmapCache) {              final Bitmap bitmap = sHardBitmapCache.get(url);              if (bitmap != null) {                  // Bitmap found in hard cache                  // Move element to first position, so that it is removed last                  sHardBitmapCache.remove(url);                  sHardBitmapCache.put(url, bitmap);                  return bitmap;              }          }            // Then try the soft reference cache          SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);          if (bitmapReference != null) {              final Bitmap bitmap = bitmapReference.get();              if (bitmap != null) {                  // Bitmap found in soft cache                  return bitmap;              } else {                  // Soft reference has been Garbage Collected                  sSoftBitmapCache.remove(url);              }          }            return null;      }         /**       * Clears the image cache used internally to improve performance. Note that for memory       * efficiency reasons, the cache will automatically be cleared after a certain inactivity delay.       */      public void clearCache() {          sHardBitmapCache.clear();          sSoftBitmapCache.clear();      }        /**       * Allow a new delay before the automatic cache clear is done.       */      private void resetPurgeTimer() {          purgeHandler.removeCallbacks(purger);          purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE);      }  }


里面有一些图片加载的方式方法,都很可取,不过对于ListView的优化而言,还应配合列表滚动监听来写。也就是说,当列表停止滚动,手离开屏幕的时候,再加载大数据,例如图片。稍后,我也会整理一篇,给大家提供参考借鉴。


源代码来自:http://code.google.com/p/android-imagedownloader/

滚动加载的例子:http://www.iteye.com/topic/1118828