欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android Bitmap詳解及Bitmap的內(nèi)存優(yōu)化

 更新時(shí)間:2017年03月22日 10:20:46   作者:liujc  
這篇文章主要介紹了Android Bitmap詳解及Bitmap的內(nèi)存優(yōu)化的相關(guān)資料,Bitmap是Android系統(tǒng)中的圖像處理的最重要類之一。用它可以獲取圖像文件信息,進(jìn)行圖像剪切、旋轉(zhuǎn)、縮放等操作,并可以指定格式保存圖像文件,需要的朋友可以參考下

Android Bitmap詳解及Bitmap的內(nèi)存優(yōu)化

一、Bitmap:

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

常用方法:

  • public void recycle()  // 回收位圖占用的內(nèi)存空間,把位圖標(biāo)記為Dead
  • 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) //按指定的圖片格式以及畫質(zhì),將圖片轉(zhuǎn)換為輸出流。
    format:壓縮圖像的格式,如Bitmap.CompressFormat.PNG或Bitmap.CompressFormat.JPEG
    quality:畫質(zhì),0-100.0表示最低畫質(zhì)壓縮,100以最高畫質(zhì)壓縮。對(duì)于PNG等無損格式的圖片,會(huì)忽略此項(xiàng)設(shè)置。
    stream: OutputStream中寫入壓縮數(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)以及新圖像的高寬。

 二、BitmapFactory工廠類:

Option 參數(shù)類:

  • public boolean inJustDecodeBounds //如果設(shè)置為true,不獲取圖片,不分配內(nèi)存,但會(huì)返回圖片的高度寬度信息。如果將這個(gè)值置為true,那么在解碼的時(shí)候?qū)⒉粫?huì)返回bitmap,只會(huì)返回這個(gè)bitmap的尺寸。這個(gè)屬性的目的是,如果你只想知道一個(gè)bitmap的尺寸,但又不想將其加載到內(nèi)存時(shí)。這是一個(gè)非常有用的屬性。
  • public int inSampleSize //圖片縮放的倍數(shù),  這個(gè)值是一個(gè)int,當(dāng)它小于1的時(shí)候,將會(huì)被當(dāng)做1處理,如果大于1,那么就會(huì)按照比例(1 / inSampleSize)bitmap的寬和高、降低分辨率,大于1時(shí)這個(gè)值將會(huì)被處置為2的倍數(shù)。例如,width=100,height=100,inSampleSize=2,那么就會(huì)將bitmap處理為,width=50,height=50,寬高降為1 / 2,像素?cái)?shù)降為1 / 4。
  • public int outWidth //獲取圖片的寬度值
  • public int outHeight //獲取圖片的高度值 ,表示這個(gè)Bitmap的寬和高,一般和inJustDecodeBounds一起使用來獲得Bitmap的寬高,但是不加載到內(nèi)存。
  • 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)前屏幕的像素密度

工廠方法:

  • public static Bitmap decodeFile(String pathName, Options opts)  //從文件讀取圖片
  • public static Bitmap decodeFile(String pathName)
  • public static Bitmap decodeStream(InputStream is)  //從輸入流讀取圖片
  • 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)  //從數(shù)組讀取圖片
  • public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts)
  • public static Bitmap decodeFileDescriptor(FileDescriptor fd) //從文件讀取文件 與decodeFile不同的是這個(gè)直接調(diào)用JNI函數(shù)進(jìn)行讀取 效率比較高
  • public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)

Bitmap.Config inPreferredConfig :

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

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

Android中一張圖片(BitMap)占用的內(nèi)存主要和以下幾個(gè)因數(shù)有關(guān):圖片長(zhǎng)度,圖片寬度,單位像素占用的字節(jié)數(shù)。一張圖片(BitMap)占用的內(nèi)存=圖片長(zhǎng)度*圖片寬度*單位像素占用的字節(jié)數(shù)。

三、Bitmap加載方式

Bitmap的加載方式有Resource資源加載、本地(SDcard)加載、網(wǎng)絡(luò)加載等加載方式。

