安卓網絡請求圖片到圖片的三級緩存技術(內存緩存,本地緩存,網絡緩存)


安卓網絡請求圖片,對於我們來說並不陌生,因為每個應用都有可能會用到這一技術。通常情況下,我們第一次都是從網絡上請求圖片資源,然后將

圖片資源保存到內存和本地,下一次動態顯示圖片的時候就不需要再從網絡上請求圖片資源了,直接從本地或者內存中獲取就可以了。這就涉及到圖片

的三級緩存技術,分別是內存緩存,本地緩存,網絡緩存。

 

緩存的流程圖:

首先我們定義一個類叫ClassLoader:

package com.jsako.showprodinfodemo;

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

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.widget.ImageView;

/**
 * 圖片加載類
 * 
 * @author Administrator
 * 
 */
public class ImageLoader {
    private Context context;
    private int loadingImage;
    private int errorImage;
    private LruCache<String, Bitmap> mapCache;
    public ImageLoader(Context context, int loadingImage, int errorImage) {
        this.context = context;
        this.loadingImage = loadingImage;
        this.errorImage = errorImage;
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        int mCacheSize = maxMemory / 8;
        mapCache = new LruCache<String, Bitmap>(mCacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight();
            }
        };
    }

    public void getAndSetImage(String imagePath, ImageView iv_image) {
        //保存當前的ImageView對應的imagepath
        iv_image.setTag(imagePath);
        iv_image.setImageResource(loadingImage);
        /*
         * 從第一級緩存中找對應imagePath的圖片 如果第一級緩存有對應圖片,顯示! 如果第一級緩存沒有圖片,從第二級緩存中找
         */
        Bitmap bitmap = getImageByFirstCache(imagePath);
        if (bitmap != null) {
            iv_image.setImageBitmap(bitmap);
            System.out.println("從一級緩存中找到");
            return;
        }
        /*
         * 從第二級緩存中找對應的圖片 如果有,則緩存到第一緩存中 如果沒有,則從第三集緩存中找
         */
        bitmap = getImageBySecondCache(imagePath);
        if (bitmap != null) {
            iv_image.setImageBitmap(bitmap);
            cacheInFirst(imagePath, bitmap);
            System.out.println("從二級緩存中找到");
            return;
        }

        /*
         * 從第三級緩存中找對應的圖片 如果有,則緩存到第一、二緩存中 如果沒有,則顯示錯誤的圖片
         */
        loadImageByThridCache(imagePath, iv_image);
    }

    /**
     * 將圖片緩存到一級緩存
     * 
     * @param imagePath
     *            圖片的url
     * @param bitmap
     */
    private void cacheInFirst(String imagePath, Bitmap bitmap) {

        mapCache.put(imagePath, bitmap);
    }

    /**
     * 從三級緩存中尋找圖片
     * 
     * @param imagePath
     *            圖片的url
     * @param
     * @return
     */
    private void loadImageByThridCache(final String imagePath,
            final ImageView iv_image) {
        new AsyncTask<String, Void, Bitmap>() {
            /**
             * 開啟異步任務前調用
             */
            @Override
            protected void onPreExecute() {

            }

            /**
             * 異步任務完成后調用
             */
            @Override
            protected void onPostExecute(Bitmap result) {
                String nowImagePath=(String) iv_image.getTag();
                if(!nowImagePath.equals(imagePath)){
                    //如果當前請求的圖片路徑和需要顯示的圖片路徑不一致的話,就不顯示圖片
                    System.out.println("不顯示圖片了");
                    return;
                }
                
                if (result != null) {
                    iv_image.setImageBitmap(result);
                } else {
                    iv_image.setImageResource(errorImage);
                }
            }

            /**
             * 后台進行異步任務
             */
            @Override
            protected Bitmap doInBackground(String... params) {
                String nowImagePath=(String) iv_image.getTag();
                if(!nowImagePath.equals(params[0])){
                    //如果當前請求的圖片路徑和需要顯示的圖片路徑不一致的話,就不進行網絡請求了
                    System.out.println("不進行網絡請求了");
                    return null;
                }
                String url = params[0];
                HttpURLConnection conn = null;
                try {
                    URL mUrl = new URL(url);
                    conn = (HttpURLConnection) mUrl.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setReadTimeout(6000);
                    conn.setConnectTimeout(6000);
                    conn.setDoInput(true);
                    conn.connect();

                    int code = conn.getResponseCode();
                    if (code == 200) {
                        InputStream in = conn.getInputStream();
                        Bitmap bitmap = BitmapFactory.decodeStream(in);
                        // 在分線程中緩存圖片到一級和二級緩存
                        cacheInFirst(url, bitmap);
                        String imageName = url
                                .substring(url.lastIndexOf("/") + 1);
                        String fileName = context.getExternalFilesDir(null)
                                .getAbsolutePath() + "/" + imageName;
                        bitmap.compress(CompressFormat.JPEG, 50,
                                new FileOutputStream(fileName));
                        return bitmap;
                    } else {
                        return null;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                } finally {
                    if (conn != null) {
                        conn.disconnect();
                    }
                }
            }
        }.execute(imagePath);
    }

    /**
     * 從二級緩存中尋找圖片
     * 
     * @param imagePath
     *            圖片的url
     * @return
     */
    private Bitmap getImageBySecondCache(String imagePath) {
        String imageName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
        String fileName = context.getExternalFilesDir(null).getAbsolutePath()
                + "/" + imageName;
        return BitmapFactory.decodeFile(fileName);
    }

    /**
     * 從一級緩存中尋找圖片
     * 
     * @param imagePath
     *            圖片的url
     * @return
     */
    private Bitmap getImageByFirstCache(String imagePath) {
        return mapCache.get(imagePath);
    }

}

可以看到這個類的構造方法有三個,第一個是上下文,第二個是加載的圖片的時候所顯示的圖片對應的資源id,第三個是圖片加載失敗后所顯示的圖片對應的資源id。

這里有幾個關鍵點:

第一個是在內存緩存中,我們應該使用LruCache來緩存圖片,這個類會根據當前所存儲的圖片空間是否大過設定值,如果比設定值大就會自動釋放內存,

這樣就能防止內存溢出的問題。

 

第二個是在使用ListView異步加載圖片的時候,並且重用了convertView的時候會導致圖片錯位的現象。

導致圖片錯位的根本原因就是重用了convertView。

解決方案如下:

a. 每次getView()都將圖片的url保存到ImageView上: imageView.setTag(imagePath)
b. 在分線程准備請求服務器加載圖片之前, 比較准備加載圖片的url與ImageView中保存的最新圖片的url是同一個,
如果不是同一個, 當前加載圖片的任務不應該再執行
如果相同, 繼續執行加載遠程圖片
c. 在主線程准備顯示圖片之前, 比較加載到圖片的url與ImageView中保存的最新圖片的url是同一個
如果不是同一個, 不需要顯示此圖片
如果相同, 顯示圖片


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM