Android 的圖片異步請求加三級緩存 ACE

使用xUtils等框架是很方便,但今天要用代碼實現bitmapUtils 的功能,很簡單,java

1 AsyncTask請求一張圖片 android

####AsyncTask緩存

#####AsyncTask是線程池+handler的封裝 第一個泛型: 傳參的參數類型類型(和doInBackground一致) 第二個泛型:
#####更新進度的參數類型(和onProgressUpdate一致) 第三個泛型: 返回結果的參數類型(和onPostExecute一致,
#####和doInBackground返回類型一致)安全

看AsyncTask源碼:網絡

public abstract class AsyncTask<Params, Progress, Result> { private static final String LOG_TAG = "AsyncTask"; private static final int CORE_POOL_SIZE = 5; private static final int MAXIMUM_POOL_SIZE = 128; private static final int KEEP_ALIVE = 1; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } };

 

核心線程5 最大線程128  這是AsyncTask的線程池  而後經過handler發送消息 , 它內部實例化了一個靜態的自定義類 InternalHandler,這個類是繼承自 Handler 的,在這個自定義類中綁定了一個叫作 AsyncTaskResult 的對象,每次子線程須要通知主線程,就調用 sendToTarget 發送消息給 handler本身。而後在 handler 的 handleMessage 中 AsyncTaskResult 根據消息的類型不一樣(例如 MESSAGE_POST_PROGRESS 會更新進度條,MESSAGE_POST_CANCEL 取消任務)而作不一樣的操做,值得一提的是,這些操做都是在UI線程進行的,意味着,從子線程一旦須要和 UI 線程交互,內部自動調用了 handler 對象把消息放在了主線程了。app

 private static final InternalHandler sHandler = new InternalHandler();

mFuture = new FutureTask<Result>(mWorker) { @Override protected void More ...done() { Message message; Result result = null; try { result = get(); } catch (InterruptedException e) { android.util.Log.w(LOG_TAG, e); } catch (ExecutionException e) { throw new RuntimeException("An error occured while executing doInBackground()", e.getCause()); } catch (CancellationException e) { message = sHandler.obtainMessage(MESSAGE_POST_CANCEL, new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null)); message.sendToTarget(); return; } catch (Throwable t) { throw new RuntimeException("An error occured while executing " + "doInBackground()", t); } message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(AsyncTask.this, result)); message.sendToTarget(); } }; private static class InternalHandler extends Handler { @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) @Override public void More ...handleMessage(Message msg) { AsyncTaskResult result = (AsyncTaskResult) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: // There is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; case MESSAGE_POST_CANCEL: result.mTask.onCancelled(); break; } } }

 

 

下面看代碼 第一步咱們先請求一張圖片 並解析 註釋寫的很詳細了.框架

NetCacheUtils.java異步

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;

/**
 * 網絡緩存
 * 
 * @author Ace
 * @date 2016-02-18
 */
public class NetCacheUtils {

    private LocalCacheUtils mLocalUtils;
    private MemoryCacheUtils mMemoryUtils;

    public NetCacheUtils(LocalCacheUtils localUtils,
            MemoryCacheUtils memoryUtils) {
        mLocalUtils = localUtils;
        mMemoryUtils = memoryUtils;
    }

    public void getBitmapFromNet(ImageView imageView, String url) {
        BitmapTask task = new BitmapTask();
        task.execute(imageView, url);
    }

    /**
     * AsyncTask是線程池+handler的封裝 第一個泛型: 傳參的參數類型類型(和doInBackground一致) 第二個泛型:
     * 更新進度的參數類型(和onProgressUpdate一致) 第三個泛型: 返回結果的參數類型(和onPostExecute一致,
     * 和doInBackground返回類型一致)
     */
    class BitmapTask extends AsyncTask<Object, Integer, Bitmap> {

        private ImageView mImageView;
        private String url;

        // 主線程運行, 預加載
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        // 子線程運行, 異步加載邏輯在此方法中處理
        @Override
        protected Bitmap doInBackground(Object... params) {
            mImageView = (ImageView) params[0];
            url = (String) params[1];
            mImageView.setTag(url);// 將imageView和url綁定在一塊兒
            // publishProgress(values)//通知進度

            // 下載圖片
            return download(url);
        }

        // 主線程運行, 更新進度
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }

        // 主線程運行, 更新主界面
        @Override
        protected void onPostExecute(Bitmap result) {
            if (result != null) {
                // 判斷當前圖片是否就是imageView要的圖片, 防止listview重用致使的圖片錯亂的狀況出現
                String bindUrl = (String) mImageView.getTag();
                if (bindUrl.equals(url)) {
                    // 給imageView設置圖片
                    mImageView.setImageBitmap(result);
  // 將圖片保存在本地
                    mLocalUtils.setBitmapToLocal(result, url);

                    // 將圖片保存在內存
                    mMemoryUtils.setBitmapToMemory(url, result);
                }
            }
        }

    }

    /**
     * 下載圖片
     * 
     * @param url
     */
    public Bitmap download(String url) {
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) (new URL(url).openConnection());

            conn.setConnectTimeout(5000);
            conn.setReadTimeout(5000);
            conn.setRequestMethod("GET");

            conn.connect();

            int responseCode = conn.getResponseCode();
            if (responseCode == 200) {
                InputStream in = conn.getInputStream();
                // 將流轉化爲bitmap對象
                Bitmap bitmap = BitmapFactory.decodeStream(in);
                return bitmap;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }

        return null;
    }

}
MemoryCacheUtils.java  用到了LruCache 很簡單
我簡單翻譯下文檔:

