Android緩存網(wǎng)絡(luò)圖片的實(shí)現(xiàn)

如果每次加載同一張圖片都要從網(wǎng)絡(luò)獲取,那代價(jià)實(shí)在太大了。所以同一張圖片只要從網(wǎng)絡(luò)獲取一次就夠了,然后在本地緩存起來,之后加載同一張圖片時(shí)就 從緩存中加載就可以了。從內(nèi)存緩存讀取圖片是最快的,但是因?yàn)閮?nèi)存容量有限,所以最好再加上文件緩存。文件緩存空間也不是無限大的,容量越大讀取效率越 低,因此可以設(shè)置一個(gè)限定大小比如10M,或者限定保存時(shí)間比如一天。
因此,加載圖片的流程應(yīng)該是:
1、先從內(nèi)存緩存中獲取,取到則返回,取不到則進(jìn)行下一步;
2、從文件緩存中獲取,取到則返回并更新到內(nèi)存緩存,取不到則進(jìn)行下一步;

3、從網(wǎng)絡(luò)下載圖片,并更新到內(nèi)存緩存和文件緩存。

接下來看內(nèi)存緩存類:ImageMemoryCache
public class ImageMemoryCache {
/**
* 從內(nèi)存讀取數(shù)據(jù)速度是最快的,為了更大限度使用內(nèi)存,這里使用了兩層緩存。
* 硬引用緩存不會(huì)輕易被回收,用來保存常用數(shù)據(jù),不常用的轉(zhuǎn)入軟引用緩存。
*/
private static final int SOFT_CACHE_SIZE = 15; //軟引用緩存容量
private static LruCache<String, Bitmap> mLruCache; //硬引用緩存
private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache; //軟引用緩存

public ImageMemoryCache(Context context) {
    int memClass = ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
    int cacheSize = 1024 * 1024 * memClass / 4;  //硬引用緩存容量,為系統(tǒng)可用內(nèi)存的1/4
    mLruCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap value) {
            if (value != null)
                return value.getRowBytes() * value.getHeight();
            else
                return 0;
        }
                                                                                    
        @Override
        protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
            if (oldValue != null)
                // 硬引用緩存容量滿的時(shí)候,會(huì)根據(jù)LRU算法把最近沒有被使用的圖片轉(zhuǎn)入此軟引用緩存
                mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
        }
    };
    mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_SIZE, 0.75f, true) {
        private static final long serialVersionUID = 6040103833179403725L;
        @Override
        protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
            if (size() > SOFT_CACHE_SIZE){    
                return true;  
            }  
            return false; 
        }
    };
}
                                                                            
/**
 * 從緩存中獲取圖片
 */
public Bitmap getBitmapFromCache(String url) {
    Bitmap bitmap;
    //先從硬引用緩存中獲取
    synchronized (mLruCache) {
        bitmap = mLruCache.get(url);
        if (bitmap != null) {
            //如果找到的話,把元素移到LinkedHashMap的最前面,從而保證在LRU算法中是最后被刪除
            mLruCache.remove(url);
            mLruCache.put(url, bitmap);
            return bitmap;
        }
    }
    //如果硬引用緩存中找不到,到軟引用緩存中找
    synchronized (mSoftCache) { 
        SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);
        if (bitmapReference != null) {
            bitmap = bitmapReference.get();
            if (bitmap != null) {
                //將圖片移回硬緩存
                mLruCache.put(url, bitmap);
                mSoftCache.remove(url);
                return bitmap;
            } else {
                mSoftCache.remove(url);
            }
        }
    }
    return null;
} 
                                                                            
/**
 * 添加圖片到緩存
 */
public void addBitmapToCache(String url, Bitmap bitmap) {
    if (bitmap != null) {
        synchronized (mLruCache) {
            mLruCache.put(url, bitmap);
        }
    }
}
                                                                            
public void clearCache() {
    mSoftCache.clear();
}

}

