第12章 Bitmap的加載和Cache

12.1 Bitmap的高效加載

  1. Bitmap是如何加載的葱蝗? BitmapFactory類提供了四類方法:
    1. decodeFile
    2. decodeResource
    3. decodeStream
    4. decodeByteArray
  2. 如何高效加載Bitmap冈爹?
    1. 采用BitmapFactory.Options按照一定的采樣率來加載所需尺寸的圖片,因?yàn)閕mageview所需的圖片大小往往小于圖片的原始尺寸。
    2. BitmapFactory.Options的inSampleSize參數(shù),即采樣率
  3. 獲取采樣率和高效加載的例子

注意:將inJustDecodeBounds設(shè)置為true的時候,BitmapFactory只會解析圖片的原始寬高信息铺纽,并不會真正的加載圖片

public Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    if (reqWidth == 0 || reqHeight == 0) {
        return 1;
    }

    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    Log.d(TAG, "origin, w= " + width + " h=" + height);
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and
        // keeps both height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    Log.d(TAG, "sampleSize:" + inSampleSize);
    return inSampleSize;
}

12.2 Android中的緩存策略

  1. 緩存的主要是為了避免流量消耗。主要策略包含緩存的添加哟忍、獲取和刪除
  2. 最常用的緩存算法是LRU狡门,核心是當(dāng)緩存滿時,會優(yōu)先淘汰那些近期最少使用的緩存對象
  3. LRU算法的緩存:LruCache(內(nèi)存緩存)和DiskLruCache(磁盤緩存)

LruCache

  1. LruCache是泛型類锅很,內(nèi)部采用LinkedHashMap以強(qiáng)引用的方式存儲外界的緩存對象;
  2. 當(dāng)緩存滿時其馏,移除較早使用的緩存對象,然后再添加新的緩存對象
  3. LruCache是線程安全的
  4. LruCache的創(chuàng)建示例
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
            }
        };
  1. put和get的示例
mMemoryCache.get(key)
mMemoryCache.put(key,bitmap)

DiskLruCache

DiskLruCache的創(chuàng)建爆安、緩存查找和緩存添加操作

