Android編程圖片加載類ImageLoader定義與用法實(shí)例分析
本文實(shí)例講述了Android編程圖片加載類ImageLoader定義與用法。分享給大家供大家參考,具體如下:
解析:
1)圖片加載使用單例模式,避免多次調(diào)用時(shí)產(chǎn)生死鎖
2)核心對(duì)象 LruCache
圖片加載時(shí)先判斷緩存里是否有圖片,如果有,就使用緩存里的
沒有就加載網(wǎng)絡(luò)的,然后置入緩存
3)使用了線程池ExecutorService mThreadPool技術(shù)
4)使用了Semaphore 信號(hào)來控制變量按照先后順序執(zhí)行,避免空指針的問題
如何使用:
在Adapter里加載圖片時(shí)
源碼:
/**
* @描述 圖片加載類
* @項(xiàng)目名稱 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;
/**
* 圖片緩存的核心對(duì)象
*/
private LruCache<String, Bitmap> mLruCache;
/**
* 線程池
*/
private ExecutorService mThreadPool;
private static final int DEAFULT_THREAD_COUNT = 1;
/**
* 隊(duì)列的調(diào)度方式
*/
private Type mType = Type.LIFO;
/**
* 任務(wù)隊(duì)列
*/
private LinkedList<Runnable> mTaskQueue;
/**
* 后臺(tái)輪詢線程
*/
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();
// 獲取我們應(yīng)用的最大可用內(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);
}
/**
* 初始化后臺(tái)輪詢線程
*/
private void initBackThread() {
// 后臺(tái)輪詢線程
mPoolThread = new Thread() {
@Override
public void run() {
Looper.prepare();
mPoolThreadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 線程池去取出一個(gè)任務(wù)進(jìn)行執(zhí)行
mThreadPool.execute(getTask());
try {
mSemaphoreThreadPool.acquire();
} catch (InterruptedException e) {
}
}
};
// 釋放一個(gè)信號(hào)量
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存儲(chǔ)路徑進(jìn)行比較
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ù),新建一個(gè)任務(wù)
*
* @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)// 檢測(cè)是否開啟硬盤緩存
{
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;
}
/**
* 從任務(wù)隊(duì)列取出一個(gè)方法
*
* @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) {
// 將每個(gè)字節(jié)與0xFF進(jìn)行與運(yùn)算,然后轉(zhuǎn)化為10進(jìn)制,然后借助于Integer再轉(zhuǎn)化為16進(jìn)制
tmp = Integer.toHexString(0xFF & b);
if (tmp.length() == 1)// 每個(gè)字節(jié)8為,轉(zhuǎn)為16進(jìn)制標(biāo)志,2個(gè)16進(jìn)制位
{
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ù)圖片需要顯示的寬和高對(duì)圖片進(jìn)行壓縮
*
* @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
* @項(xiàng)目名稱 App_News
* @包名 com.android.news.util
* @類名 ImageSizeUtil
* @author chenlin
* @date 2014-3-7 下午7:37:50
* @version 1.0
*/
public class ImageSizeUtil {
/**
* 根據(jù)需求的寬和高以及圖片實(shí)際的寬和高計(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獲適當(dāng)?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的實(shí)際寬度
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的實(shí)際高度
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的某個(gè)屬性值
*
* @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ā)入門與進(jìn)階教程》、《Android調(diào)試技巧與常見問題解決方法匯總》、《Android基本組件用法總結(jié)》、《Android視圖View技巧總結(jié)》、《Android布局layout技巧總結(jié)》及《Android控件用法總結(jié)》
希望本文所述對(duì)大家Android程序設(shè)計(jì)有所幫助。
- 詳解Android?GLide圖片加載常用幾種方法
- Android圖片加載框架Coil的詳細(xì)使用總結(jié)
- Android 官推 kotlin-first 的圖片加載庫——Coil的使用入門
- Android基于Glide v4.x的圖片加載進(jìn)度監(jiān)聽
- Android ListView實(shí)現(xiàn)ImageLoader圖片加載的方法
- Android中RecyclerView 滑動(dòng)時(shí)圖片加載的優(yōu)化
- Android圖片加載框架Glide的基本用法介紹
- Android圖片加載利器之Picasso基本用法
- 如何在Android中高效管理圖片加載
相關(guān)文章
Android 7.0開發(fā)獲取存儲(chǔ)設(shè)備信息的方法
這篇文章主要介紹了Android 7.0開發(fā)獲取存儲(chǔ)設(shè)備信息的方法,結(jié)合實(shí)例形式分析了Android7.0針對(duì)存儲(chǔ)設(shè)備信息的獲取、判斷操作方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-11-11
android開發(fā)中獲取手機(jī)分辨率大小的方法
不管是在我們的布局還是在實(shí)現(xiàn)代碼中進(jìn)行操控,我們的靈活性都不是局限于一個(gè)固定的數(shù)值,而是面對(duì)不同的手機(jī)對(duì)象都有一個(gè)適應(yīng)的數(shù)值。2013-04-04
Android自定義View實(shí)現(xiàn)圓形進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)圓形進(jìn)度條,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06
Android TextView中文本點(diǎn)擊文字跳轉(zhuǎn) (代碼簡(jiǎn)單)
用過微博Android手機(jī)端的朋友的都知道微博正文有時(shí)有一些高亮顯示的文本,如話題、提到的人等等,當(dāng)點(diǎn)擊這些文本時(shí)會(huì)跳到另外一個(gè)頁面(即另一個(gè)activity),下面就要來模仿微博的這個(gè)功能2016-01-01
Android中使用ScrollView實(shí)現(xiàn)滑動(dòng)到底部顯示加載更多
本文主要介紹了android利用ScrollView實(shí)現(xiàn)滑動(dòng)到底部顯示加載更多的示例代碼。具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-04-04
Android使用Recyclerview實(shí)現(xiàn)圖片水平自動(dòng)循環(huán)滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android使用Recyclerview實(shí)現(xiàn)圖片水平自動(dòng)循環(huán)滾動(dòng)效果,實(shí)現(xiàn)精彩的跑馬燈效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Android scrollview如何監(jiān)聽滑動(dòng)狀態(tài)
這篇文章主要介紹了Android scrollview監(jiān)聽滑動(dòng)狀態(tài)的實(shí)例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12