* A cache that holds strong references to a limited number of values. Each time 

* a value is accessed, it is moved to the head of a queue. When a value is 

* added to a full cache, the value at the end of that queue is evicted and may  
* become eligible for garbage collection. 

* Cache保存一個強引用來限制內容數量,每當Item被訪問的時候,此Item就會移動到隊列的頭部。
* 當cache已滿的時候加入新的item時,在隊列尾部的item會被回收。

* <p>If your cached values hold resources that need to be explicitly released,
* override {@link #entryRemoved}.
* 若是你cache的某個值須要明確釋放,重寫entryRemoved()ide

 

* <p>By default, the cache size is measured in the number of entries. Override
* {@link #sizeOf} to size the cache in different units. For example, this cache
* is limited to 4MiB of bitmaps: 默認cache大小是測量的item的數量,重寫sizeof計算不一樣item的
* 大小。工具

  1.  {@code 
  2.  *   int cacheSize = 4 * 1024 * 1024; // 4MiB 
  3.  *   LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) { 
  4.  *       protected int sizeOf(String key, Bitmap value) { 
  5.  *           return value.getByteCount(); 
  6.  *       } 
  7.  *   }}

 

-------------------------------------------------------------------

<p>This class is thread-safe. Perform multiple cache operations atomically by
* synchronizing on the cache: <pre> {@code
* synchronized (cache) {
* if (cache.get(key) == null) {
* cache.put(key, value);
* }
* }}</pre>
* 他是線程安全的,自動地執行多個緩存操做而且加鎖

 

-------------------------

<p>This class does not allow null to be used as a key or value. A return
* value of null from {@link #get}, {@link #put} or {@link #remove} is
* unambiguous: the key was not in the cache.
* 不容許key或者value爲null
* 當get(),put(),remove()返回值爲null時,key相應的項不在cache中

 

最重要的大概就是以上幾點: 使用很簡單
來看代碼
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

/**
 * 內存緩存工具類
 * 
 * @author Ace
 * @date 2016-02-19
 */
public class MemoryCacheUtils {


    // Android 2.3 (API Level
    // 9)開始,垃圾回收器會更傾向於回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得再也不可靠,建議用LruCache,它是強引用

    private LruCache<String, Bitmap> mCache;

    public MemoryCacheUtils() {
        int maxMemory = (int) Runtime.getRuntime().maxMemory();// 獲取虛擬機分配的最大內存
                                                                // 16M
        // LRU 最近最少使用, 經過控制內存不要超過最大值(由開發者指定), 來解決內存溢出,就像上面翻譯的所說 若是cache滿了會清理最近最少使用的緩存對象
        mCache = new LruCache<String, Bitmap>(maxMemory / 8) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                // 計算一個bitmap的大小
                int size = value.getRowBytes() * value.getHeight();// 每一行的字節數乘以高度
                return size;
            }
        };
    }

    public Bitmap getBitmapFromMemory(String url) {

        return mCache.get(url);
    }

    public void setBitmapToMemory(String url, Bitmap bitmap) {

        mCache.put(url, bitmap);
    }

}
 

