第12章 Bitmap的加載和Cache

12.1 Bitmap的高效加載

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

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

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)緩存滿時(shí),會(huì)優(yōu)先淘汰那些近期最少使用的緩存對(duì)象
  3. LRU算法的緩存:LruCache(內(nèi)存緩存)和DiskLruCache(磁盤(pán)緩存)

LruCache

  1. LruCache是泛型類,內(nèi)部采用LinkedHashMap以強(qiáng)引用的方式存儲(chǔ)外界的緩存對(duì)象;
  2. 當(dāng)緩存滿時(shí),移除較早使用的緩存對(duì)象,然后再添加新的緩存對(duì)象
  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)存緩存和磁盤(pán)緩存的實(shí)現(xiàn)

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

    1. 同步加載,不能在主線程使用,首先嘗試從LruCache中獲取沒(méi),接著嘗試從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中獲取,如果讀取成功則返回,否則會(huì)從線程池中去調(diào)用loadBitmap,加載成功后,封裝成一個(gè)LoaderResult通過(guò)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)致的列表錯(cuò)位問(wèn)題:可以在給ImageView設(shè)置圖片之前檢查url有沒(méi)有改變,如果發(fā)生改變則不給設(shè)置圖片。
  4. 優(yōu)化列表的卡頓現(xiàn)象

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

12.3 幾個(gè)開(kāi)源Imageloader

1. Glide 一般項(xiàng)目里用這個(gè)的多,還可以實(shí)現(xiàn)裁剪,模糊等效果
2. Fresco
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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