1. 從本地(SDcard)文件讀取

方式一

/**
 * 獲取縮放后的本地圖片
 *
 * @param filePath 文件路徑
 * @param width  寬
 * @param height  高
 * @return
 */
 public static Bitmap readBitmapFromFile(String filePath, int width, int height) {
   BitmapFactory.Options options = new BitmapFactory.Options();
   options.inJustDecodeBounds = true;
   BitmapFactory.decodeFile(filePath, 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.decodeFile(filePath, options);
 }

方式二 (效率高于方式一)

/**
 * 獲取縮放后的本地圖片
 *
 * @param filePath 文件路徑
 * @param width  寬
 * @param height  高
 * @return
 */
 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;
 }

2. 從輸入流中讀取文件(網(wǎng)絡(luò)加載)

/**
   * 獲取縮放后的本地圖片
   *
   * @param ins  輸入流
   * @param width 寬
   * @param height 高
   * @return
   */
  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);
  }

3.Resource資源加載

Res資源加載方式:

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);
}

此種方式相當(dāng)?shù)暮馁M(fèi)內(nèi)存 建議采用decodeStream代替decodeResource 可以如下形式:

public static Bitmap readBitmapFromResource(Resources resources, int resourcesId, int width, int height) {
  InputStream ins = resources.openRawResource(resourcesId);
  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);
}

BitmapFactory.decodeResource 加載的圖片可能會(huì)經(jīng)過縮放,該縮放目前是放在 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。

2.Assets資源加載方式:

/**
 * 獲取縮放后的本地圖片
 *
 * @param filePath 文件路徑,即文件名稱
 * @return
 */
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;
}

4.從二進(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);
  }

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

Drawable轉(zhuǎn)化成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;
}

drawable的獲取方式:Drawable drawable = getResources().getDrawable(R.drawable.ic_launcher);

2.Bitmap轉(zhuǎn)換成Drawable

public static Drawable bitmapToDrawable(Resources resources, Bitmap bm) {
  Drawable drawable = new BitmapDrawable(resources, bm);
  return drawable;
}

3.Bitmap轉(zhuǎn)換成byte[]

public byte[] bitmap2Bytes(Bitmap bm) {
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
  return baos.toByteArray();
}

4.byte[]轉(zhuǎn)換成Bitmap

Bitmap bitmap = BitmapFactory.decodeByteArray(byte, 0, b.length);

5.InputStream轉(zhuǎn)換成Bitmap

InputStream is = getResources().openRawResource(id); 
Bitmap bitmap = BitmaoFactory.decodeStream(is);

6.InputStream轉(zhuǎn)換成byte[]

InputStream is = getResources().openRawResource(id);//也可以通過其他方式接收一個(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();

五、Bitmap簡(jiǎn)單操作

1.將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();
  }
}

2.圖片壓縮:

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;
}

3.圖片縮放:

