欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android實(shí)現(xiàn)圖片緩存與異步加載

 更新時(shí)間:2016年02月19日 11:02:49   投稿:lijiao  
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)圖片緩存與異步加載的相關(guān)資料,需要的朋友可以參考下

ImageManager2這個(gè)類具有異步從網(wǎng)絡(luò)下載圖片,從sd讀取本地圖片,內(nèi)存緩存,硬盤緩存,圖片使用動(dòng)畫漸現(xiàn)等功能,已經(jīng)將其應(yīng)用在包含大量圖片的應(yīng)用中一年多,沒(méi)有出現(xiàn)oom。

Android程序常常會(huì)內(nèi)存溢出,網(wǎng)上也有很多解決方案,如軟引用,手動(dòng)調(diào)用recycle等等。但經(jīng)過(guò)我們實(shí)踐發(fā)現(xiàn)這些方案,都沒(méi)能起到很好的效果,我們的應(yīng)用依然會(huì)出現(xiàn)很多oom,尤其我們的應(yīng)用包含大量的圖片。android3.0之后軟引用基本已經(jīng)失效,因?yàn)樘摂M機(jī)只要碰到軟引用就回收,所以帶不來(lái)任何性能的提升。

我這里的解決方案是HandlerThread(異步加載)+LruCache(內(nèi)存緩存)+DiskLruCache(硬盤緩存)。

作為程序員,我也不多說(shuō),直接和大家共享我的代碼,用代碼交流更方便些。

package com.example.util;
 
import java.io.File;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
 
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.util.EntityUtils;
 
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.media.ThumbnailUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.widget.ImageView;
 
import com.example.MyApplication;
 
/**
 * 圖片加載類
 * 
 * @author 月月鳥
 */
public class ImageManager2 {
 
  private static ImageManager2 imageManager;
  public LruCache<String, Bitmap> mMemoryCache;
  private static final int DISK_CACHE_SIZE = 1024 * 1024 * 20; // 10MB
  private static final String DISK_CACHE_SUBDIR = "thumbnails";
  public DiskLruCache mDiskCache;
  private static MyApplication myapp;
 
  /** 圖片加載隊(duì)列,后進(jìn)先出 */
  private Stack<ImageRef> mImageQueue = new Stack<ImageRef>();
 
  /** 圖片請(qǐng)求隊(duì)列,先進(jìn)先出,用于存放已發(fā)送的請(qǐng)求。 */
  private Queue<ImageRef> mRequestQueue = new LinkedList<ImageRef>();
 
  /** 圖片加載線程消息處理器 */
  private Handler mImageLoaderHandler;
 
  /** 圖片加載線程是否就緒 */
  private boolean mImageLoaderIdle = true;
 
  /** 請(qǐng)求圖片 */
  private static final int MSG_REQUEST = 1;
  /** 圖片加載完成 */
  private static final int MSG_REPLY = 2;
  /** 中止圖片加載線程 */
  private static final int MSG_STOP = 3;
  /** 如果圖片是從網(wǎng)絡(luò)加載,則應(yīng)用漸顯動(dòng)畫,如果從緩存讀出則不應(yīng)用動(dòng)畫 */
  private boolean isFromNet = true;
 
  /**
   * 獲取單例,只能在UI線程中使用。
   * 
   * @param context
   * @return
   */
  public static ImageManager2 from(Context context) {
 
    // 如果不在ui線程中,則拋出異常
    if (Looper.myLooper() != Looper.getMainLooper()) {
      throw new RuntimeException("Cannot instantiate outside UI thread.");
    }
 
    if (myapp == null) {
      myapp = (MyApplication) context.getApplicationContext();
    }
 
    if (imageManager == null) {
      imageManager = new ImageManager2(myapp);
    }
 
    return imageManager;
  }
 
