如何處理大圖片加載

Android程序分配的堆內(nèi)存一共16M(基本上就是這么多,不同的設(shè)備可能會有一些差異)

android加載圖片的時候使用的是ARGB格式加載的,也就是1px的大小等于4byte,emmmmm,要是不做一些處理的話內(nèi)存是不夠折騰的

說到解析圖片就需要提到BitmapFactory,這個類里面提供了方法(decodeByteArray, decodeFile, decodeResource等)來解析生成bitmap對象,同時也有大圖片核心處理類BitmapFactory.Option

他有三個成員變量用來更改圖片所占內(nèi)存的設(shè)置

1.inJustDecodeBounds 可以設(shè)置為true/false,true:不用把圖片真正加載到內(nèi)存即可得到寬度和高度

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

2.inSampleSize:采樣率,最好使用2的指數(shù),如2(1/4)、4(1/16)、8(1/64)、16
采樣率的意思:比如是4,就是在橫豎的像素上每4個像素取一個點,這樣像素就變成了1/4*1/4 = 1/16 變成了原來的十六分之一。

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

下面的代碼非常簡單地將任意一張圖片壓縮成50*50的縮略圖,并在ImageView上展示。

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 50, 50));

3.Bitmap.Config inPreferredConfig:位圖存儲配置,用于指定用什么樣的方式存儲位圖像素信息。默認(rèn)使用最好的配置:Bitmap.Config.ARGB_8888

在網(wǎng)上查了一下還有一種方法就是使用圖片緩存技術(shù)
加載一張圖片的時候還比較和諧,但是多張的時候(比如在ListView、GridView或ViewPager),隨著展示的圖片越來越多如果內(nèi)存不及時的進行釋放還是會造成OOM。
內(nèi)存緩存技術(shù)對那些大量占用應(yīng)用程序?qū)氋F內(nèi)存的圖片提供了快速訪問的方法。其中最核心的類是LruCache (此類在android-support-v4的包中提供) 。這個類非常適合用來緩存圖片,它的主要算法原理是把最近使用的對象用強引用存儲在 LinkedHashMap 中,并且把最近最少使用的對象在緩存值達(dá)到預(yù)設(shè)定值之前從內(nèi)存中移除。
在過去,我們經(jīng)常會使用一種非常流行的內(nèi)存緩存技術(shù)的實現(xiàn),即軟引用或弱引用 (SoftReference or WeakReference)。但是現(xiàn)在已經(jīng)不再推薦使用這種方式了,因為從 Android 2.3 (API Level 9)開始,垃圾回收器會更傾向于回收持有軟引用或弱引用的對象,這讓軟引用和弱引用變得不再可靠。另外,Android 3.0 (API Level 11)中,圖片的數(shù)據(jù)會存儲在本地的內(nèi)存當(dāng)中,因而無法用一種可預(yù)見的方式將其釋放,這就有潛在的風(fēng)險造成應(yīng)用程序的內(nèi)存溢出并崩潰。

為了能夠選擇一個合適的緩存大小給LruCache, 有以下多個因素應(yīng)該放入考慮范圍內(nèi),例如:

你的設(shè)備可以為每個應(yīng)用程序分配多大的內(nèi)存?
設(shè)備屏幕上一次最多能顯示多少張圖片?有多少圖片需要進行預(yù)加載,因為有可能很快也會顯示在屏幕上?
你的設(shè)備的屏幕大小和分辨率分別是多少?一個超高分辨率的設(shè)備(例如 Galaxy Nexus) 比起一個較低分辨率的設(shè)備(例如 Nexus S),在持有相同數(shù)量圖片的時候,需要更大的緩存空間。
圖片的尺寸和大小,還有每張圖片會占據(jù)多少內(nèi)存空間。
圖片被訪問的頻率有多高?會不會有一些圖片的訪問頻率比其它圖片要高?如果有的話,你也許應(yīng)該讓一些圖片常駐在內(nèi)存當(dāng)中,或者使用多個LruCache 對象來區(qū)分不同組的圖片。
你能維持好數(shù)量和質(zhì)量之間的平衡嗎?有些時候,存儲多個低像素的圖片,而在后臺去開線程加載高像素的圖片會更加的有效。

