您好,登錄后才能下訂單哦!
本文實例講述了Android編程圖片加載類ImageLoader定義與用法。分享給大家供大家參考,具體如下:
解析:
1)圖片加載使用單例模式,避免多次調用時產生死鎖
2)核心對象 LruCache
圖片加載時先判斷緩存里是否有圖片,如果有,就使用緩存里的
沒有就加載網絡的,然后置入緩存
3)使用了線程池ExecutorService mThreadPool技術
4)使用了Semaphore 信號來控制變量按照先后順序執行,避免空指針的問題
如何使用:
在Adapter里加載圖片時
源碼:
/** * @描述 圖片加載類 * @項目名稱 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; /** * 隊列的調度方式 */ 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(); // 獲取我們應用的最大可用內存 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(); } }; // 創建線程池 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) { // 線程池去取出一個任務進行執行 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; } /** * 根據path為imageview設置圖片 * * @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回調設置圖片 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); } }; }; } // 根據path在緩存中獲取bitmap Bitmap bm = getBitmapFromLruCache(path); if (bm != null) { refreashBitmap(path, imageView, bm); } else { addTask(buildTask(path, imageView, isFromNet)); } } /** * 根據傳入的參數,新建一個任務 * * @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())// 如果在緩存文件中發現 { 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 // 直接從網絡加載 { 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; } /** * 利用簽名輔助類,將字符串字節數組 * * @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) { // 將每個字節與0xFF進行與運算,然后轉化為10進制,然后借助于Integer再轉化為16進制 tmp = Integer.toHexString(0xFF & b); if (tmp.length() == 1)// 每個字節8為,轉為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); } } /** * 根據圖片需要顯示的寬和高對圖片進行壓縮 * * @param path * @param width * @param height * @return */ protected Bitmap decodeSampledBitmapFromPath(String path, int width, int height) { // 獲得圖片的寬和高,并不把圖片加載到內存中 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); } /** * 根據path在緩存中獲取bitmap * * @param key * @return */ private Bitmap getBitmapFromLruCache(String key) { return mLruCache.get(key); } private class ImgBeanHolder { Bitmap bitmap; ImageView imageView; String path; } }
相關工具類:
/** * @描述 獲取圖片大小工具類s * @項目名稱 App_News * @包名 com.android.news.util * @類名 ImageSizeUtil * @author chenlin * @date 2014-3-7 下午7:37:50 * @version 1.0 */ public class ImageSizeUtil { /** * 根據需求的寬和高以及圖片實際的寬和高計算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; } /** * 根據ImageView獲適當的壓縮的寬和高 * * @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; } }
更多關于Android相關內容感興趣的讀者可查看本站專題:《Android圖形與圖像處理技巧總結》、《Android開發入門與進階教程》、《Android調試技巧與常見問題解決方法匯總》、《Android基本組件用法總結》、《Android視圖View技巧總結》、《Android布局layout技巧總結》及《Android控件用法總結》
希望本文所述對大家Android程序設計有所幫助。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。