ImageLoader的實(shí)現(xiàn)

  1. 圖片壓縮功能實(shí)現(xiàn)——使用BitmapFactory.Options的inSampleSize

  2. 內(nèi)存緩存和磁盤緩存的實(shí)現(xiàn)

    1. 在ImageLoader的構(gòu)造方法中叛复,建立好LruCache和DiskLruCache
    2. 在loadBitmapFromHttp中,將http下載的數(shù)據(jù)存儲到DiskLruCache中扔仓,然后去調(diào)用loadBitmapFromDiskCache;
    3. 在loadBitmapFromDiskCache中褐奥,取出相應(yīng)的Cache,使用圖片壓縮獲得壓縮后的bitmap翘簇,將bitmap放入LruCache中——addBitmapToMemoryCache(key,bitmap)撬码。
    4. 這樣經(jīng)過2、3兩步版保,即實(shí)現(xiàn)了獲取Bitmap呜笑,又實(shí)現(xiàn)了內(nèi)存夫否、硬盤的雙緩存
  3. 同步加載和異步加載接口的設(shè)計(jì)

    1. 同步加載,不能在主線程使用叫胁,首先嘗試從LruCache中獲取沒慷吊,接著嘗試從DiskLruCache中讀取,最后才從網(wǎng)絡(luò)中拉取曹抬。
    public Bitmap loadBitmap(String uri, int reqWidth, int reqHeight) {
        Bitmap bitmap = loadBitmapFromMemCache(uri);
        if (bitmap != null) {
            Log.d(TAG, "loadBitmapFromMemCache,url:" + uri);
            return bitmap;
        }
    
        try {
            bitmap = loadBitmapFromDiskCache(uri, reqWidth, reqHeight);
            if (bitmap != null) {
                Log.d(TAG, "loadBitmapFromDisk,url:" + uri);
                return bitmap;
            }
            bitmap = loadBitmapFromHttp(uri, reqWidth, reqHeight);
            Log.d(TAG, "loadBitmapFromHttp,url:" + uri);
        } catch (IOException e) {
            e.printStackTrace();
        }
    
        if (bitmap == null && !mIsDiskLruCacheCreated) {
            Log.w(TAG, "encounter error, DiskLruCache is not created.");
            bitmap = downloadBitmapFromUrl(uri);
        }
    
        return bitmap;
    }
    
    
    1. 異步加載: 首先嘗試從LruCache中獲取溉瓶,如果讀取成功則返回,否則會從線程池中去調(diào)用loadBitmap谤民,加載成功后堰酿,封裝成一個LoaderResult通過Handler發(fā)給主線程去顯示。
    public void bindBitmap(final String uri, final ImageView imageView,
            final int reqWidth, final int reqHeight) {
        imageView.setTag(TAG_KEY_URI, uri);
        Bitmap bitmap = loadBitmapFromMemCache(uri);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
    
        Runnable loadBitmapTask = new Runnable() {
    
            @Override
            public void run() {
                Bitmap bitmap = loadBitmap(uri, reqWidth, reqHeight);
                if (bitmap != null) {
                    LoaderResult result = new LoaderResult(imageView, uri, bitmap);
                    mMainHandler.obtainMessage(MESSAGE_POST_RESULT, result).sendToTarget();
                }
            }
        };
        THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
    }
    
    1. 異步加載導(dǎo)致的列表錯位問題:可以在給ImageView設(shè)置圖片之前檢查url有沒有改變张足,如果發(fā)生改變則不給設(shè)置圖片触创。
  4. 優(yōu)化列表的卡頓現(xiàn)象

    1. 不要在getView中執(zhí)行耗時操作,不要在getView中直接加載圖片为牍,否則肯定會導(dǎo)致卡頓哼绑;
    2. 控制異步任務(wù)的執(zhí)行頻率:在列表滑動的時候停止加載圖片,等列表停下來以后再加載圖片碉咆;
    3. 使用硬件加速來解決莫名的卡頓問題抖韩,給Activity添加配置android:hardwareAccelerated="true"。

12.3 幾個開源Imageloader

1. Glide 一般項(xiàng)目里用這個的多疫铜,還可以實(shí)現(xiàn)裁剪茂浮,模糊等效果
2. Fresco
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市壳咕,隨后出現(xiàn)的幾起案子席揽,更是在濱河造成了極大的恐慌,老刑警劉巖谓厘,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件幌羞,死亡現(xiàn)場離奇詭異,居然都是意外死亡竟稳,警方通過查閱死者的電腦和手機(jī)属桦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來住练,“玉大人地啰,你說我怎么就攤上這事〗补洌” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵岭埠,是天一觀的道長盏混。 經(jīng)常有香客問我蔚鸥,道長,這世上最難降的妖魔是什么许赃? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任止喷,我火速辦了婚禮,結(jié)果婚禮上混聊,老公的妹妹穿的比我還像新娘弹谁。我一直安慰自己,他們只是感情好句喜,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布预愤。 她就那樣靜靜地躺著,像睡著了一般咳胃。 火紅的嫁衣襯著肌膚如雪植康。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天展懈,我揣著相機(jī)與錄音销睁,去河邊找鬼。 笑死存崖,一個胖子當(dāng)著我的面吹牛冻记,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播来惧,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼檩赢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了违寞?” 一聲冷哼從身側(cè)響起贞瞒,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎趁曼,沒想到半個月后军浆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡挡闰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年乒融,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摄悯。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡赞季,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奢驯,到底是詐尸還是另有隱情申钩,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布瘪阁,位于F島的核電站撒遣,受9級特大地震影響邮偎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜义黎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一禾进、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧廉涕,春花似錦泻云、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至馏鹤,卻和暖如春征椒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背湃累。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工勃救, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人治力。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓蒙秒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宵统。 傳聞我的和親對象是個殘疾皇子晕讲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容