Bitmap的建立html
new Bitmap()
的形式去建立,而是經過BitmapFactory
中的靜態方法去建立,如:BitmapFactory.decodeStream(is);//經過InputStream去解析生成Bitmap
(這裏就不貼BitmapFactory
中建立Bitmap
的方法了,你們能夠本身去看它的源碼),咱們跟進
BitmapFactory中建立
Bitmap`的源碼,最終均可以追溯到這幾個native函數private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
Rect padding, Options opts);
private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
Rect padding, Options opts);
private static native Bitmap nativeDecodeAsset(long nativeAsset, Rect padding, Options opts);
private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
int length, Options opts);
複製代碼
而Bitmap
又是Java對象,這個Java對象又是從native,也就是C/C++中產生的,因此,在Android中Bitmap的內存管理涉及到兩部分,一部分是native,另外一部分是dalvik,也就是咱們常說的java堆(若是對java堆與棧不瞭解的同窗能夠戳),到這裏基本就已經瞭解了建立Bitmap的一些內存中的特性(你們可使用adb shell dumpsys meminfo
去查看Bitmap實例化以後的內存使用狀況)。java
Bitmap的使用android
咱們已經知道了BitmapFactory
是如何經過各類資源建立Bitmap
了,那麼咱們如何合理的使用它呢?如下是幾個咱們使用Bitmap
須要關注的點shell
Size緩存
Config.ARGB_8888
的參數去建立一個Bitmap
,這是Google推薦的配置色彩參數,也是Android4.4及以上版本默認建立Bitmap的Config參數(Bitmap.Config.inPreferredConfig
的默認值),那麼每個像素將會佔用4byte,若是一張手機照片的尺寸爲1280×720,那麼咱們能夠很容易的計算出這張圖片佔用的內存大小爲 1280x720x4 = 3686400(byte) = 3.5M,一張未經處理的照片就已經3.5M了! 顯而易見,在開發當中,這是咱們最須要關注的問題,不然分分鐘OOM!Bitmap
的大小到適合的程度啦!辛虧在BitmapFactory
中,咱們能夠很方便的經過BitmapFactory.Options
中的options.inSampleSize
去設置Bitmap
的壓縮比,官方給出的說法是If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory....For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16 the number of pixels. Any value <= 1 is treated the same as 1.bash
很簡潔明瞭啊!也就是說,只要按計算方法設置了這個參數,就能夠完成咱們Bitmap的Size調整了。那麼,應該怎麼調整姿式才比較舒服呢?下面先介紹其中一種經過InputStream
的方式去建立Bitmap
的方法,上一段從Gallery中獲取照片而且將圖片Size調整到合適手機尺寸的代碼:ide
static final int PICK_PICS = 9;
public void startGallery(){
Intent i = new Intent();
i.setAction(Intent.ACTION_PICK);
i.setType("image/*");
startActivityForResult(i,PICK_PICS);
}
private int[] getScreenWithAndHeight(){
WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
return new int[]{dm.widthPixels,dm.heightPixels};
}
/**
*
* @param actualWidth 圖片實際的寬度,也就是options.outWidth
* @param actualHeight 圖片實際的高度,也就是options.outHeight
* @param desiredWidth 你但願圖片壓縮成爲的目的寬度
* @param desiredHeight 你但願圖片壓縮成爲的目的高度
* @return
*/
private int findBestSampleSize(int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {
double wr = (double) actualWidth / desiredWidth;
double hr = (double) actualHeight / desiredHeight;
double ratio = Math.min(wr, hr);
float n = 1.0f;
//這裏咱們爲何要尋找 與ratio最接近的2的倍數呢?
//緣由就在於API中對於inSimpleSize的註釋:最終的inSimpleSize應該爲2的倍數,咱們應該向上取與壓縮比最接近的2的倍數。
while ((n * 2) <= ratio) {
n *= 2;
}
return (int) n;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(resultCode == RESULT_OK){
switch (requestCode){
case PICK_PICS:
Uri uri = data.getData();
InputStream is = null;
try {
is = getContentResolver().openInputStream(uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
BitmapFactory.Options options = new BitmapFactory.Options();
//當這個參數爲true的時候,意味着你能夠在解析時候不申請內存的狀況下去獲取Bitmap的寬和高
//這是調整Bitmap Size一個很重要的參數設置
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream( is,null,options );
int realHeight = options.outHeight;
int realWidth = options.outWidth;
int screenWidth = getScreenWithAndHeight()[0];
int simpleSize = findBestSampleSize(realWidth,realHeight,screenWidth,300);
options.inSampleSize = simpleSize;
//當你但願獲得Bitmap實例的時候,不要忘了將這個參數設置爲false
options.inJustDecodeBounds = false;
try {
is = getContentResolver().openInputStream(uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);
iv.setImageBitmap(bitmap);
try {
is.close();
is = null;
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
複製代碼
咱們來看看對於1080P的魅族Note3拍攝的高清無碼照片這段代碼的功效:
壓縮前:函數
Reuse 上面介紹了BitmapFactory
經過InputStream
去建立Bitmap
的這種方式,以及BitmapFactory.Options.inSimpleSize
和 BitmapFactory.Options.inJustDecodeBounds
的使用方法,但將單個Bitmap加載到UI是簡單的,可是若是咱們須要一次性加載大量的圖片,事情就會變得複雜起來。Bitmap
是吃內存大戶,咱們不但願屢次解析相同的Bitmap
,也不但願可能不會用到的Bitmap
一直存在於內存中,因此,這個場景下,Bitmap
的重用變得異常的重要。 在這裏只介紹一種BitmapFactory.Options.inBitmap
的重用方式,下一篇文章會介紹使用三級緩存來實現Bitmap的重用。性能
根據官方文檔在Android 3.0 引進了BitmapFactory.Options.inBitmap,若是這個值被設置了,decode方法會在加載內容的時候去重用已經存在的bitmap. 這意味着bitmap的內存是被從新利用的,這樣能夠提高性能, 而且減小了內存的分配與回收。然而,使用inBitmap有一些限制。特別是在Android 4.4 以前,只支持同等大小的位圖。 咱們看來看看這個參數最基本的運用方法。ui
new BitmapFactory.Options options = new BitmapFactory.Options();
//inBitmap只有當inMutable爲true的時候是可用的。
options.inMutable = true;
Bitmap reusedBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.reused_btimap,options);
options.inBitmap = reusedBitmap;
複製代碼
這樣,當你在下一次decodeBitmap的時候,將設置了options.inMutable=true
以及options.inBitmap
的Options
傳入,Android就會複用你的Bitmap了,具體實例:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(reuseBitmap());
}
private LinearLayout reuseBitmap(){
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
linearLayout.setOrientation(LinearLayout.VERTICAL);
ImageView iv = new ImageView(this);
iv.setLayoutParams(new ViewGroup.LayoutParams(500,300));
options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//inBitmap只有當inMutable爲true的時候是可用的。
options.inMutable = true;
BitmapFactory.decodeResource(getResources(),R.drawable.big_pic,options);
//壓縮Bitmap到咱們但願的尺寸
//確保不會OOM
options.inSampleSize = findBestSampleSize(options.outWidth,options.outHeight,500,300);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.big_pic,options);
options.inBitmap = bitmap;
iv.setImageBitmap(bitmap);
linearLayout.addView(iv);
ImageView iv1 = new ImageView(this);
iv1.setLayoutParams(new ViewGroup.LayoutParams(500,300));
iv1.setImageBitmap( BitmapFactory.decodeResource(getResources(),R.drawable.big_pic,options));
linearLayout.addView(iv1);
ImageView iv2 = new ImageView(this);
iv2.setLayoutParams(new ViewGroup.LayoutParams(500,300));
iv2.setImageBitmap( BitmapFactory.decodeResource(getResources(),R.drawable.big_pic,options));
linearLayout.addView(iv2);
return linearLayout;
}
複製代碼
以上代碼中,咱們在解析了一次一張1080P分辨率的圖片,而且設置在options.inBitmap
中,而後分別decode了同一張圖片,而且傳入了相同的options
。最終只佔用一份第一次解析Bitmap
的內存。
Recycle 必定要記得及時回收Bitmap,不然如上分析,你的native以及dalvik的內存都會被一直佔用着,最終致使OOM
// 先判斷是否已經回收
if(bitmap != null && !bitmap.isRecycled()){
// 回收而且置爲null
bitmap.recycle();
bitmap = null;
}
System.gc();
複製代碼
Enjoy Android :) 若是有誤,輕噴,歡迎指正。