Bitmap-詳解

參考資料


目錄

    1. Bitmap
    1. BitmapFactory
    1. Bitmap加載方法
    1. Bitmap | Drawable | InputStream | Byte[ ] 之間轉(zhuǎn)換
    1. Bitmap常用操作
    1. Bitmap的內(nèi)存優(yōu)化

1)Bitmap

Bitmap圖像處理的最重要類之一。用它可以獲取圖像文件信息,進(jìn)行圖像顏色變換、剪切、旋轉(zhuǎn)、縮放等操作,并可以指定格式保存圖像文件

方法 說(shuō)明
public void recycle() 回收位圖占用的內(nèi)存空間
public final boolean isRecycled() 判斷位圖內(nèi)存是否已釋放
public final int getWidth() 獲取位圖的寬度
public final int getHeight() 獲取位圖的高度
public final boolean isMutable() 圖片是否可修改
public int getScaledWidth(Canvas canvas) 獲取指定密度轉(zhuǎn)換后的圖像的寬度
public int getScaledHeight(Canvas canvas) 獲取指定密度轉(zhuǎn)換后的圖像的高度
public boolean compress(CompressFormat format, int quality, OutputStream stream) 按指定的圖片格式以及畫(huà)質(zhì),將圖片轉(zhuǎn)換為輸出流
format:壓縮圖像的格式,如Bitmap.CompressFormat.PNG或Bitmap.CompressFormat.JPEG
quality:畫(huà)質(zhì),0-100.0表示最低畫(huà)質(zhì)壓縮,100以最高畫(huà)質(zhì)壓縮。對(duì)于PNG等無(wú)損格式的圖片,會(huì)忽略此項(xiàng)設(shè)置。
stream: OutputStream中寫(xiě)入壓縮數(shù)據(jù)。
return: 是否成功壓縮到指定的流。
public static Bitmap createBitmap(Bitmap src) 以src為原圖生成不可變得新圖像
public static Bitmap createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter) 以src為原圖,創(chuàng)建新的圖像,指定新圖像的高寬以及是否可變
public static Bitmap createBitmap(int width, int height, Config config) 創(chuàng)建指定格式、大小的位圖
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height) 以source為原圖,創(chuàng)建新的圖片,指定起始坐標(biāo)以及新圖像的高寬

2) BitmapFactory

Option 參數(shù)類:

Option 參數(shù)類方法 說(shuō)明
public boolean inJustDecodeBounds 如果設(shè)置為true,不獲取圖片,不分配內(nèi)存,但會(huì)返回圖片的高度寬度信息
public int inSampleSize 圖片縮放的倍數(shù)
public int outWidth 獲取圖片的寬度值
public int outHeight 獲取圖片的高度值
public int inDensity 用于位圖的像素壓縮比
public int inTargetDensity 用于目標(biāo)位圖的像素壓縮比(要生成的位圖)
public byte[] inTempStorage 創(chuàng)建臨時(shí)文件,將圖片存儲(chǔ)
public boolean inScaled 設(shè)置為true時(shí)進(jìn)行圖片壓縮,從inDensity到inTargetDensity
public boolean inDither 如果為true,解碼器嘗試抖動(dòng)解碼
public Bitmap.Config inPreferredConfig 設(shè)置解碼器
這個(gè)值是設(shè)置色彩模式,默認(rèn)值是ARGB_8888,在這個(gè)模式下,一個(gè)像素點(diǎn)占用4bytes空間,一般對(duì)透明度不做要求的話,一般采用RGB_565模式,這個(gè)模式下一個(gè)像素點(diǎn)占用2bytes
public String outMimeType 設(shè)置解碼圖像
public boolean inPurgeable 當(dāng)存儲(chǔ)Pixel的內(nèi)存空間在系統(tǒng)內(nèi)存不足時(shí)是否可以被回收
public boolean inInputShareable inPurgeable為true情況下才生效,是否可以共享一個(gè)InputStream
public boolean inPreferQualityOverSpeed 為true則優(yōu)先保證Bitmap質(zhì)量其次是解碼速度
public boolean inMutable 配置Bitmap是否可以更改,比如:在Bitmap上隔幾個(gè)像素加一條線段
public int inScreenDensity 當(dāng)前屏幕的像素密度