文件緩存類:ImageFileCache
public class ImageFileCache {
private static final String CACHDIR = "ImgCach";
private static final String WHOLESALE_CONV = ".cach";

private static final int MB = 1024*1024;
private static final int CACHE_SIZE = 10;
private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10;
                                                          
public ImageFileCache() {
    //清理文件緩存
    removeCache(getDirectory());
}
                                                          
/** 從緩存中獲取圖片 **/
public Bitmap getImage(final String url) {    
    final String path = getDirectory() + "/" + convertUrlToFileName(url);
    File file = new File(path);
    if (file.exists()) {
        Bitmap bmp = BitmapFactory.decodeFile(path);
        if (bmp == null) {
            file.delete();
        } else {
            updateFileTime(path);
            return bmp;
        }
    }
    return null;
}
                                                          
/** 將圖片存入文件緩存 **/
public void saveBitmap(Bitmap bm, String url) {
    if (bm == null) {
        return;
    }
    //判斷sdcard上的空間
    if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
        //SD空間不足
        return;
    }
    String filename = convertUrlToFileName(url);
    String dir = getDirectory();
    File dirFile = new File(dir);
    if (!dirFile.exists())
        dirFile.mkdirs();
    File file = new File(dir +"/" + filename);
    try {
        file.createNewFile();
        OutputStream outStream = new FileOutputStream(file);
        bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
        outStream.flush();
        outStream.close();
    } catch (FileNotFoundException e) {
        Log.w("ImageFileCache", "FileNotFoundException");
    } catch (IOException e) {
        Log.w("ImageFileCache", "IOException");
    }
} 
                                                          
/**
 * 計(jì)算存儲(chǔ)目錄下的文件大小,
 * 當(dāng)文件總大小大于規(guī)定的CACHE_SIZE或者sdcard剩余空間小于FREE_SD_SPACE_NEEDED_TO_CACHE的規(guī)定
 * 那么刪除40%最近沒有被使用的文件
 */
private boolean removeCache(String dirPath) {
    File dir = new File(dirPath);
    File[] files = dir.listFiles();
    if (files == null) {
        return true;
    }
    if (!android.os.Environment.getExternalStorageState().equals(
            android.os.Environment.MEDIA_MOUNTED)) {
        return false;
    }
                                                      
    int dirSize = 0;
    for (int i = 0; i < files.length; i++) {
        if (files[i].getName().contains(WHOLESALE_CONV)) {
            dirSize += files[i].length();
        }
    }
                                                      
    if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
        int removeFactor = (int) ((0.4 * files.length) + 1);
        Arrays.sort(files, new FileLastModifSort());
        for (int i = 0; i < removeFactor; i++) {
            if (files[i].getName().contains(WHOLESALE_CONV)) {
                files[i].delete();
            }
        }
    }
                                                      
    if (freeSpaceOnSd() <= CACHE_SIZE) {
        return false;
    }
                                                              
    return true;
}
                                                          
/** 修改文件的最后修改時(shí)間 **/
public void updateFileTime(String path) {
    File file = new File(path);
    long newModifiedTime = System.currentTimeMillis();
    file.setLastModified(newModifiedTime);
}
                                                          
/** 計(jì)算sdcard上的剩余空間 **/
private int freeSpaceOnSd() {
    StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
    double sdFreeMB = ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;
    return (int) sdFreeMB;
} 
                                                          
/** 將url轉(zhuǎn)成文件名 **/
private String convertUrlToFileName(String url) {
    String[] strs = url.split("/");
    return strs[strs.length - 1] + WHOLESALE_CONV;
}
                                                          
/** 獲得緩存目錄 **/
private String getDirectory() {
    String dir = getSDPath() + "/" + CACHDIR;
    return dir;
}
                                                          
/** 取SD卡路徑 **/
private String getSDPath() {
    File sdDir = null;
    boolean sdCardExist = Environment.getExternalStorageState().equals(
            android.os.Environment.MEDIA_MOUNTED);  //判斷sd卡是否存在
    if (sdCardExist) {
        sdDir = Environment.getExternalStorageDirectory();  //獲取根目錄
    }
    if (sdDir != null) {
        return sdDir.toString();
    } else {
        return "";
    }
} 
                                                      