  /**
   * 私有構(gòu)造函數(shù),保證單例模式
   * 
   * @param context
   */
  private ImageManager2(Context context) {
    int memClass = ((ActivityManager) context
        .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
    memClass = memClass > 32 ? 32 : memClass;
    // 使用可用內(nèi)存的1/8作為圖片緩存
    final int cacheSize = 1024 * 1024 * memClass / 8;
 
    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
 
      protected int sizeOf(String key, Bitmap bitmap) {
        return bitmap.getRowBytes() * bitmap.getHeight();
      }
 
    };
 
    File cacheDir = DiskLruCache
        .getDiskCacheDir(context, DISK_CACHE_SUBDIR);
    mDiskCache = DiskLruCache.openCache(context, cacheDir, DISK_CACHE_SIZE);
 
  }
 
  /**
   * 存放圖片信息
   */
  class ImageRef {
 
    /** 圖片對(duì)應(yīng)ImageView控件 */
    ImageView imageView;
    /** 圖片URL地址 */
    String url;
    /** 圖片緩存路徑 */
    String filePath;
    /** 默認(rèn)圖資源ID */
    int resId;
    int width = 0;
    int height = 0;
 
    /**
     * 構(gòu)造函數(shù)
     * 
     * @param imageView
     * @param url
     * @param resId
     * @param filePath
     */
    ImageRef(ImageView imageView, String url, String filePath, int resId) {
      this.imageView = imageView;
      this.url = url;
      this.filePath = filePath;
      this.resId = resId;
    }
 
    ImageRef(ImageView imageView, String url, String filePath, int resId,
        int width, int height) {
      this.imageView = imageView;
      this.url = url;
      this.filePath = filePath;
      this.resId = resId;
      this.width = width;
      this.height = height;
    }
 
  }
 
  /**
   * 顯示圖片
   * 
   * @param imageView
   * @param url
   * @param resId
   */
  public void displayImage(ImageView imageView, String url, int resId) {
    if (imageView == null) {
      return;
    }
    if (imageView.getTag() != null
        && imageView.getTag().toString().equals(url)) {
      return;
    }
    if (resId >= 0) {
      if (imageView.getBackground() == null) {
        imageView.setBackgroundResource(resId);
      }
      imageView.setImageDrawable(null);
 
    }
    if (url == null || url.equals("")) {
      return;
    }
 
    // 添加url tag
    imageView.setTag(url);
 
    // 讀取map緩存
    Bitmap bitmap = mMemoryCache.get(url);
    if (bitmap != null) {
      setImageBitmap(imageView, bitmap, false);
      return;
    }
 
    // 生成文件名
    String filePath = urlToFilePath(url);
    if (filePath == null) {
      return;
    }
 
    queueImage(new ImageRef(imageView, url, filePath, resId));
  }
 
  /**
   * 顯示圖片固定大小圖片的縮略圖,一般用于顯示列表的圖片,可以大大減小內(nèi)存使用
   * 
   * @param imageView 加載圖片的控件
   * @param url 加載地址
   * @param resId 默認(rèn)圖片
   * @param width 指定寬度
   * @param height 指定高度
   */
  public void displayImage(ImageView imageView, String url, int resId,
      int width, int height) {
    if (imageView == null) {
      return;
    }
    if (resId >= 0) {
 
      if (imageView.getBackground() == null) {
        imageView.setBackgroundResource(resId);
      }
      imageView.setImageDrawable(null);
 
    }
    if (url == null || url.equals("")) {
      return;
    }
 
    // 添加url tag
    imageView.setTag(url);
    // 讀取map緩存
    Bitmap bitmap = mMemoryCache.get(url + width + height);
    if (bitmap != null) {
      setImageBitmap(imageView, bitmap, false);
      return;
    }
 
    // 生成文件名
    String filePath = urlToFilePath(url);
    if (filePath == null) {
      return;
    }
 
    queueImage(new ImageRef(imageView, url, filePath, resId, width, height));
  }
 
  /**
   * 入隊(duì),后進(jìn)先出
   * 
   * @param imageRef
   */
  public void queueImage(ImageRef imageRef) {
 
    // 刪除已有ImageView
    Iterator<ImageRef> iterator = mImageQueue.iterator();
    while (iterator.hasNext()) {
      if (iterator.next().imageView == imageRef.imageView) {
        iterator.remove();
      }
    }
 
    // 添加請(qǐng)求
    mImageQueue.push(imageRef);
    sendRequest();
  }
 
