使用Android新式LruCache緩存圖片,基于線程池異步加載圖片

cm54 9年前發布 | 2K 次閱讀 Java Android

    import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.graphics.Bitmap;  
import android.graphics.BitmapFactory;  
import android.os.Handler;  
import android.os.Message;  
import android.support.v4.util.LruCache;  
import android.util.Log;  
import android.widget.ImageView;  

/* 
 * 使用Android新式LruCache緩存圖片,基于線程池異步加載圖片。  
 * 基本思路:開辟一個線程池下載網絡圖片,同時創建一個LruCache作為Android內存緩存混存圖片。 
 * 上層應用傳遞過來一個URL要求從該URL下載圖片時,首先檢查LruCache中是否存在以該URL為索引的緩存圖片, 
 * 若有,則直接從緩存中讀出來返回給上層應用;若沒有,此時再開辟線程下載,下載完成后將此bitmap埋入緩存。 
 * 備注:圖片的url作為緩存圖片時LruCache的 Key。 
 * LruCache在內存中的緩存模型為<K,V>。 
 */  

public class AsyncImageLoader {  

    private ExecutorService pool;  
    private Handler handler;  
    private ImageLoadedListener listener;  

    private final int WHAT = 0xe001;  

    // 默認的線程池容量  
    private int DEFAULT_TASK_NUMBER = 10;  

    // 網絡超時時間:30秒  
    private static int TIMEOUT = 30 * 1000;  

    private LruCache<String, Bitmap> mMemoryCache;  
    // 4MB緩存大小  
    private final int CACHE_SIZE = 4 * 1024 * 1024;  

    public AsyncImageLoader(int asyncTaskNumber) {  
        initialization(asyncTaskNumber);  
    }  

    public AsyncImageLoader() {  
        // 默認的構造函數初始化線程池容量為:TASK_NUMBER  
        initialization(DEFAULT_TASK_NUMBER);  
    }  

    // 初始化  
    private void initialization(int asyncTaskNumber) {  
        mMemoryCache = new LruCache<String, Bitmap>(CACHE_SIZE) {  
            @Override  
            protected int sizeOf(String key, Bitmap value) {  
                return value.getRowBytes() * value.getHeight();  
            }  
        };  

        // 創建容量為 asyncTaskNumber 的線程池。  
        pool = Executors.newFixedThreadPool(asyncTaskNumber);  

        handler = new Handler() {  
            @Override  
            public void handleMessage(Message message) {  
                switch (message.what) {  
                case WHAT:  
                    listener.imageLoaded((Bitmap) message.obj);  
                }  
            }  
        };  
    }  


    // 異步設置一個ImagView的Bitmap(該ImageView的Bitmap從網絡加載)  
    // 該方法為public,作為該工具類的外部調用接口。  
    public void asyncSetImageBitmap(String url, final ImageView view) {  
        download(url, new ImageLoadedListener() {  
            @Override  
            public void imageLoaded(Bitmap bitmap) {  
                view.setImageBitmap(bitmap);  
            }  
        });  
    }  

    // 異步的從一個URL下載一個Bitmap。  
    // 下載成功后,回調imageLoaded(Bitmap bitmap, String url);  
    // 該方法為public,作為該工具類的外部調用接口。  
    public void download(String url, ImageLoadedListener listener) {  
        this.listener = listener;  

        // 首先從緩存中檢查是否存在以url為key的bitmap。  
        // 若有,則直接從緩存中讀取使用,不再使用線程重復加載。  
        Bitmap bmp = mMemoryCache.get(url);  
        if (bmp != null) {  
            Log.d("讀取緩存", url + " 已經緩存,無須重復下載!");  
            sendResult(bmp);  
            return;  
        }  

        Thread t = new DownloadThread(url);  
        pool.execute(t);  
    }  


    // 開辟一個下載線程  
    private class DownloadThread extends Thread {  

        private String url;  

        public DownloadThread(String url) {  
            this.url = url;  
        }  

        @Override  
        public void run() {  
            Bitmap bmp = loadBitmapFromURL(url);  

            // 將新的bitmap埋入緩存  
            mMemoryCache.put(url, bmp);  

            sendResult(bmp);  
        }  
    }  

    // 發送消息通知:bitmap已經下載完成。  
    private void sendResult(Bitmap bitmap) {  
        Message message = handler.obtainMessage();  
        message.what = WHAT;  
        message.obj = bitmap;  
        handler.sendMessage(message);  
    }  

    // 回調函數  
    public interface ImageLoadedListener {  
        public void imageLoaded(Bitmap bitmap);  
    }  

    // 給定一個URL,從這個URL下載Bitmap  
    public static Bitmap loadBitmapFromURL(String url) {  
        if (!url.contains("http://")) {  
            url = "http://" + url;  
        }  

        Log.d("線程:" + Thread.currentThread().getId(), "開始下載: " + url);  

        Bitmap bmp = null;  
        try {  
            byte[] imageBytes = loadRawDataFromURL(url);  
            bmp = BitmapFactory.decodeByteArray(imageBytes, 0,  
                    imageBytes.length);  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  

        return bmp;  
    }  

    // 給定一個URL,從這個URL下載原始數據塊。  
    public static byte[] loadRawDataFromURL(String u) throws Exception {  
        URL url = new URL(u);  
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
        // 配置基礎網絡鏈接參數  
        conn.setConnectTimeout(TIMEOUT);  
        conn.setReadTimeout(TIMEOUT);  

        InputStream is = conn.getInputStream();  
        BufferedInputStream bis = new BufferedInputStream(is);  

        ByteArrayOutputStream baos = new ByteArrayOutputStream();  

        final int BUFFER_SIZE = 1024 * 5;  
        final int EOF = -1;  

        int c;  
        byte[] buf = new byte[BUFFER_SIZE];  

        while (true) {  
            c = bis.read(buf);  
            if (c == EOF)  
                break;  

            baos.write(buf, 0, c);  
        }  

        conn.disconnect();  
        is.close();  

        byte[] data = baos.toByteArray();  
        baos.flush();  

        return data;  
    }  
}  </pre> 


 本文由用戶 cm54 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!