/**
 * 根據(jù)文件的最后修改時(shí)間進(jìn)行排序
 */
private class FileLastModifSort implements Comparator<File> {
    public int compare(File arg0, File arg1) {
        if (arg0.lastModified() > arg1.lastModified()) {
            return 1;
        } else if (arg0.lastModified() == arg1.lastModified()) {
            return 0;
        } else {
            return -1;
        }
    }
}

}

從網(wǎng)絡(luò)獲取圖片:
public class ImageGetFormHttp {
private static final String LOG_TAG = "ImageGetForHttp";

public static Bitmap downloadBitmap(String url) {
    final HttpClient client = new DefaultHttpClient();
    final HttpGet getRequest = new HttpGet(url);
                                                         
    try {
        HttpResponse response = client.execute(getRequest);
        final int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != HttpStatus.SC_OK) {
            Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url);
            return null;
        }
                                                             
        final HttpEntity entity = response.getEntity();
        if (entity != null) {
            InputStream inputStream = null;
            try {
                inputStream = entity.getContent();
                FilterInputStream fit = new FlushedInputStream(inputStream);
                return BitmapFactory.decodeStream(fit);
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                    inputStream = null;
                }
                entity.consumeContent();
            }
        }
    } catch (IOException e) {
        getRequest.abort();
        Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
    } catch (IllegalStateException e) {
        getRequest.abort();
        Log.w(LOG_TAG, "Incorrect URL: " + url);
    } catch (Exception e) {
        getRequest.abort();
        Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
    } finally {
        client.getConnectionManager().shutdown();
    }
    return null;
}
                                                 
/*
 * An InputStream that skips the exact number of bytes provided, unless it reaches EOF.
 */
static class FlushedInputStream extends FilterInputStream {
    public FlushedInputStream(InputStream inputStream) {
        super(inputStream);
    }
                                                 
    @Override
    public long skip(long n) throws IOException {
        long totalBytesSkipped = 0L;
        while (totalBytesSkipped < n) {
            long bytesSkipped = in.skip(n - totalBytesSkipped);
            if (bytesSkipped == 0L) {
                int b = read();
                if (b < 0) {
                    break;  // we reached EOF
                } else {
                    bytesSkipped = 1; // we read one byte
                }
            }
            totalBytesSkipped += bytesSkipped;
        }
        return totalBytesSkipped;
    }
}

}

最后,獲取一張圖片的流程就如下代碼所示:
/*** 獲得一張圖片,從三個(gè)地方獲取,首先是內(nèi)存緩存,然后是文件緩存,最后從網(wǎng)絡(luò)獲取 ***/
public Bitmap getBitmap(String url) {
// 從內(nèi)存緩存中獲取圖片
Bitmap result;
result = memoryCache.getBitmapFromCache(url);
if (result == null) {
// 文件緩存中獲取
result = fileCache.getImage(url);
if (result == null) {
// 從網(wǎng)絡(luò)獲取
result = ImageGetFormHttp.downloadBitmap(url);
if (result != null) {
fileCache.saveBitmap(result, url);
memoryCache.addBitmapToCache(url, result);
}
} else {
// 添加到內(nèi)存緩存
memoryCache.addBitmapToCache(url, result);
}
}
return result;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,701評論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,568評論 19 139
  • 一、緩存介紹: (一)、Android中緩存的必要性: 智能手機(jī)的緩存管理應(yīng)用非常的普遍和需要,是提高用戶體驗(yàn)的有...
    溫暖的外星閱讀 937評論 0 12
  • 一、簡介 在泰國舉行的谷歌開發(fā)者論壇上,谷歌為我們介紹了一個(gè)名叫Glide的圖片加載庫,作者是bumptech。這...
    天天大保建閱讀 7,762評論 2 28
  • 我為你雙眸失真 希望你待我如珍 我痛徹心扉你的冷漠,心慌慌如枯木 五百年一輪回,一千年一等待 我以淚洗面報(bào)你滴水之...
    Denis曉鐘閱讀 302評論 0 0

友情鏈接更多精彩內(nèi)容