  /**
   * 發(fā)送請(qǐng)求
   */
  private void sendRequest() {
 
    // 開(kāi)啟圖片加載線程
    if (mImageLoaderHandler == null) {
      HandlerThread imageLoader = new HandlerThread("image_loader");
      imageLoader.start();
      mImageLoaderHandler = new ImageLoaderHandler(
          imageLoader.getLooper());
    }
 
    // 發(fā)送請(qǐng)求
    if (mImageLoaderIdle && mImageQueue.size() > 0) {
      ImageRef imageRef = mImageQueue.pop();
      Message message = mImageLoaderHandler.obtainMessage(MSG_REQUEST,
          imageRef);
      mImageLoaderHandler.sendMessage(message);
      mImageLoaderIdle = false;
      mRequestQueue.add(imageRef);
    }
  }
 
  /**
   * 圖片加載線程
   */
  class ImageLoaderHandler extends Handler {
 
    public ImageLoaderHandler(Looper looper) {
      super(looper);
    }
 
    public void handleMessage(Message msg) {
      if (msg == null)
        return;
 
      switch (msg.what) {
 
      case MSG_REQUEST: // 收到請(qǐng)求
        Bitmap bitmap = null;
        Bitmap tBitmap = null;
        if (msg.obj != null && msg.obj instanceof ImageRef) {
 
          ImageRef imageRef = (ImageRef) msg.obj;
          String url = imageRef.url;
          if (url == null)
            return;
          // 如果本地url即讀取sd相冊(cè)圖片,則直接讀取,不用經(jīng)過(guò)DiskCache
          if (url.toLowerCase().contains("dcim")) {
 
            tBitmap = null;
            BitmapFactory.Options opt = new BitmapFactory.Options();
            opt.inSampleSize = 1;
            opt.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(url, opt);
            int bitmapSize = opt.outHeight * opt.outWidth * 4;
            opt.inSampleSize = bitmapSize / (1000 * 2000);
            opt.inJustDecodeBounds = false;
            tBitmap = BitmapFactory.decodeFile(url, opt);
            if (imageRef.width != 0 && imageRef.height != 0) {
              bitmap = ThumbnailUtils.extractThumbnail(tBitmap,
                  imageRef.width, imageRef.height,
                  ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
              isFromNet = true;
            } else {
              bitmap = tBitmap;
              tBitmap = null;
            }
 
          } else
            bitmap = mDiskCache.get(url);
 
          if (bitmap != null) {
            // ToolUtil.log("從disk緩存讀取");
            // 寫入map緩存
            if (imageRef.width != 0 && imageRef.height != 0) {
              if (mMemoryCache.get(url + imageRef.width
                  + imageRef.height) == null)
                mMemoryCache.put(url + imageRef.width
                    + imageRef.height, bitmap);
            } else {
              if (mMemoryCache.get(url) == null)
                mMemoryCache.put(url, bitmap);
            }
 
          } else {
            try {
              byte[] data = loadByteArrayFromNetwork(url);
 
              if (data != null) {
 
                BitmapFactory.Options opt = new BitmapFactory.Options();
                opt.inSampleSize = 1;
 
                opt.inJustDecodeBounds = true;
                BitmapFactory.decodeByteArray(data, 0,
                    data.length, opt);
                int bitmapSize = opt.outHeight * opt.outWidth
                    * 4;// pixels*3 if it's RGB and pixels*4
                      // if it's ARGB
                if (bitmapSize > 1000 * 1200)
                  opt.inSampleSize = 2;
                opt.inJustDecodeBounds = false;
                tBitmap = BitmapFactory.decodeByteArray(data,
                    0, data.length, opt);
                if (imageRef.width != 0 && imageRef.height != 0) {
                  bitmap = ThumbnailUtils
                      .extractThumbnail(
                          tBitmap,
                          imageRef.width,
                          imageRef.height,
                          ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
                } else {
                  bitmap = tBitmap;
                  tBitmap = null;
                }
 
                if (bitmap != null && url != null) {
                  // 寫入SD卡
                  if (imageRef.width != 0
                      && imageRef.height != 0) {
                    mDiskCache.put(url + imageRef.width
                        + imageRef.height, bitmap);
                    mMemoryCache.put(url + imageRef.width
                        + imageRef.height, bitmap);
                  } else {
                    mDiskCache.put(url, bitmap);
                    mMemoryCache.put(url, bitmap);
                  }
                  isFromNet = true;
                }
              }
            } catch (OutOfMemoryError e) {
            }
 
          }
 
        }
 
        if (mImageManagerHandler != null) {
          Message message = mImageManagerHandler.obtainMessage(
              MSG_REPLY, bitmap);
          mImageManagerHandler.sendMessage(message);
        }
        break;
 
      case MSG_STOP: // 收到終止指令
        Looper.myLooper().quit();
        break;
 
      }
    }
  }
 
  /** UI線程消息處理器 */
  private Handler mImageManagerHandler = new Handler() {
 
    @Override
    public void handleMessage(Message msg) {
      if (msg != null) {
        switch (msg.what) {
 
        case MSG_REPLY: // 收到應(yīng)答
 
          do {
            ImageRef imageRef = mRequestQueue.remove();
 
            if (imageRef == null)
              break;
 
            if (imageRef.imageView == null
                || imageRef.imageView.getTag() == null
                || imageRef.url == null)
              break;
 
            if (!(msg.obj instanceof Bitmap) || msg.obj == null) {
              break;
            }
            Bitmap bitmap = (Bitmap) msg.obj;
 
            // 非同一ImageView
            if (!(imageRef.url).equals((String) imageRef.imageView
                .getTag())) {
              break;
            }
 
            setImageBitmap(imageRef.imageView, bitmap, isFromNet);
            isFromNet = false;
 
          } while (false);
 
          break;
        }
      }
      // 設(shè)置閑置標(biāo)志
      mImageLoaderIdle = true;
 
      // 若服務(wù)未關(guān)閉,則發(fā)送下一個(gè)請(qǐng)求。
      if (mImageLoaderHandler != null) {
        sendRequest();
      }
    }
  };
 
  /**
   * 添加圖片顯示漸現(xiàn)動(dòng)畫
   * 
   */
  private void setImageBitmap(ImageView imageView, Bitmap bitmap,
      boolean isTran) {
    if (isTran) {
      final TransitionDrawable td = new TransitionDrawable(
          new Drawable[] {
              new ColorDrawable(android.R.color.transparent),
              new BitmapDrawable(bitmap) });
      td.setCrossFadeEnabled(true);
      imageView.setImageDrawable(td);
      td.startTransition(300);
    } else {
      imageView.setImageBitmap(bitmap);
    }
  }
 
  /**
   * 從網(wǎng)絡(luò)獲取圖片字節(jié)數(shù)組
   * 
   * @param url
   * @return
   */
  private byte[] loadByteArrayFromNetwork(String url) {
 
    try {
 
      HttpGet method = new HttpGet(url);
      HttpResponse response = myapp.getHttpClient().execute(method);
      HttpEntity entity = response.getEntity();
      return EntityUtils.toByteArray(entity);
 
    } catch (Exception e) {
      return null;
    }
 
  }
 
  /**
   * 根據(jù)url生成緩存文件完整路徑名
   * 
   * @param url
   * @return
   */
  public String urlToFilePath(String url) {
 
    // 擴(kuò)展名位置
    int index = url.lastIndexOf('.');
    if (index == -1) {
      return null;
    }
 
    StringBuilder filePath = new StringBuilder();
 
    // 圖片存取路徑
    filePath.append(myapp.getCacheDir().toString()).append('/');
 
    // 圖片文件名 
    filePath.append(MD5.Md5(url)).append(url.substring(index));
 
    return filePath.toString();
  }
 
  /**
   * Activity#onStop后,ListView不會(huì)有殘余請(qǐng)求。
   */
  public void stop() {
 
    // 清空請(qǐng)求隊(duì)列
    mImageQueue.clear();
 
  }
 
}

這里就是給出了異步加載、內(nèi)存緩存和硬盤緩存的解決方案,希望對(duì)大家的學(xué)習(xí)有所幫助。

相關(guān)文章