工廠方法:

方法 說(shuō)明
public static Bitmap decodeFile(String pathName, Options opts)
public static Bitmap decodeFile(String pathName)
從文件讀取圖片
public static Bitmap decodeFileDescriptor(FileDescriptor fd)
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)
從文件讀取文件 與decodeFile不同的是這個(gè)直接調(diào)用JNI函數(shù)進(jìn)行讀取 效率比較高
public static Bitmap decodeStream(InputStream is)
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)
從輸入流讀取圖片
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)
public static Bitmap decodeResource(Resources res, int id)
public static Bitmap decodeResource(Resources res, int id, Options opts)
從資源文件讀取圖片
public static Bitmap decodeByteArray(byte[] data, int offset, int length)
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)
從數(shù)組讀取圖片

Bitmap.Config inPreferredConfig 枚舉變量 (位圖位數(shù)越高代表其可以存儲(chǔ)的顏色信息越多,圖像越逼真,占用內(nèi)存越大)

常量 說(shuō)明
public static final Bitmap.Config ALPHA_8 代表8位Alpha位圖 每個(gè)像素占用1byte內(nèi)存
public static final Bitmap.Config ARGB_4444 代表16位ARGB位圖 每個(gè)像素占用2byte內(nèi)存
public static final Bitmap.Config ARGB_8888 代表32位ARGB位圖 每個(gè)像素占用4byte內(nèi)存
public static final Bitmap.Config RGB_565 代表8位RGB位圖 每個(gè)像素占用2byte內(nèi)存

3) Bitmap加載方法

  • 從本地(SDcard)文件讀取
public static Bitmap readBitmapFromFileDescriptor(String filePath, int width, int height) {
        try {
            FileInputStream fis = new FileInputStream(filePath);
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
            float srcWidth = options.outWidth;
            float srcHeight = options.outHeight;
            int inSampleSize = 1;

            if (srcHeight > height || srcWidth > width) {
                if (srcWidth > srcHeight) {
                    inSampleSize = Math.round(srcHeight / height);
                } else {
                    inSampleSize = Math.round(srcWidth / width);
                }
            }

            options.inJustDecodeBounds = false;
            options.inSampleSize = inSampleSize;

            return BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
        } catch (Exception ex) {
        }
        return null;
    }
  • 從輸入流讀取文件(網(wǎng)絡(luò)加載)
public static Bitmap readBitmapFromInputStream(InputStream ins, int width, int height) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(ins, null, options);
        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;
        int inSampleSize = 1;

        if (srcHeight > height || srcWidth > width) {
            if (srcWidth > srcHeight) {
                inSampleSize = Math.round(srcHeight / height);
            } else {
                inSampleSize = Math.round(srcWidth / width);
            }
        }

        options.inJustDecodeBounds = false;
        options.inSampleSize = inSampleSize;

        return BitmapFactory.decodeStream(ins, null, options);
    }
  • Resource資源加載

此種方式相當(dāng)?shù)暮馁M(fèi)內(nèi)存 建議采用通過(guò)InputStream ins = resources.openRawResource(resourcesId);
然后使用decodeStream代替decodeResource

public static Bitmap readBitmapFromResource(Resources resources, int resourcesId, int width, int height) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(resources, resourcesId, options);
        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;
        int inSampleSize = 1;

        if (srcHeight > height || srcWidth > width) {
            if (srcWidth > srcHeight) {
                inSampleSize = Math.round(srcHeight / height);
            } else {
                inSampleSize = Math.round(srcWidth / width);
            }
        }

        options.inJustDecodeBounds = false;
        options.inSampleSize = inSampleSize;

        return BitmapFactory.decodeResource(resources, resourcesId, options);
    }

BitmapFactory.decodeResource 加載的圖片可能會(huì)經(jīng)過(guò)縮放,該縮放目前是放在 java 層做的,效率比較低,而且需要消耗 java 層的內(nèi)存。因此,如果大量使用該接口加載圖片,容易導(dǎo)致OOM錯(cuò)誤
BitmapFactory.decodeStream 不會(huì)對(duì)所加載的圖片進(jìn)行縮放,相比之下占用內(nèi)存少,效率更高。
這兩個(gè)接口各有用處,如果對(duì)性能要求較高,則應(yīng)該使用 decodeStream;如果對(duì)性能要求不高,且需要 Android 自帶的圖片自適應(yīng)縮放功能,則可以使用 decodeResource。

  • Assets資源加載方式
