1. 程式人生 > >Android中圖片快取的一些總結

Android中圖片快取的一些總結

最近在做圖片載入時遇到記憶體溢位問題,所以這裡找到一些資料學習後,在這裡做一個總結。
這裡使用帶了LruCache技術和DiskLruCache技術,簡單地說,LruCache是做的記憶體快取處理,只負責記憶體中圖片的管理,也就是在記憶體中快取被清除後還是需要重新從網路去載入,這就是導致效果很差,一個很直觀的就是消耗流量,再一個就是網路載入就會很慢,大量圖片時就會導致很卡頓不流暢,體驗也不好。所以為了解決上面的問題還需要DiskLruCache做本地快取。
Bitmap生成
在學習上快取技術前,我先學習了一下BitmapFcatroy這個類的用法。BitmapFactory這個類提供了多個解析方法用於建立Bitmap物件,對不同的圖片來源使用不一樣的方法,具體如下:
SD卡圖片選擇decodeFile(),BitmapFactory.decodeFileDescriptor()方法
網路上的圖片選擇decodeStream()方法
資原始檔選擇使用decodeResource()方法
在預設情況下,使用上面的方法得到bitmap時會預設給bitmap分配記憶體,如果,圖片過大就會導致記憶體溢位,所以,每一種解析方法都提供了一個可選的BitmapFactory.Options引數,將這個引數的inJustDecodeBounds屬性設定為true就可以讓解析方法禁止為bitmap分配記憶體,返回值也不再是一個Bitmap物件,而是null。雖然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType屬性都會被賦值。這個技巧讓我們可以在載入圖片之前就獲取到圖片的長寬值和MIME型別,從而根據情況對圖片進行壓縮。
BitmapFactory的具體用法如下demo:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,  
        int reqWidth, int reqHeight) {  
    // 第一次解析將inJustDecodeBounds設定為true,來獲取圖片大小  
    final BitmapFactory.Options options = new BitmapFactory.Options();  
    options.inJustDecodeBounds = true;  
    BitmapFactory.decodeResource(res, resId, options);  
    // 呼叫上面定義的方法計算inSampleSize值  
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // 使用獲取到的inSampleSize值再次解析圖片 options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } public static int calculateInSampleSize(BitmapFactory.Options options, int
reqWidth, int reqHeight) { // 源圖片的高度和寬度 final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // 計算出實際寬高和目標寬高的比率 final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // 選擇寬和高中最小的比率作為inSampleSize的值,這樣可以保證最終圖片的寬和高 // 一定都會大於等於目標的寬和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; }

LruCache快取
接下來就要用一下LruCache在記憶體中做快取了,這個其實用法很簡單,用到的也比較多,在使用Volley載入圖片時也需要一個自己實現的快取,也就是一個LruCache,一個簡單的demo如下:

private LruCache<String, Bitmap> mMemoryCache;  

@Override  
protected void onCreate(Bundle savedInstanceState) {  
    // 獲取到可用記憶體的最大值,使用記憶體超出這個值會引起OutOfMemory異常。  
    // LruCache通過建構函式傳入快取值,以KB為單位。  
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
    // 使用最大可用記憶體值的1/8作為快取的大小。  
    int cacheSize = maxMemory / 8;  
    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {  
        @Override  
        protected int sizeOf(String key, Bitmap bitmap) {  
            // 重寫此方法來衡量每張圖片的大小,預設返回圖片數量。  
            return bitmap.getByteCount() / 1024;  
        }  
    };  
}  

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {  
    if (getBitmapFromMemCache(key) == null) {  
        mMemoryCache.put(key, bitmap);  
    }  
}  

public Bitmap getBitmapFromMemCache(String key) {  
    return mMemoryCache.get(key);  
}  

在這個例子當中,使用了系統分配給應用程式的八分之一記憶體來作為快取大小。在中高配置的手機當中,這大概會有4兆(32/8)的快取空間。一個全螢幕的 GridView 使用4張 800x480解析度的圖片來填充,則大概會佔用1.5兆的空間(800*480*4)。因此,這個快取大小可以儲存2.5頁的圖片。
當向 ImageView 中載入一張圖片時,首先會在 LruCache 的快取中進行檢查。如果找到了相應的鍵值,則會立刻更新ImageView ,否則開啟一個後臺執行緒來載入這張圖片。
DiskLruCache
那麼接下來就是DiskLruCache,這是google官方認證的。使用是非常廣泛的,我們看新聞時總是會發現有些看過的圖片在我們斷網後還可以開啟檢視,有些新聞網頁也是可以在斷網後檢視的,他們都是做了本地快取處理了。通常情況下多數應用程式都會將快取的位置選擇為 /sdcard/Android/data/<應用程式包名>/cache 這個路徑。選擇在這個位置有兩點好處:第一,這是儲存在SD卡上的,因此即使快取再多的資料也不會對手機的內建儲存空間有任何影響,只要SD卡空間足夠就行。第二,這個路徑被Android系統認定為應用程式的快取路徑,當程式被解除安裝的時候,這裡的資料也會一起被清除掉,這樣就不會出現刪除程式之後手機上還有很多殘留資料的問題。在使用了DiskLruCache快取是可以在手機上檢視的,裡面有一些命名沒有規則的檔案,但是有一個檔案可以看懂叫journal的檔案,這個相當於一個日誌檔案,記錄快取檔案的命名和大小還能用於統計快取的大小。
關於DiskLruCache的使用
開啟快取
DiskLruCache是不能new出例項的,如果我們要建立一個DiskLruCache的例項,則需要呼叫它的open()方法,介面如下所示

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)  

open()方法接收四個引數,第一個引數指定的是資料的快取地址,第二個引數指定當前應用程式的版本號,第三個引數指定同一個key可以對應多少個快取檔案,基本都是傳1,第四個引數指定最多可以快取多少位元組的資料。
其中快取地址前面已經說過了,通常都會存放在 /sdcard/Android/data/<應用程式包名>/cache 這個路徑下面,但同時我們又需要考慮如果這個手機沒有SD卡,或者SD正好被移除了的情況,因此專門寫一個方法來獲取快取地址,如下所示:

    /**
     * 獲取DiskLruCache的快取資料夾 注意第二個引數dataType
     * DiskLruCache用一個String型別的唯一值對不同型別的資料進行區分.
     * 比如bitmap,object等資料夾.其中在bitmap資料夾中快取了圖片. card/Android/data/<application
     * package>/cache 如果 快取資料的存放位置為: /sdSD卡不存在時快取存放位置為: /data/data/<application
     * package>/cache
     * 
     */
    @SuppressLint("NewApi")
    public static File getDiskLruCacheDir(Context context, String dataType) {
        String dirPath;
        File cacheFile = null;
        if (Environment.MEDIA_MOUNTED.equals(Environment
                .getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            dirPath = context.getExternalCacheDir().getPath();
        }else{
            dirPath = context.getCacheDir().getPath();
        }
        cacheFile = new File(dirPath + File.separator + dataType);
        return cacheFile;
    }

    /**
     * 獲取APP當前版本號
     * 
     * @param context
     * @return
     */
    public static int getAppVersionCode(Context context) {
        int versionCode = 1;
        try {
            String packageName = context.getPackageName();
            PackageManager packageManager = context.getPackageManager();
            PackageInfo packageInfo = packageManager.getPackageInfo(
                    packageName, 0);
            versionCode = packageInfo.versionCode;
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
        return versionCode;
    }

    /**
     * 將字串用MD5編碼. 比如在該示例中將url進行MD5編碼
     */
    public static String getStringByMD5(String string) {
        String md5String = null;
        try {
            // Create MD5 Hash
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
            messageDigest.update(string.getBytes());
            byte messageDigestByteArray[] = messageDigest.digest();
            if (messageDigestByteArray == null
                    || messageDigestByteArray.length == 0) {
                return md5String;
            }

            // Create hexadecimal String
            StringBuffer hexadecimalStringBuffer = new StringBuffer();
            int length = messageDigestByteArray.length;
            for (int i = 0; i < length; i++) {
                hexadecimalStringBuffer.append(Integer
                        .toHexString(0xFF & messageDigestByteArray[i]));
            }
            md5String = hexadecimalStringBuffer.toString();
            return md5String;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return md5String;
    }

讀取快取
快取已經寫入成功之後,接下來我們就該學習一下如何讀取了。讀取的方法要比寫入簡單一些,主要是藉助DiskLruCache的get()方法實現的,介面如下所示:

public synchronized Snapshot get(String key) throws IOException  

很明顯,get()方法要求傳入一個key來獲取到相應的快取資料,而這個key毫無疑問就是將圖片URL進行MD5編碼後的值了,也就是getStringByMD5後的值。
獲取到的是一個DiskLruCache.Snapshot物件,只需要呼叫它的getInputStream()方法就可以得到快取檔案的輸入流了。同樣地,getInputStream()方法也需要傳一個index引數,這裡傳入0就好。有了檔案的輸入流之後,想要把快取圖片顯示到介面上就輕而易舉了。所以,一段完整的讀取快取,並將圖片載入到介面上的程式碼如下所示:

try {  
    String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
    String key = getStringByMD5(imageUrl);  
    DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);  
    if (snapShot != null) {  
        InputStream is = snapShot.getInputStream(0);  
        Bitmap bitmap = BitmapFactory.decodeStream(is);  
        mImage.setImageBitmap(bitmap);  
    }  
} catch (IOException e) {  
    e.printStackTrace();  
}  

移出快取

public synchronized boolean remove(String key) throws IOException  

用法和get類似。

相關推薦

Android圖片快取一些總結

最近在做圖片載入時遇到記憶體溢位問題,所以這裡找到一些資料學習後,在這裡做一個總結。 這裡使用帶了LruCache技術和DiskLruCache技術,簡單地說,LruCache是做的記憶體快取處理,只負責記憶體中圖片的管理,也就是在記憶體中快取被清除後還是需要

Android圖片的三級快取

為什麼要使用三級快取 如今的 Android App 經常會需要網路互動,通過網路獲取圖片是再正常不過的事了假如每次啟動的時候都從網路拉取圖片的話,勢必會消耗很多流量。在當前的狀況下,對於非wifi使用者來說,流量還是很貴的,一個很耗流量的應用,其使用者數量級肯定要受到影響特別是,當我們想要重複瀏覽一些圖片

Android快取處理及非同步載入圖片類的封裝

一、快取介紹: (一)、Android中快取的必要性: 智慧手機的快取管理應用非常的普遍和需要,是提高使用者體驗的有效手段之一。 1、沒有快取的弊端: 流量開銷:對於客戶端——伺服器端應用,從遠端獲取圖片算是經常要用的一個功能,而圖片資源往往會消耗比較大的流量。 載入速

Android開發圖片快取框架Glide的總結

前言 前段時間寫過一篇圖片快取框架Picasso的用法,對於Picasso有些同學也比較熟悉,採用Lru最近最少快取策略,並且自帶記憶體和硬碟快取機制,在圖片載入尤其是多圖載入著實為大夥省了不少力,在此之前同樣也相識有Afinal、Xutil、Univer

Android圖片的三級快取詳解

圖片的三級快取機制一般是指應用載入圖片的時候,分別去訪問內容,檔案,網路獲取圖片的一種行為。 一、三級快取流程圖 三級快取流程圖 二、程式碼框架搭建 這裡我仿造 Picasso 的載入圖片程式碼,也做出了with,load,into等方法。 2.1 with(c

android圖片的三級cache策略(記憶體、檔案、網路)之二:記憶體快取策略

前言 記得很久之前我寫了一篇banner的文章,好多朋友找我要程式碼,並要我開放banner中使用的圖片管理工廠-ImageManager。如果想很好地理解下面的故事,請參看我半年前寫的兩篇博文:android中圖片的三級cache策略(記憶體、檔案、網路) 一 和 an

Android圖片的三級快取---小理解

/** * 三級快取之網路快取 */ public class NetCacheUtils { private LocalCacheUtils mLocalCacheUtils; private MemoryCacheUtils mMemo

cocos2d-x 移植到android編譯的一些問題:fatal error: Box2D/Box2D.h: No such file or directory&quot;

cocos2 app nal get dsc err 2dx ros blue 1、fatal error: Box2D/Box2D.h: No such file or directory" 須要加入box2d庫的支持,改動android.mk

Android圖片的三級緩存策略

getitem 圖片顯示 current sco clas 實例 ase activit fileinput 一、簡單介紹 如今的Android應用程序中。不可避免

AndroidPaint的一些使用心得記錄

心得 ant 不可 ext 設置字體大小 畫筆 n) 記錄 12px Paint是Android api中繪制文字,圖形的重要類,位於android.graphics包下,這個類早早就出現了。類似於JavaScript中的Paint,Paint的使用也離不開Canvas。

Android圖片優化之WebP使用

一、什麼是 WebP? WebP(發音 weppy,專案主頁),是一種支援有失真壓縮和無失真壓縮的圖片檔案格式,派生自影象編碼格式 VP8。根據 Google 的測試,無失真壓縮後的 WebP 比 PNG 檔案少了 45% 的檔案大小,即使這些 PNG 檔案經過其他壓縮工具壓縮之後,WebP 還

今年Android面試經歷的一些總結

此文希望能給想跳槽的朋友一些參考。 我們隨著工作時間的增加和技術經驗的積累。原先的公司不一定能技術提供給你想要的發展空間與前景。說直白點,家裡還有老婆孩子還有老父母,我們需要更高的薪酬讓他們過上更好的日子。所以跳槽是個不錯的選擇。 夏末的時候我就有了跳槽的想法。然後就是不斷的跑了很多家公司面

Android快取策略

快取策略的主要流程: 當程式第一次從網路載入圖片後,將其快取到儲存裝置上,下一次就不用再次從網路上獲取了。為了提高應用的使用者體驗,往往還會再記憶體中再快取一份,這樣當應用打算從網路請求一張圖片時,首先從記憶體中讀取,如果沒有那就從儲存裝置中獲取,如果儲存裝置也沒有,那就從網路上下載

android圖片的三級cache策略(記憶體 檔案 網路) 一

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android圖片的淡入

Android中活動中圖片的淡入 我是一個計 算機小白,也許這樣就更能理解小白的心情,我想大神都是從這個階段過來的,這幾天想做一個手機桌布類的應用,剛開始做歡迎頁,想在歡迎頁加入一個淡入圖片,也許效果會很好,以前是學過的,可是學過的東西不一定都能刻在腦子裡,上網上查查資料,發現有很多大神

Android 本地圖片快取

廢話 每次從記憶體裡面讀圖片,圖片小的話還好,圖片大的話比較吃力,速度慢不說,還容易因為記憶體問題出現崩潰。 後來加上了快取,從快取中讀取,結果發現,還是會爆炸,檢查一下發現,一張拍照3M多,直接把整個快取區都炸開了,既然找到問題了,也就好解決了。 所以就加上了個壓縮,邏輯是:1-

Android外掛開發篇總結和概述

                剛剛終於寫完了外掛開發的最後一篇文章,下面就來總結一下,關於Android中外掛篇從去年的11月份就開始規劃了,主要從三個方面去解讀Android中外掛開發原理。說白了,外掛開發的原理就是:動態載入技術。但是我們在開發外掛的過程中可能會遇到很多問題,所以這裡就分為三篇文章進行解

Android圖片佔用記憶體的計算

本人的網易部落格原文 在Android開發中,我現在發現很多人還不會對圖片佔用記憶體進行很好的計算。因此撰寫該博文來做介紹,期望達到拋磚引玉的作用。   Android中一張圖片(BitMap)佔用的記憶體主要和以下幾個因數有關:圖片長度,圖片寬度,單位畫素佔用的位元組

Android清除快取

有時候會遇到要清除應用快取的功能,不經常用,總忘,所以在這裡總結,實際價值個人感覺不大。 /** * 快取管理類 */ public class DataCleanManager { /** * 獲取快取大小 * * @param context

Android圖片快取ImageCache

為什麼要做快取?              在UI介面載入一張圖片時很簡單,然而如果需要載入多張較大的影象,事情就會變得更加複雜。在許多情況下(如ListView、GridView或ViewPager等的元件),螢幕上的圖片的總數伴隨螢幕的滾動會大大增加,且基本上是無限的。