/**
 * 根據(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;
}

4.獲取圖片旋轉(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;
}

5.設(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;
}

6.通過圖片id獲得Bitmap:

Bitmap bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);

7.通過 assest 獲取 獲得Drawable bitmap:

InputStream in = this.getAssets().open("ic_launcher");
Drawable da = Drawable.createFromStream(in, null);
Bitmap mm = BitmapFactory.decodeStream(in);

8.通過 sdcard 獲得 bitmap

Bitmap bit = BitmapFactory.decodeFile("/sdcard/android.jpg");

9.view轉(zhuǎn)Bitmap

public static Bitmap convertViewToBitmap(View view, int bitmapWidth, int bitmapHeight){
Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
view.draw(new Canvas(bitmap));
return bitmap;
}

10.將控件轉(zhuǎn)換為bitmap

public static Bitmap convertViewToBitMap(View view){
// 打開圖像緩存
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;
}

11.放大縮小圖片

public static Bitmap zoomBitmap(Bitmap bitmap,int w,int h){ 
int width = bitmap.getWidth(); 
int height = bitmap.getHeight(); 
Matrix matrix = new Matrix(); 
float scaleWidht = ((float)w / width); 
float scaleHeight = ((float)h / height); 
matrix.postScale(scaleWidht, scaleHeight); 
Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); 
return newbmp; 
}

12.獲得圓角圖片的方法

public static Bitmap getRoundedCornerBitmap(Bitmap bitmap,float roundPx){ 

Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap 
    .getHeight(), Config.ARGB_8888); 
Canvas canvas = new Canvas(output); 

final int color = 0xff424242; 
final Paint paint = new Paint(); 
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); 
final RectF rectF = new RectF(rect); 

paint.setAntiAlias(true); 
canvas.drawARGB(0, 0, 0, 0); 
paint.setColor(color); 
canvas.drawRoundRect(rectF, roundPx, roundPx, paint); 

paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); 
canvas.drawBitmap(bitmap, rect, rect, paint); 

return output; 
}

13.對(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;
}

六、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)用開發(fā)中比較重要的內(nèi)容。

1. 要及時(shí)回收Bitmap的內(nèi)存

Bitmap類有一個(gè)方法recycle(),從方法名可以看出意思是回收。這里就有疑問了,Android系統(tǒng)有自己的垃圾回收機(jī)制,可以不定期的回收掉不使用的內(nèi)存空間,當(dāng)然也包括Bitmap的空間。那為什么還需要這個(gè)方法呢?

Bitmap類的構(gòu)造方法都是私有的,所以開發(fā)者不能直接new出一個(gè)Bitmap對(duì)象,只能通過BitmapFactory類的各種靜態(tài)方法來實(shí)例化一個(gè)Bitmap。仔細(xì)查看BitmapFactory的源代碼可以看到,生成Bitmap對(duì)象最終都是通過JNI調(diào)用方式實(shí)現(xiàn)的。所以,加載Bitmap到內(nèi)存里以后,是包含兩部分內(nèi)存區(qū)域的。簡(jiǎn)單的說,一部分是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()方法來釋放C部分的內(nèi)存。從Bitmap類的源代碼也可以看到,recycle()方法里也的確是調(diào)用了JNI方法了的。

釋放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()并不能保證立即開始進(jìn)行回收過程,而只是為了加快回收的到來。

如何調(diào)用recycle()方法進(jìn)行回收已經(jīng)了解了,那什么時(shí)候釋放Bitmap的內(nèi)存比較合適呢?一般來說,如果代碼已經(jīng)不再需要使用Bitmap對(duì)象了,就可以釋放了。釋放內(nèi)存以后,就不能再使用該Bitmap對(duì)象了,如果再次使用,就會(huì)拋出異常。所以一定要保證不再使用的時(shí)候釋放。比如,如果是在某個(gè)Activity中使用Bitmap,就可以在Activity的onStop()或者onDestroy()方法中進(jìn)行回收。

2.捕獲異常

為了避免應(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ì)象過程中可能發(fā)生的OutOfMemory異常進(jìn)行了捕獲。如果發(fā)生了OutOfMemory異常,應(yīng)用不會(huì)崩潰,而是得到了一個(gè)默認(rèn)的Bitmap圖。

注意:很多開發(fā)者會(huì)習(xí)慣性的在代碼中直接捕獲Exception。但是對(duì)于OutOfMemoryError來說,這樣做是捕獲不到的。因?yàn)镺utOfMemoryError是一種Error,而不是Exception。在此僅僅做一下提醒,避免寫錯(cuò)代碼而捕獲不到OutOfMemoryError。

3.緩存通用的Bitmap對(duì)象

有時(shí)候,可能需要在一個(gè)Activity里多次用到同一張圖片。比如一個(gè)Activity會(huì)展示一些用戶的頭像列表,而如果用戶沒有設(shè)置頭像的話,則會(huì)顯示一個(gè)默認(rèn)頭像,而這個(gè)頭像是位于應(yīng)用程序本身的資源文件中的。
如果有類似上面的場(chǎng)景,就可以對(duì)同一Bitmap進(jìn)行緩存。如果不進(jìn)行緩存,盡管看到的是同一張圖片文件,但是使用BitmapFactory類的方法來實(shí)例化出來的Bitmap,是不同的Bitmap對(duì)象。緩存可以避免新建多個(gè)Bitmap對(duì)象,避免內(nèi)存的浪費(fèi)。
在Android應(yīng)用開發(fā)過程中,也會(huì)經(jīng)常使用緩存的技術(shù)。這里所說的緩存有兩個(gè)級(jí)別,一個(gè)是硬盤緩存,一個(gè)是內(nèi)存緩存。比如說,在開發(fā)網(wǎng)絡(luò)應(yīng)用過程中,可以將一些從網(wǎng)絡(luò)上獲取的數(shù)據(jù)保存到SD卡中,下次直接從SD卡讀取,而不從網(wǎng)絡(luò)中讀取,從而節(jié)省網(wǎng)絡(luò)流量。這種方式就是硬盤緩存。再比如,應(yīng)用程序經(jīng)常會(huì)使用同一對(duì)象,也可以放到內(nèi)存中緩存起來,需要的時(shí)候直接從內(nèi)存中讀取。這種方式就是內(nèi)存緩存。

4.壓縮圖片

如果圖片像素過大,使用BitmapFactory類的方法實(shí)例化Bitmap的過程中,需要大于8M的內(nèi)存空間,就必定會(huì)發(fā)生OutOfMemory異常。這個(gè)時(shí)候該如何處理呢?如果有這種情況,則可以將圖片縮小,以減少載入圖片過程中的內(nèi)存的使用,避免異常發(fā)生。

使用BitmapFactory.Options設(shè)置inSampleSize就可以縮小圖片。屬性值inSampleSize表示縮略圖大小為原始圖片大小的幾分之一。即如果這個(gè)值為2,則取出的縮略圖的寬和高都是原始圖片的1/2,圖片的大小就為原始大小的1/4。

如果知道圖片的像素過大,就可以對(duì)其進(jìn)行縮小。那么如何才知道圖片過大呢?

使用BitmapFactory.Options設(shè)置inJustDecodeBounds為true后,再使用decodeFile()等方法,并不會(huì)真正的分配空間,即解碼出來的Bitmap為null,但是可計(jì)算出原始圖片的寬度和高度,即options.outWidth和options.outHeight。通過這兩個(gè)值,就可以知道圖片是否過大了。

BitmapFactory.Options opts = new BitmapFactory.Options();
  // 設(shè)置inJustDecodeBounds為true
  opts.inJustDecodeBounds = true;
  // 使用decodeFile方法得到圖片的寬和高
  BitmapFactory.decodeFile(path, opts);
  // 打印出圖片的寬和高
  Log.d("example", opts.outWidth + "," + opts.outHeight);

在實(shí)際項(xiàng)目中,可以利用上面的代碼,先獲取圖片真實(shí)的寬度和高度,然后判斷是否需要跑縮小。如果不需要縮小,設(shè)置inSampleSize的值為1。如果需要縮小,則動(dòng)態(tài)計(jì)算并設(shè)置inSampleSize的值,對(duì)圖片進(jìn)行縮小。需要注意的是,在下次使用BitmapFactory的decodeFile()等方法實(shí)例化Bitmap對(duì)象前,別忘記將opts.inJustDecodeBound設(shè)置回false。否則獲取的bitmap對(duì)象還是null。

注意:如果程序的圖片的來源都是程序包中的資源,或者是自己服務(wù)器上的圖片,圖片的大小是開發(fā)者可以調(diào)整的,那么一般來說,就只需要注意使用的圖片不要過大,并且注意代碼的質(zhì)量,及時(shí)回收Bitmap對(duì)象,就能避免OutOfMemory異常的發(fā)生。
如果程序的圖片來自外界,這個(gè)時(shí)候就特別需要注意OutOfMemory的發(fā)生。一個(gè)是如果載入的圖片比較大,就需要先縮??;另一個(gè)是一定要捕獲異常,避免程序Crash。

 感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!

相關(guān)文章

  • 基于Android實(shí)現(xiàn)跳轉(zhuǎn)到WiFi開關(guān)設(shè)置頁的詳細(xì)步驟

    基于Android實(shí)現(xiàn)跳轉(zhuǎn)到WiFi開關(guān)設(shè)置頁的詳細(xì)步驟

    在Android應(yīng)用開發(fā)中,有時(shí)候需要引導(dǎo)用戶到特定的系統(tǒng)設(shè)置頁面,例如Wi-Fi開關(guān)設(shè)置頁,可以通過隱式Intent來實(shí)現(xiàn)這一功能,以下是詳細(xì)的步驟以及相關(guān)的Kotlin代碼示例,需要的朋友可以參考下
    2024-09-09
  • Android GPS詳解及示例代碼

    Android GPS詳解及示例代碼

    本文主要講解Android GPS,這里整理了GPS 的基礎(chǔ)知識(shí)資料,并提供示例代碼和實(shí)現(xiàn)效果圖,有興趣的小伙伴可以參考下
    2016-08-08
  • Android實(shí)現(xiàn)漸變色的圓弧虛線效果

    Android實(shí)現(xiàn)漸變色的圓弧虛線效果

    最近在學(xué)習(xí)Android的paint類的時(shí)候,學(xué)習(xí)了PathEffect路徑效果和Shader渲染效果。所以就做了下面的一個(gè)效果,其中自定義的view組主要是用DashPathEffect、SweepGradient的API形成的效果。感興趣的朋友們可以參考借鑒,下面來一起看看吧。
    2016-10-10
  • Android實(shí)現(xiàn)爆炸式菜單按鈕彈出效果

    Android實(shí)現(xiàn)爆炸式菜單按鈕彈出效果

    這篇文章主要介紹了Android實(shí)現(xiàn)爆炸式菜單按鈕彈出效果,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05
  • Android中實(shí)現(xiàn)ping功能的多種方法詳解

    Android中實(shí)現(xiàn)ping功能的多種方法詳解

    這篇文章主要介紹了Android中實(shí)現(xiàn)ping功能的多種方法詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-03-03
  • EditText限制小數(shù)點(diǎn)前后位數(shù)的實(shí)例

    EditText限制小數(shù)點(diǎn)前后位數(shù)的實(shí)例

    下面小編就為大家?guī)硪黄狤ditText限制小數(shù)點(diǎn)前后位數(shù)的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-04-04
  • Android實(shí)現(xiàn)自動(dòng)填充短信驗(yàn)證碼功能

    Android實(shí)現(xiàn)自動(dòng)填充短信驗(yàn)證碼功能

    這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)自動(dòng)填充短信驗(yàn)證碼功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • Android 實(shí)現(xiàn)微信長(zhǎng)按菜單 -FloatMenu

    Android 實(shí)現(xiàn)微信長(zhǎng)按菜單 -FloatMenu

    在日常開發(fā)中,長(zhǎng)按某個(gè)view出現(xiàn)個(gè)菜單是很常見的需求,下面小編給大家?guī)砹薃ndroid 實(shí)現(xiàn)微信長(zhǎng)按菜單 -FloatMenu的實(shí)現(xiàn)思路及具體實(shí)現(xiàn)代碼,感興趣的朋友跟隨腳本之家小編一起看看吧
    2018-07-07
  • Android仿支付寶支付密碼輸入框

    Android仿支付寶支付密碼輸入框

    這篇文章主要為大家詳細(xì)介紹了Android仿支付寶支付密碼輸入框的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • gradle配置國(guó)內(nèi)鏡像的實(shí)現(xiàn)

    gradle配置國(guó)內(nèi)鏡像的實(shí)現(xiàn)

    這篇文章主要介紹了gradle配置國(guó)內(nèi)鏡像的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09

最新評(píng)論