1. 程式人生 > >手寫圖片快取框架 ImageLoader

手寫圖片快取框架 ImageLoader

圖片快取是App開發中最常見的,本篇博文給大家帶來自己手寫的圖片快取框,大致的思路很簡單,首先從記憶體中獲取圖片,如果記憶體中沒有,就從手機本地進行獲取,如果還沒有,就從網路訪問進行獲取。
所以,我們在ImageLoader中只需要暴露一個方法loadImage(),外部只需要呼叫這個方法就可以完成圖片快取的所以邏輯

//載入圖片到對應的控制元件
public void loadImage(String key, ImageView view) {

    synchronized (view) {
        this.imageView = view;
        //檢查快取裡是否有
Bitmap bitmap = getFromCache(key); if (bitmap != null) { //快取存在,直接顯示 view.setImageBitmap(bitmap); } else { //網路進行下載 /*view.setBackgroundDrawable(drawable);*/ view.setBackgroundDrawable(new ColorDrawable(Color.GRAY)); ImageAsycTask task = new
ImageAsycTask(view); task.execute(key); } } }

這裡,我將從記憶體中和本地獲取圖片的邏輯都統一放在getFromCache()方法中,這裡值得一提的是,當記憶體中沒有,本地有該圖片的時候,還會將這個圖片放入LinkedHashMap中,讓這個圖片在LinkedHashMap中處於最新的位置,不至於被回收。

private Bitmap getFromCache(String key) {
    //檢查記憶體軟引用
    synchronized (firstHashMap) {
        if
(firstHashMap.get(key) != null) { Bitmap bitmap = firstHashMap.get(key).get(); if (bitmap != null) { //更新一下,因為Lru演算法會預設清除最老的選項 firstHashMap.put(key, new SoftReference<Bitmap>(bitmap)); return bitmap; } } } //檢查磁碟 Bitmap bitmap = getFromLocal(key); if (bitmap != null) { //更新一下,因為Lru演算法會預設清除最老的選項 firstHashMap.put(key, new SoftReference<Bitmap>(bitmap)); return bitmap; } return null; }

在記憶體中,我使用了一個LinkedHashMap

private static LinkedHashMap<String, SoftReference<Bitmap>> firstHashMap = new LinkedHashMap<String, SoftReference<Bitmap>>(MAX_LENGTH) {
    @Override
    protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
        if (this.size() > MAX_LENGTH) {
            //返回true,表示移除最老的
            return true;
        }
            //往磁碟進行新增
            diskCache(eldest.getKey(), eldest.getValue());
            return false;
    }

};

這裡內部的removeEldestEntry()方法內部如果返回true,會預設移除掉最舊的一個成員,返回false表示不移除,同時還會把圖片放入到手機本地中,這個邏輯通過diskCache()方法實現的,這裡圖片在本地中名字使用md5加密後的名字

//  把圖片快取到本地磁碟
private static void diskCache(String key, SoftReference<Bitmap> value) {
    //訊息摘要演算法
    Bitmap bitmap;
    FileOutputStream os = null;
    try {
        String fileName = MD5Utils.md5(key, "utf-8");
        String path = mContext.getCacheDir().getAbsolutePath() + File.separator + fileName;

        os = new FileOutputStream(new File(path));
        if (value.get() != null) {
            value.get().compress(Bitmap.CompressFormat.JPEG, 80, os);

        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}

如果本地快取中沒有,會通過getFromLocal(key)方法,從手機本地中進行獲取

//檢查sd卡里是否有
private Bitmap getFromLocal(String key) {
    InputStream is = null;
    try {
        String filname = MD5Utils.md5(key, "utf-8");
        if (filname == null) {
            return null;

        } else {
            String path = mContext.getCacheDir().getAbsolutePath() + File.separator + filname;
            is = new FileInputStream(new File(path));
            Bitmap bitmap = BitmapFactory.decodeStream(is);
            return bitmap;
        }
    } catch (Exception e) {
        e.printStackTrace();
        return null;

    } finally {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

如果本地和記憶體都沒有的話,那麼就從網路進行獲取,這裡使用了AsyncTask

class ImageAsycTask extends AsyncTask<String, Void, Bitmap> {
    private ImageView imagView;
    private String key;

    public ImageAsycTask(ImageView imageView) {
        this.imagView = imageView;
    }



    @Override
    protected Bitmap doInBackground(String... strings) {
        this.key = strings[0];
        Log.i(TAG,key);
        Bitmap bitmap = downLoad(key);

        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {

        super.onPostExecute(bitmap);
        if (bitmap != null) {
            addCache(key, bitmap);
            /*Log.i("11",bitmap.toString());*/
            imagView.setImageBitmap(bitmap);
        }
    }
}

其中downLoad()方法就是訪問網路獲取圖片的方法

private Bitmap downLoad(String key) {
    final Bitmap[] bitmap = new Bitmap[1];
    mHttpClient = new OkHttpClient();
    Request request = new Request.Builder().url(key).build();
    mHttpClient.newCall(request).enqueue(new Callback() {



        @Override
        public void onFailure(Call call, IOException e) {
            Log.i("TAG", "網路訪問失敗了");

        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            InputStream is = response.body().byteStream();
            bitmap[0] = BitmapFactory.decodeStream(is);
            Log.d("okHttp", bitmap[0].toString());
                   }
    });
    return bitmap[0];


}

圖片下載完成之後,我們會將其讀寫到記憶體中,並顯示在view上,這個view是通過AsyncTask的建構函式傳進來的

private void addCache(String key, Bitmap bitmap) {
    if (bitmap != null) {
        synchronized (firstHashMap) {
            firstHashMap.put(key, new SoftReference<Bitmap>(bitmap));
        }
    }
}

這樣這個圖片快取框架就寫好了,我們就單純的在MainActivity中訪問網路進行顯示來驗證我們的框架,佈局太簡單就不貼了

public class MainActivity extends AppCompatActivity {
    String url = "http://7mno4h.com2.z0.glb.qiniucdn.com/560bd9b6Nc4b5cbfe.jpg";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImageView imageView  = (ImageView) findViewById(R.id.image);
        ImageLoader imageLoader = ImageLoader.getmInstance(this);
        imageLoader.loadImage(url,imageView);
    }
}

執行專案,效果如下:
這裡寫圖片描述