  • OKHttp使用詳解

    OKHttp使用詳解

    OkHttp 是一套處理 HTTP 網(wǎng)絡(luò)請(qǐng)求的依賴庫(kù),由 Square 公司設(shè)計(jì)研發(fā)并開(kāi)源,目前可以在 Java 和 Kotlin 中使用,這篇文章主要介紹了OKHttp詳解,需要的朋友可以參考下
    2024-01-01
  • Android實(shí)現(xiàn)沉浸式狀態(tài)欄功能

    Android實(shí)現(xiàn)沉浸式狀態(tài)欄功能

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)沉浸式狀態(tài)欄功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • Pagerslidingtabstrip菜單標(biāo)題欄制作方法

    Pagerslidingtabstrip菜單標(biāo)題欄制作方法

    這篇文章主要為大家詳細(xì)介紹了Pagerslidingtabstrip菜單標(biāo)題欄的制作方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • Android 使用Kotlin自定義View的方法教程

    Android 使用Kotlin自定義View的方法教程

    最近想加強(qiáng)一下自定義view方面的學(xué)習(xí),正好也在學(xué)習(xí)Kotlin,所以就嘗試著用Kotlin寫一下簡(jiǎn)單的自定義view,下面這篇文章主要給大家介紹了關(guān)于Android使用Kotlin自定義View的方法教程,需要的朋友可以參考下。
    2017-12-12
  • android自定義開(kāi)關(guān)控件-SlideSwitch的實(shí)例

