溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android編程圖片加載類ImageLoader定義與用法實例分析

發(fā)布時間:2020-10-20 12:46:06 來源:腳本之家 閱讀:189 作者:LovooGod 欄目:移動開發(fā)

本文實例講述了Android編程圖片加載類ImageLoader定義與用法。分享給大家供大家參考,具體如下:

解析:

1)圖片加載使用單例模式,避免多次調(diào)用時產(chǎn)生死鎖
2)核心對象 LruCache

圖片加載時先判斷緩存里是否有圖片,如果有,就使用緩存里的

沒有就加載網(wǎng)絡(luò)的,然后置入緩存

3)使用了線程池ExecutorService mThreadPool技術(shù)
4)使用了Semaphore 信號來控制變量按照先后順序執(zhí)行,避免空指針的問題

如何使用:

在Adapter里加載圖片時

復制代碼 代碼如下:
ImageLoader.getInstance.loadImage("https://cache.yisu.com/upload/information/20200623/125/125059.jpg", mImageView, true);

源碼:

/**
 * @描述 圖片加載類
 * @項目名稱 App_News
 * @包名 com.android.news.tools
 * @類名 ImageLoader
 * @author chenlin
 * @date 2015-3-7 下午7:35:28
 * @version 1.0
 */
public class ImageLoader {
  private static ImageLoader mInstance;
  /**
   * 圖片緩存的核心對象
   */
  private LruCache<String, Bitmap> mLruCache;
  /**
   * 線程池
   */
  private ExecutorService mThreadPool;
  private static final int DEAFULT_THREAD_COUNT = 1;
  /**
   * 隊列的調(diào)度方式
   */
  private Type mType = Type.LIFO;
  /**
   * 任務隊列
   */
  private LinkedList<Runnable> mTaskQueue;
  /**
   * 后臺輪詢線程
   */
  private Thread mPoolThread;
  private Handler mPoolThreadHandler;
  /**
   * UI線程中的Handler
   */
  private Handler mUIHandler;
  private Semaphore mSemaphorePoolThreadHandler = new Semaphore(0);
  private Semaphore mSemaphoreThreadPool;
  private boolean isDiskCacheEnable = true;
  private static final String TAG = "ImageLoader";
  public enum Type {
    FIFO, LIFO;
  }
  private ImageLoader(int threadCount, Type type) {
    init(threadCount, type);
  }
  /**
   * 初始化
   *
   * @param threadCount
   * @param type
   */
  private void init(int threadCount, Type type) {
    initBackThread();
    // 獲取我們應用的最大可用內(nèi)存
    int maxMemory = (int) Runtime.getRuntime().maxMemory();
    int cacheMemory = maxMemory / 8;
    mLruCache = new LruCache<String, Bitmap>(cacheMemory) {
      @Override
      protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
      }
    };
    // 創(chuàng)建線程池
    mThreadPool = Executors.newFixedThreadPool(threadCount);
    mTaskQueue = new LinkedList<Runnable>();
    mType = type;
    mSemaphoreThreadPool = new Semaphore(threadCount);
  }
  /**
   * 初始化后臺輪詢線程
   */
  private void initBackThread() {
    // 后臺輪詢線程
    mPoolThread = new Thread() {
      @Override
      public void run() {
        Looper.prepare();
        mPoolThreadHandler = new Handler() {
          @Override
          public void handleMessage(Message msg) {
            // 線程池去取出一個任務進行執(zhí)行
            mThreadPool.execute(getTask());
            try {
              mSemaphoreThreadPool.acquire();
            } catch (InterruptedException e) {
            }
          }
        };
        // 釋放一個信號量
        mSemaphorePoolThreadHandler.release();
        Looper.loop();
      };
    };
    mPoolThread.start();
  }
  public static ImageLoader getInstance() {
    if (mInstance == null) {
      synchronized (ImageLoader.class) {
        if (mInstance == null) {
          mInstance = new ImageLoader(DEAFULT_THREAD_COUNT, Type.LIFO);
        }
      }
    }
    return mInstance;
  }
  public static ImageLoader getInstance(int threadCount, Type type) {
    if (mInstance == null) {
      synchronized (ImageLoader.class) {
        if (mInstance == null) {
          mInstance = new ImageLoader(threadCount, type);
        }
      }
    }
    return mInstance;
  }
  /**
   * 根據(jù)path為imageview設(shè)置圖片
   *
   * @param path
   * @param imageView
   */
  public void loadImage(final String path, final ImageView imageView, final boolean isFromNet) {
    imageView.setTag(path);
    if (mUIHandler == null) {
      mUIHandler = new Handler() {
        public void handleMessage(Message msg) {
          // 獲取得到圖片,為imageview回調(diào)設(shè)置圖片
          ImgBeanHolder holder = (ImgBeanHolder) msg.obj;
          Bitmap bm = holder.bitmap;
          ImageView imageview = holder.imageView;
          String path = holder.path;
          // 將path與getTag存儲路徑進行比較
          if (imageview.getTag().toString().equals(path)) {
            imageview.setImageBitmap(bm);
          }
        };
      };
    }
    // 根據(jù)path在緩存中獲取bitmap
    Bitmap bm = getBitmapFromLruCache(path);
    if (bm != null) {
      refreashBitmap(path, imageView, bm);
    } else {
      addTask(buildTask(path, imageView, isFromNet));
    }
  }
  /**
   * 根據(jù)傳入的參數(shù),新建一個任務
   *
   * @param path
   * @param imageView
   * @param isFromNet
   * @return
   */
  private Runnable buildTask(final String path, final ImageView imageView, final boolean isFromNet) {
    return new Runnable() {
      @Override
      public void run() {
        Bitmap bm = null;
        if (isFromNet) {
          File file = getDiskCacheDir(imageView.getContext(), md5(path));
          if (file.exists())// 如果在緩存文件中發(fā)現(xiàn)
          {
            Log.e(TAG, "find image :" + path + " in disk cache .");
            bm = loadImageFromLocal(file.getAbsolutePath(), imageView);
          } else {
            if (isDiskCacheEnable)// 檢測是否開啟硬盤緩存
            {
              boolean downloadState = DownloadImgUtils.downloadImgByUrl(path, file);
              if (downloadState)// 如果下載成功
              {
                Log.e(TAG,
                    "download image :" + path + " to disk cache . path is "
                        + file.getAbsolutePath());
                bm = loadImageFromLocal(file.getAbsolutePath(), imageView);
              }
            } else
            // 直接從網(wǎng)絡(luò)加載
            {
              Log.e(TAG, "load image :" + path + " to memory.");
              bm = DownloadImgUtils.downloadImgByUrl(path, imageView);
            }
          }
        } else {
          bm = loadImageFromLocal(path, imageView);
        }
        // 3、把圖片加入到緩存
        addBitmapToLruCache(path, bm);
        refreashBitmap(path, imageView, bm);
        mSemaphoreThreadPool.release();
      }
    };
  }
  private Bitmap loadImageFromLocal(final String path, final ImageView imageView) {
    Bitmap bm;
    // 加載圖片
    // 圖片的壓縮
    // 1、獲得圖片需要顯示的大小
    ImageSize imageSize = ImageSizeUtil.getImageViewSize(imageView);
    // 2、壓縮圖片
    bm = decodeSampledBitmapFromPath(path, imageSize.width, imageSize.height);
    return bm;
  }
  /**
   * 從任務隊列取出一個方法
   *
   * @return
   */
  private Runnable getTask() {
    if (mType == Type.FIFO) {
      return mTaskQueue.removeFirst();
    } else if (mType == Type.LIFO) {
      return mTaskQueue.removeLast();
    }
    return null;
  }
  /**
   * 利用簽名輔助類,將字符串字節(jié)數(shù)組
   *
   * @param str
   * @return
   */
  public String md5(String str) {
    byte[] digest = null;
    try {
      MessageDigest md = MessageDigest.getInstance("md5");
      digest = md.digest(str.getBytes());
      return bytes2hex02(digest);
    } catch (NoSuchAlgorithmException e) {
      e.printStackTrace();
    }
    return null;
  }
  /**
   * 方式二
   *
   * @param bytes
   * @return
   */
  public String bytes2hex02(byte[] bytes) {
    StringBuilder sb = new StringBuilder();
    String tmp = null;
    for (byte b : bytes) {
      // 將每個字節(jié)與0xFF進行與運算,然后轉(zhuǎn)化為10進制,然后借助于Integer再轉(zhuǎn)化為16進制
      tmp = Integer.toHexString(0xFF & b);
      if (tmp.length() == 1)// 每個字節(jié)8為,轉(zhuǎn)為16進制標志,2個16進制位
      {
        tmp = "0" + tmp;
      }
      sb.append(tmp);
    }
    return sb.toString();
  }
  private void refreashBitmap(final String path, final ImageView imageView, Bitmap bm) {
    Message message = Message.obtain();
    ImgBeanHolder holder = new ImgBeanHolder();
    holder.bitmap = bm;
    holder.path = path;
    holder.imageView = imageView;
    message.obj = holder;
    mUIHandler.sendMessage(message);
  }
  /**
   * 將圖片加入LruCache
   *
   * @param path
   * @param bm
   */
  protected void addBitmapToLruCache(String path, Bitmap bm) {
    if (getBitmapFromLruCache(path) == null) {
      if (bm != null)
        mLruCache.put(path, bm);
    }
  }
  /**
   * 根據(jù)圖片需要顯示的寬和高對圖片進行壓縮
   *
   * @param path
   * @param width
   * @param height
   * @return
   */
  protected Bitmap decodeSampledBitmapFromPath(String path, int width, int height) {
    // 獲得圖片的寬和高,并不把圖片加載到內(nèi)存中
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(path, options);
    options.inSampleSize = ImageSizeUtil.caculateInSampleSize(options, width, height);
    // 使用獲得到的InSampleSize再次解析圖片
    options.inJustDecodeBounds = false;
    Bitmap bitmap = BitmapFactory.decodeFile(path, options);
    return bitmap;
  }
  private synchronized void addTask(Runnable runnable) {
    mTaskQueue.add(runnable);
    // if(mPoolThreadHandler==null)wait();
    try {
      if (mPoolThreadHandler == null)
        mSemaphorePoolThreadHandler.acquire();
    } catch (InterruptedException e) {
    }
    mPoolThreadHandler.sendEmptyMessage(0x110);
  }
  /**
   * 獲得緩存圖片的地址
   *
   * @param context
   * @param uniqueName
   * @return
   */
  public File getDiskCacheDir(Context context, String uniqueName) {
    String cachePath;
    if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
      cachePath = context.getExternalCacheDir().getPath();
    } else {
      cachePath = context.getCacheDir().getPath();
    }
    return new File(cachePath + File.separator + uniqueName);
  }
  /**
   * 根據(jù)path在緩存中獲取bitmap
   *
   * @param key
   * @return
   */
  private Bitmap getBitmapFromLruCache(String key) {
    return mLruCache.get(key);
  }
  private class ImgBeanHolder {
    Bitmap bitmap;
    ImageView imageView;
    String path;
  }
}