public static Bitmap readBitmapFromAssetsFile(Context context, String filePath) {
        Bitmap image = null;
        AssetManager am = context.getResources().getAssets();
        try {
            InputStream is = am.open(filePath);
            image = BitmapFactory.decodeStream(is);
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return image;
    }
  • 從二進(jìn)制數(shù)據(jù)讀取圖片
public static Bitmap readBitmapFromByteArray(byte[] data, int width, int height) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(data, 0, data.length, options);
        float srcWidth = options.outWidth;
        float srcHeight = options.outHeight;
        int inSampleSize = 1;

        if (srcHeight > height || srcWidth > width) {
            if (srcWidth > srcHeight) {
                inSampleSize = Math.round(srcHeight / height);
            } else {
                inSampleSize = Math.round(srcWidth / width);
            }
        }

        options.inJustDecodeBounds = false;
        options.inSampleSize = inSampleSize;

        return BitmapFactory.decodeByteArray(data, 0, data.length, options);
    }

4) Bitmap | Drawable | InputStream | Byte[ ] 之間進(jìn)行轉(zhuǎn)換

  • Drawable -> Bitmap
public static Bitmap drawableToBitmap(Drawable drawable) {
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }
  • Bitmap -> Drawable
public static Drawable bitmapToDrawable(Resources resources, Bitmap bm) {
        Drawable drawable = new BitmapDrawable(resources, bm);
        return drawable;
    }
  • Bitmap -> byte[]
public byte[] bitmap2Bytes(Bitmap bm) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
        return baos.toByteArray();
    }
  • byte[] -> Bitmap
Bitmap bitmap = BitmapFactory.decodeByteArray(byte, 0, b.length);
  • InputStream -> Bitmap
InputStream is  = getResources().openRawResource(id);  
Bitmap bitmap = BitmaoFactory.decodeStream(is);  
  • Bitmap -> InputStream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
InputStream isBm = new ByteArrayInputStream(baos.toByteArray());
  • InputStream -> byte[]
InputStream is = getResources().openRawResource(id);//也可以通過(guò)其他方式接收一個(gè)InputStream對(duì)象  
ByteArrayOutputStream baos = new ByteArrayOutputStream();    
byte[] b = new byte[1024*2];    
int len = 0;    
while ((len = is.read(b, 0, b.length)) != -1)    
{    
   baos.write(b, 0, len);    
   baos.flush();    
}    
byte[] bytes = baos.toByteArray(); 

5) Bitmap常用操作

  • 將Bitmap保存為本地文件
public static void writeBitmapToFile(String filePath, Bitmap b, int quality) {
        try {
            File desFile = new File(filePath);
            FileOutputStream fos = new FileOutputStream(desFile);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            b.compress(Bitmap.CompressFormat.JPEG, quality, bos);
            bos.flush();
            bos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • 圖片壓縮
private static Bitmap compressImage(Bitmap image) {
        if (image == null) {
            return null;
        }
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            byte[] bytes = baos.toByteArray();
            ByteArrayInputStream isBm = new ByteArrayInputStream(bytes);
            Bitmap bitmap = BitmapFactory.decodeStream(isBm);
            return bitmap;
        } catch (OutOfMemoryError e) {
        } finally {
            try {
                if (baos != null) {
                    baos.close();
                }
            } catch (IOException e) {
            }
        }
        return null;
    }
  • 圖片縮放
/**
     * 根據(jù)scale生成一張圖片
     *
     * @param bitmap
     * @param scale  等比縮放值
     * @return
     */
    public static Bitmap bitmapScale(Bitmap bitmap, float scale) {
        Matrix matrix = new Matrix();
        matrix.postScale(scale, scale); // 長(zhǎng)和寬放大縮小的比例
        Bitmap resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        return resizeBmp;
    }
  • 獲取圖片旋轉(zhuǎn)角度
/**
     * 讀取照片exif信息中的旋轉(zhuǎn)角度
     *
     * @param path 照片路徑
     * @return角度
     */
    private static int readPictureDegree(String path) {
        if (TextUtils.isEmpty(path)) {
            return 0;
        }
        int degree = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    degree = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    degree = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    degree = 270;
                    break;
            }
        } catch (Exception e) {
        }
        return degree;
    }
  • 設(shè)置圖片旋轉(zhuǎn)角度
private static Bitmap rotateBitmap(Bitmap b, float rotateDegree) {
        if (b == null) {
            return null;
        }
        Matrix matrix = new Matrix();
        matrix.postRotate(rotateDegree);
        Bitmap rotaBitmap = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), matrix, true);
        return rotaBitmap;
    }
  • 控件轉(zhuǎn)為Bitmap
public static Bitmap convertViewToBitMap(View view){
    // 打開(kāi)圖像緩存
    view.setDrawingCacheEnabled(true);
    // 必須調(diào)用measure和layout方法才能成功保存可視組件的截圖到png圖像文件
    // 測(cè)量View大小
    view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
    // 發(fā)送位置和尺寸到View及其所有的子View
    view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
    // 獲得可視組件的截圖
    Bitmap bitmap = view.getDrawingCache();
    return bitmap;
}
public static Bitmap getBitmapFromView(View view){
    Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(returnedBitmap);
    Drawable bgDrawable = view.getBackground();
    if (bgDrawable != null)
        bgDrawable.draw(canvas);
    else
        canvas.drawColor(Color.WHITE);
    view.draw(canvas);
    return returnedBitmap;
}
  • 圓角圖片
        ImageView img = (ImageView) findViewById(R.id.img);

        Bitmap mBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.def);

        Bitmap bmp = Bitmap.createBitmap(mBitmap.getWidth(),mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        Paint mPaint = new Paint();
        mPaint.setAntiAlias(true);

        RectF rectF = new RectF(0,0,mBitmap.getWidth(),mBitmap.getHeight());
        canvas.drawRoundRect(rectF,mBitmap.getWidth()/2,mBitmap.getHeight()/2,mPaint);
        
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(mBitmap,0,0,mPaint);

        img.setImageBitmap(bmp);
  • 對(duì) bitmap 進(jìn)行裁剪
public Bitmap  bitmapClip(Context context , int id , int x , int y){
     Bitmap map = BitmapFactory.decodeResource(context.getResources(), id);
     map = Bitmap.createBitmap(map, x, y, 120, 120);
     return map;
}

6) Bitmap的內(nèi)存優(yōu)化

在Android應(yīng)用里,最耗費(fèi)內(nèi)存的就是圖片資源。而且在Android系統(tǒng)中,讀取位圖Bitmap時(shí),分給虛擬機(jī)中的圖片的堆棧大小只有8M,如果超出了,就會(huì)出現(xiàn)OutOfMemory異常。所以,對(duì)于圖片的內(nèi)存優(yōu)化,是Android應(yīng)用開(kāi)發(fā)中比較重要的內(nèi)容。

  • 要及時(shí)回收Bitmap的內(nèi)存
    Bitmap類有一個(gè)方法recycle(),從方法名可以看出意思是回收。這里就有疑問(wèn)了,Android系統(tǒng)有自己的垃圾回收機(jī)制,可以不定期的回收掉不使用的內(nèi)存空間,當(dāng)然也包括Bitmap的空間。那為什么還需要這個(gè)方法呢?
    Bitmap類的構(gòu)造方法都是私有的,所以開(kāi)發(fā)者不能直接new出一個(gè)Bitmap對(duì)象,只能通過(guò)BitmapFactory類的各種靜態(tài)方法來(lái)實(shí)例化一個(gè)Bitmap。仔細(xì)查看BitmapFactory的源代碼可以看到,生成Bitmap對(duì)象最終都是通過(guò)JNI調(diào)用方式實(shí)現(xiàn)的。所以,加載Bitmap到內(nèi)存里以后,是包含兩部分內(nèi)存區(qū)域的。簡(jiǎn)單的說(shuō),一部分是Java部分的,一部分是C部分的。這個(gè)Bitmap對(duì)象是由Java部分分配的,不用的時(shí)候系統(tǒng)就會(huì)自動(dòng)回收了,但是那個(gè)對(duì)應(yīng)的C可用的內(nèi)存區(qū)域,虛擬機(jī)是不能直接回收的,這個(gè)只能調(diào)用底層的功能釋放。所以需要調(diào)用recycle()方法來(lái)釋放C部分的內(nèi)存。從Bitmap類的源代碼也可以看到,recycle()方法里也的確是調(diào)用了JNI方法了的。
    那如果不調(diào)用recycle(),是否就一定存在內(nèi)存泄露呢?也不是的。Android的每個(gè)應(yīng)用都運(yùn)行在獨(dú)立的進(jìn)程里,有著獨(dú)立的內(nèi)存,如果整個(gè)進(jìn)程被應(yīng)用本身或者系統(tǒng)殺死了,內(nèi)存也就都被釋放掉了,當(dāng)然也包括C部分的內(nèi)存。
    Android對(duì)于進(jìn)程的管理是非常復(fù)雜的。簡(jiǎn)單的說(shuō),Android系統(tǒng)的進(jìn)程分為幾個(gè)級(jí)別,系統(tǒng)會(huì)在內(nèi)存不足的情況下殺死一些低優(yōu)先級(jí)的進(jìn)程,以提供給其它進(jìn)程充足的內(nèi)存空間。在實(shí)際項(xiàng)目開(kāi)發(fā)過(guò)程中,有的開(kāi)發(fā)者會(huì)在退出程序的時(shí)候使用Process.killProcess(Process.myPid())的方式將自己的進(jìn)程殺死,但是有的應(yīng)用僅僅會(huì)使用調(diào)用Activity.finish()方法的方式關(guān)閉掉所有的Activity。

在 2.3.3 及以下需要調(diào)用 recycle()函數(shù),在 2.3.3 以上 GC 會(huì)自動(dòng)管理,除非你明
確不需要再用。

釋放Bitmap的示例代碼片段:

// 先判斷是否已經(jīng)回收
if(bitmap != null && !bitmap.isRecycled()){ 
        // 回收并且置為null
        bitmap.recycle(); 
        bitmap = null; 
} 
System.gc();

從上面的代碼可以看到,bitmap.recycle()方法用于回收該Bitmap所占用的內(nèi)存,接著將bitmap置空,最后使用System.gc()調(diào)用一下系統(tǒng)的垃圾回收器進(jìn)行回收,可以通知垃圾回收器盡快進(jìn)行回收。這里需要注意的是,調(diào)用System.gc()并不能保證立即開(kāi)始進(jìn)行回收過(guò)程,而只是為了加快回收的到來(lái)。
如何調(diào)用recycle()方法進(jìn)行回收已經(jīng)了解了,那什么時(shí)候釋放Bitmap的內(nèi)存比較合適呢?一般來(lái)說(shuō),如果代碼已經(jīng)不再需要使用Bitmap對(duì)象了,就可以釋放了。釋放內(nèi)存以后,就不能再使用該Bitmap對(duì)象了,如果再次使用,就會(huì)拋出異常。所以一定要保證不再使用的時(shí)候釋放。比如,如果是在某個(gè)Activity中使用Bitmap,就可以在Activity的onStop()或者onDestroy()方法中進(jìn)行回收。

  • 捕獲異常
    為了避免應(yīng)用在分配Bitmap內(nèi)存的時(shí)候出現(xiàn)OutOfMemory異常以后Crash掉,需要特別注意實(shí)例化Bitmap部分的代碼。通常,在實(shí)例化Bitmap的代碼中,一定要對(duì)OutOfMemory異常進(jìn)行捕獲.
Bitmap bitmap = null;
try {
    // 實(shí)例化Bitmap
    bitmap = BitmapFactory.decodeFile(path);
} catch (OutOfMemoryError e) {
    //
}
if (bitmap == null) {
    // 如果實(shí)例化失敗 返回默認(rèn)的Bitmap對(duì)象
    return defaultBitmapMap;
}

這里對(duì)初始化Bitmap對(duì)象過(guò)程中可能發(fā)生的OutOfMemory異常進(jìn)行了捕獲。如果發(fā)生了OutOfMemory異常,應(yīng)用不會(huì)崩潰,而是得到了一個(gè)默認(rèn)的Bitmap圖。
注意:很多開(kāi)發(fā)者會(huì)習(xí)慣性的在代碼中直接捕獲Exception。但是對(duì)于OutOfMemoryError來(lái)說(shuō),這樣做是捕獲不到的。因?yàn)镺utOfMemoryError是一種Error,而不是Exception。在此僅僅做一下提醒,避免寫(xiě)錯(cuò)代碼而捕獲不到OutOfMemoryError。

  • 緩存通用的Bitmap對(duì)象
    有時(shí)候,可能需要在一個(gè)Activity里多次用到同一張圖片。比如一個(gè)Activity會(huì)展示一些用戶的頭像列表,而如果用戶沒(méi)有設(shè)置頭像的話,則會(huì)顯示一個(gè)默認(rèn)頭像,而這個(gè)頭像是位于應(yīng)用程序本身的資源文件中的。
    如果有類似上面的場(chǎng)景,就可以對(duì)同一Bitmap進(jìn)行緩存。如果不進(jìn)行緩存,盡管看到的是同一張圖片文件,但是使用BitmapFactory類的方法來(lái)實(shí)例化出來(lái)的Bitmap,是不同的Bitmap對(duì)象。緩存可以避免新建多個(gè)Bitmap對(duì)象,避免內(nèi)存的浪費(fèi)。
    在Android應(yīng)用開(kāi)發(fā)過(guò)程中,也會(huì)經(jīng)常使用緩存的技術(shù)。這里所說(shuō)的緩存有兩個(gè)級(jí)別,一個(gè)是硬盤緩存,一個(gè)是內(nèi)存緩存。比如說(shuō),在開(kāi)發(fā)網(wǎng)絡(luò)應(yīng)用過(guò)程中,可以將一些從網(wǎng)絡(luò)上獲取的數(shù)據(jù)保存到SD卡中,下次直接從SD卡讀取,而不從網(wǎng)絡(luò)中讀取,從而節(jié)省網(wǎng)絡(luò)流量。這種方式就是硬盤緩存。再比如,應(yīng)用程序經(jīng)常會(huì)使用同一對(duì)象,也可以放到內(nèi)存中緩存起來(lái),需要的時(shí)候直接從內(nèi)存中讀取。這種方式就是內(nèi)存緩存。
  • 壓縮圖片
    如果圖片像素過(guò)大,使用BitmapFactory類的方法實(shí)例化Bitmap的過(guò)程中,需要大于8M的內(nèi)存空間,就必定會(huì)發(fā)生OutOfMemory異常。這個(gè)時(shí)候該如何處理呢?如果有這種情況,則可以將圖片縮小,以減少載入圖片過(guò)程中的內(nèi)存的使用,避免異常發(fā)生。
    使用BitmapFactory.Options設(shè)置inSampleSize就可以縮小圖片。屬性值inSampleSize表示縮略圖大小為原始圖片大小的幾分之一。即如果這個(gè)值為2,則取出的縮略圖的寬和高都是原始圖片的1/2,圖片的大小就為原始大小的1/4。
    如果知道圖片的像素過(guò)大,就可以對(duì)其進(jìn)行縮小。那么如何才知道圖片過(guò)大呢?
    使用BitmapFactory.Options設(shè)置inJustDecodeBounds為true后,再使用decodeFile()等方法,并不會(huì)真正的分配空間,即解碼出來(lái)的Bitmap為null,但是可計(jì)算出原始圖片的寬度和高度,即options.outWidth和options.outHeight。通過(guò)這兩個(gè)值,就可以知道圖片是否過(guò)大了。注意:如果程序的圖片的來(lái)源都是程序包中的資源,或者是自己服務(wù)器上的圖片,圖片的大小是開(kāi)發(fā)者可以調(diào)整的,那么一般來(lái)說(shuō),就只需要注意使用的圖片不要過(guò)大,并且注意代碼的質(zhì)量,及時(shí)回收Bitmap對(duì)象,就能避免OutOfMemory異常的發(fā)生。
    如果程序的圖片來(lái)自外界,這個(gè)時(shí)候就特別需要注意OutOfMemory的發(fā)生。一個(gè)是如果載入的圖片比較大,就需要先縮?。涣硪粋€(gè)是一定要捕獲異常,避免程序Crash。
最后編輯于
?著作權(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)容