并沒有一個指定的緩存大小可以滿足所有的應(yīng)用程序,這是由你決定的。你應(yīng)該去分析程序內(nèi)存的使用情況,然后制定出一個合適的解決方案。一個太小的緩存空間,有可能造成圖片頻繁地被釋放和重新加載,這并沒有好處。而一個太大的緩存空間,則有可能還是會引起 java.lang.OutOfMemory 的異常。

下面是一個使用 LruCache 來緩存圖片的例子:

private LruCache<String, Bitmap> mMemoryCache;
 
@Override
protected void onCreate(Bundle savedInstanceState) {
    // 獲取到可用內(nèi)存的最大值,使用內(nèi)存超出這個值會引起OutOfMemory異常。
    // LruCache通過構(gòu)造函數(shù)傳入緩存值,以KB為單位。
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    // 使用最大可用內(nèi)存值的1/8作為緩存的大小。
    int cacheSize = maxMemory / 8;
    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            // 重寫此方法來衡量每張圖片的大小,默認(rèn)返回圖片數(shù)量。
            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);
}

在這個例子當(dāng)中,使用了系統(tǒng)分配給應(yīng)用程序的八分之一內(nèi)存來作為緩存大小。在中高配置的手機當(dāng)中,這大概會有4兆(32/8)的緩存空間。一個全屏幕的 GridView 使用4張 800x480分辨率的圖片來填充,則大概會占用1.5兆的空間(8004804)。因此,這個緩存大小可以存儲2.5頁的圖片。
當(dāng)向 ImageView 中加載一張圖片時,首先會在 LruCache 的緩存中進行檢查。如果找到了相應(yīng)的鍵值,則會立刻更新ImageView ,否則開啟一個后臺線程來加載這張圖片。

public void loadBitmap(int resId, ImageView imageView) {
    final String imageKey = String.valueOf(resId);
    final Bitmap bitmap = getBitmapFromMemCache(imageKey);
    if (bitmap != null) {
        imageView.setImageBitmap(bitmap);
    } else {
        imageView.setImageResource(R.drawable.image_placeholder);
        BitmapWorkerTask task = new BitmapWorkerTask(imageView);
        task.execute(resId);
    }
}

BitmapWorkerTask 還要把新加載的圖片的鍵值對放到緩存中。

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    // 在后臺加載圖片。
    @Override
    protected Bitmap doInBackground(Integer... params) {
        final Bitmap bitmap = decodeSampledBitmapFromResource(
                getResources(), params[0], 100, 100);
        addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
        return bitmap;
    }
}

最后再推薦三個第三方直接加載圖片、
picasso:

compile 'com.squareup.picasso:picasso:2.5.2'

Glide:

 compile 'com.github.bumptech.glide:glide:3.7.0'  
 compile 'com.android.support:support-v4:23.2.1'  

fresco:

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

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

  • 7.1 壓縮圖片 一、基礎(chǔ)知識 1、圖片的格式 jpg:最常見的圖片格式。色彩還原度比較好,可以支持適當(dāng)壓縮后保持...
    AndroidMaster閱讀 2,712評論 0 13
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 30,265評論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,675評論 1 32
  • 一、緩存介紹: (一)、Android中緩存的必要性: 智能手機的緩存管理應(yīng)用非常的普遍和需要,是提高用戶體驗的有...
    溫暖的外星閱讀 943評論 0 12
  • 剛參加工作那陣子,幾個年輕的同事總愛互相開玩笑,一見面就問:你cǎ了嗎?“c?!痹谖覀儺?dāng)?shù)厥菍S脕碇浮柏i吃食”的。...
    單老師碼字閱讀 1,232評論 15 10

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