    android自定義開(kāi)關(guān)控件-SlideSwitch的實(shí)例

    本篇文章主要介紹了android自定義開(kāi)關(guān)控件-SlideSwitch的實(shí)例,實(shí)現(xiàn)了手機(jī)控件開(kāi)關(guān)的功能,感興趣的小伙伴們可以參考一下。
    2016-11-11
  • Android ViewFlipper的詳解及實(shí)例

    Android ViewFlipper的詳解及實(shí)例

    這篇文章主要介紹了Android ViewFlipper的詳解及實(shí)例的相關(guān)資料,通過(guò)本文希望能幫助大家理解這部分內(nèi)容,需要的朋友可以參考下
    2017-08-08
  • Android Lock鎖實(shí)現(xiàn)原理詳細(xì)分析

    Android Lock鎖實(shí)現(xiàn)原理詳細(xì)分析

    這篇文章主要介紹了Android Lock鎖實(shí)現(xiàn)原理,Lock接口的實(shí)現(xiàn)類提供了比使用synchronized關(guān)鍵字更加靈活和廣泛的鎖定對(duì)象操作,而且是以面向?qū)ο蟮姆绞竭M(jìn)行對(duì)象加鎖
    2023-02-02
  • Android XML數(shù)據(jù)的三種解析方式

    Android XML數(shù)據(jù)的三種解析方式

    這篇文章主要為大家詳細(xì)介紹了Android XML數(shù)據(jù)的三種解析方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Android 控制車載藍(lán)牙播放音樂(lè)詳解流程

    Android 控制車載藍(lán)牙播放音樂(lè)詳解流程

    本篇文章介紹了手機(jī)端音樂(lè)暫停和播放狀態(tài),從服務(wù)端告訴客戶端、設(shè)備端實(shí)現(xiàn)暫停、播放、上一首、下一首等功能的實(shí)現(xiàn),通讀本篇對(duì)大家的學(xué)習(xí)或工作具有一定的價(jià)值,需要的朋友可以參考下
    2021-10-10
  • Android_UI 仿QQ側(cè)滑菜單效果的實(shí)現(xiàn)

    Android_UI 仿QQ側(cè)滑菜單效果的實(shí)現(xiàn)

    相信大家對(duì)QQ側(cè)滑菜單的效果已經(jīng)不陌生了吧,側(cè)滑進(jìn)入個(gè)人頭像一側(cè),進(jìn)行對(duì)頭像的更改,我的收藏,QQ錢包,我的文件等一系列的操作,下面小編給大家分享Android_UI 仿QQ側(cè)滑菜單效果的實(shí)現(xiàn),一起看看吧
    2017-04-04

最新評(píng)論