最後一級緩存 本地緩存 把網絡下載的圖片 文件名以MD5的形式保存到內存卡的制定目錄

/**
 * 本地緩存工具類
 * 
 * @author Ace
 * @date 2016-02-19
 */
public class LocalCacheUtils {

    // 圖片緩存的文件夾
    public static final String DIR_PATH = Environment
            .getExternalStorageDirectory().getAbsolutePath()
            + "/ace_bitmap_cache";

    public Bitmap getBitmapFromLocal(String url) {
        try {
            File file = new File(DIR_PATH, MD5Encoder.encode(url));

            if (file.exists()) {
                Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(
                        file));
                return bitmap;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public void setBitmapToLocal(Bitmap bitmap, String url) {
        File dirFile = new File(DIR_PATH);

        // 建立文件夾 文件夾不存在或者它不是文件夾 則建立一個文件夾.mkdirs,mkdir的區別在於假如文件夾有好幾層路徑的話,前者會建立缺失的父目錄 後者不會建立這些父目錄
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            dirFile.mkdirs();
        }

        try {
            File file = new File(DIR_PATH, MD5Encoder.encode(url));
            // 將圖片壓縮保存在本地,參1:壓縮格式;參2:壓縮質量(0-100);參3:輸出流
            bitmap.compress(CompressFormat.JPEG, 100,
                    new FileOutputStream(file));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

MD5Encoder 

import java.security.MessageDigest;

public class MD5Encoder {
    
    public static String encode(String string) throws Exception {
        byte[] hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10) {
                hex.append("0");
            }
            hex.append(Integer.toHexString(b & 0xFF));
        }
        return hex.toString();
    }
}

 

最後新建一個工具類來使用咱們上面的三個緩存工具類

/**
 * 三級緩存工具類
 * 
 * @author Ace
 * @date 2016-02-19
 */
public class MyBitmapUtils {

    // 網絡緩存工具類
    private NetCacheUtils mNetUtils;
    // 本地緩存工具類
    private LocalCacheUtils mLocalUtils;
    // 內存緩存工具類
    private MemoryCacheUtils mMemoryUtils;

    public MyBitmapUtils() {
        mMemoryUtils = new MemoryCacheUtils();
        mLocalUtils = new LocalCacheUtils();
        mNetUtils = new NetCacheUtils(mLocalUtils, mMemoryUtils);
    }

    public void display(ImageView imageView, String url) {
        // 設置默認加載圖片
        imageView.setImageResource(R.drawable.news_pic_default);

        // 先從內存緩存加載
        Bitmap bitmap = mMemoryUtils.getBitmapFromMemory(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            System.out.println("從內存讀取圖片啦...");
            return;
        }

        // 再從本地緩存加載
        bitmap = mLocalUtils.getBitmapFromLocal(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            System.out.println("從本地讀取圖片啦...");
            // 給內存設置圖片
            mMemoryUtils.setBitmapToMemory(url, bitmap);
            return;
        }

        // 從網絡緩存加載
        mNetUtils.getBitmapFromNet(imageView, url);
    }

}
相關文章
相關標籤/搜索