相關(guān)工具類:

/**
 * @描述 獲取圖片大小工具類s
 * @項目名稱 App_News
 * @包名 com.android.news.util
 * @類名 ImageSizeUtil
 * @author chenlin
 * @date 2014-3-7 下午7:37:50
 * @version 1.0
 */
public class ImageSizeUtil {
  /**
   * 根據(jù)需求的寬和高以及圖片實際的寬和高計算SampleSize
   *
   * @param options
   * @param width
   * @param height
   * @return
   */
  public static int caculateInSampleSize(Options options, int reqWidth, int reqHeight) {
    int width = options.outWidth;
    int height = options.outHeight;
    int inSampleSize = 1;
    if (width > reqWidth || height > reqHeight) {
      int widthRadio = Math.round(width * 1.0f / reqWidth);
      int heightRadio = Math.round(height * 1.0f / reqHeight);
      inSampleSize = Math.max(widthRadio, heightRadio);
    }
    return inSampleSize;
  }
  /**
   * 根據(jù)ImageView獲適當?shù)膲嚎s的寬和高
   *
   * @param imageView
   * @return
   */
  public static ImageSize getImageViewSize(ImageView imageView) {
    ImageSize imageSize = new ImageSize();
    DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics();
    LayoutParams lp = imageView.getLayoutParams();
    int width = imageView.getWidth();// 獲取imageview的實際寬度
    if (lp != null) {
      if (width <= 0) {
        width = lp.width;// 獲取imageview在layout中聲明的寬度
      }
    }
    if (width <= 0) {
      // width = imageView.getMaxWidth();// 檢查最大值
      width = getImageViewFieldValue(imageView, "mMaxWidth");
    }
    if (width <= 0) {
      width = displayMetrics.widthPixels;
    }
    int height = imageView.getHeight();// 獲取imageview的實際高度
    if (lp != null) {
      if (height <= 0) {
        height = lp.height;// 獲取imageview在layout中聲明的寬度
      }
    }
    if (height <= 0) {
      height = getImageViewFieldValue(imageView, "mMaxHeight");// 檢查最大值
    }
    if (height <= 0) {
      height = displayMetrics.heightPixels;
    }
    imageSize.width = width;
    imageSize.height = height;
    return imageSize;
  }
  public static class ImageSize {
    public int width;
    public int height;
  }
  /**
   * 通過反射獲取imageview的某個屬性值
   *
   * @param object
   * @param fieldName
   * @return
   */
  private static int getImageViewFieldValue(Object object, String fieldName) {
    int value = 0;
    try {
      Field field = ImageView.class.getDeclaredField(fieldName);
      field.setAccessible(true);
      int fieldValue = field.getInt(object);
      if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
        value = fieldValue;
      }
    } catch (Exception e) {
    }
    return value;
  }
}

更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android圖形與圖像處理技巧總結(jié)》、《Android開發(fā)入門與進階教程》、《Android調(diào)試技巧與常見問題解決方法匯總》、《Android基本組件用法總結(jié)》、《Android視圖View技巧總結(jié)》、《Android布局layout技巧總結(jié)》及《Android控件用法總結(jié)》

希望本文所述對大家Android程序設(shè)計有所幫助。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI