接上一篇。
主要研究下bitmap和drawable的使用,以及两者的区别。
先看测试代码:
package com.example.imagetext;
import java.util.ArrayList;
import java.util.List;
import com.example.imagetext.R.drawable;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;
import android.widget.ImageView;
public class MainActivity extends Activity {
private Drawable drawable1,drawable2,drawable4,drawable3;
private Bitmap bitmap1,bitmap2,bitmap3,bitmap4,bitmap6,bitmap5,bitmap7;
private ImageView iv1,iv2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
drawable1 = getResources().getDrawable(R.drawable.bg_1);
drawable2 = getResources().getDrawable(R.drawable.bg_1);
bitmap1 = ((BitmapDrawable)drawable1).getBitmap();
bitmap2 = ((BitmapDrawable)drawable2).getBitmap();
iv1= (ImageView) findViewById(R.id.iv1);
iv2= (ImageView) findViewById(R.id.iv2);
iv1.setImageResource(R.drawable.bg_1);
iv2.setImageResource(R.drawable.bg_1);
drawable3 = iv1.getDrawable();
drawable4 = iv2.getDrawable();
bitmap3 = ((BitmapDrawable)drawable3).getBitmap();
bitmap4 = ((BitmapDrawable)drawable4).getBitmap();
bitmap5 = BitmapFactory.decodeResource(getResources(), R.drawable.bg_1);
bitmap6=BitmapFactory.decodeResource(getResources(), R.drawable.bg_1);
bitmap7 = BitmapFactory.decodeStream(getResources().openRawResource(R.drawable.bg_1));
if(drawable1.equals(drawable2)){
Log.e("tttext","drawble1==drawable2");
}else{
Log.e("tttext","drawble1!=drawable2");
}
if(drawable3.equals(drawable4)){
Log.e("tttext","drawble3==drawable4");
}else{
Log.e("tttext","drawble3!=drawable4");
}
if(bitmap1.equals(bitmap2)){
Log.e("tttext","bitmap1==bitmap2");
}else{
Log.e("tttext","bitmap1!=bitmap2");
}
if(bitmap3.equals(bitmap4)){
Log.e("tttext","bitmap3==bitmap4");
}else{
Log.e("tttext","bitmap3!=bitmap4");
}
if(bitmap3.equals(bitmap1)){
Log.e("tttext","bitmap3==bitmap1");
}else{
Log.e("tttext","bitmap3!=bitmap1");
}
if(bitmap5.equals(bitmap6)){
Log.e("tttext","bitmap5==bitmap6");
}else{
Log.e("tttext","bitmap5!=bitmap6");
}
Log.e("tttext","bitmap1占用内存:"+getBitmapsize(bitmap1)/1024/1024+"M");
Log.e("tttext","bitmap3占用内存:"+getBitmapsize(bitmap3)/1024/1024+"M");
Log.e("tttext","bitmap5占用内存:"+getBitmapsize(bitmap5)/1024/1024+"M");
Log.e("tttext","bitmap7占用内存:"+getBitmapsize(bitmap7)/1024/1024+"M");
}
@SuppressLint("NewApi")
public long getBitmapsize(Bitmap bitmap) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
return bitmap.getByteCount();
}
// Pre HC-MR1
return bitmap.getRowBytes() * bitmap.getHeight();
}
}
打印结果如下:
好的,现在我们逐一进行分析。
1。 drawable1 = getResources().getDrawable(R.drawable.bg_1);
drawable2 = getResources().getDrawable(R.drawable.bg_1);
bitmap1 = ((BitmapDrawable)drawable1).getBitmap();
bitmap2 = ((BitmapDrawable)drawable2).getBitmap();
从打印结果可以看出,drawable1和drawable2并不是同一个对象,但是它们所持有的bitmap却是同一个对象。上篇文章我已经说过了,当drawable引用的是一个资源图片时,它指向的是一个位图对象bitmapDrawable并且它会去缓存里面寻找是否已经存在这个bitmap,如果有直接复用,否则新建。
2。 iv1.setImageResource(R.drawable.bg_1);
iv2.setImageResource(R.drawable.bg_1);
drawable3 = iv1.getDrawable();
drawable4 = iv2.getDrawable();
bitmap3 = ((BitmapDrawable)drawable3).getBitmap();
bitmap4 = ((BitmapDrawable)drawable4).getBitmap();
这里看到,尽管我们使用.setImageResource方法时,打印出来的结果也一样,但是,不同的是,setImageResource与setImageDrawable所引用的drawble以及bitmap并不是同一个,也就是说setImageResource只会复用setImageResource的位图,setImageDrawable只会复用setImageDrawable的位图,这里要注意。
3。bitmap5 = BitmapFactory.decodeResource(getResources(), R.drawable.bg_1);
bitmap6=BitmapFactory.decodeResource(getResources(), R.drawable.bg_1);
bitmap7 = BitmapFactory.decodeStream(getResources().openRawResource(R.drawable.bg_1));
这里我是直接从资源文件夹里加载的bitmap
,分别用了两种方法,从打印的结果可知,三者均不为同一个bitmap对象,并没有进行复用。
但是,从占用内存大小可知:BitmapFactory.decodeResource得到的bitmap依然是放大了4倍的,(为什么放大的是4倍,可以看看我上篇文章所说的,测试机是320dpi,而这个图片是放在mdpi文件件下的),而BitmapFactory.decodeStream得到的则是真实的没有经过缩放的图片,这也正是两个方法之间的区别。
4。
setImageResource(直接在xml里src引用也是调用的这个方法)
setImageDrawable
setImageBitmap
这几个方法的区别是什么呢?
首先从源码可以看到,setImageDrawable和setImageBitmap这两个方法是一样的,都是调用的setImageDrawable,之所以要区分出一个setImageBitmap是为了方便我们在引用网络图片或者是本地非资源文件的图片时转化成bitmap的缩放。另外这两个方法是异步去加载的。
而setImageResource或者是src这个从源码也可以看到,它们是在主线程中执行的,可能会造成activity的启动的延迟。
5.到底有没有必要调用bitmap.recycle()这个方法去手动回收图片呢,这里应该分场景:
如果是从资源文件夹里加载的图片,最好不要自己去手动释放,因为它会复用缓存里的bitmap但是并不会去判断它是否被回收了,否则很容易出现异常。
如果是从网络上或者是从本地相册之类的文件加载的图片,因为我们本身就算用的流去转成bitmap,就算回收了也不会去复用,这个时候是可以去手动回收的,并且可以和软引用联合使用。
那可能有的人要问了,如果我非要使用资源文件夹里的图片,又非要手动去回收,但是又要保证不出现异常该怎么办呢?比如说一个banner页,这个时候,我建议不要用自带的setImageResource(直接在xml里src引用也是调用的这个方法)
setImageDrawable这两个方法。
应该用bitmap=BitmapFactory.decodeResource和setImageBitmap联合使用,这里并不会复用,并且系统也会根据频幕密度去缩放,避免造成内存的浪费,同时你也可以自己手动去回收bitmap而不会报异常,因为上面我讲过了,用这个方法是系统是不会去复用bitmap的。
6.bitmap和Drawble的具体区别,我再总结下,bitmap表示的是一个位图,可以理解为一个对象,而Drawble表示的是一个图画,即可以是一个位图,也可以是个图形。
bitmap和Drawble都可以进行缩放,但是只有bitmap可以对像素进行精确的操作。
所以当你要精确的压缩放大像素时,就必须用bitmap。比如网络图片的传输和加载。但是这里值得注意的是,我们可以先从流中获取到bitmap的宽高信息,并且计算出你具体想要缩放到的宽高。然后再加载到bitmap,可以一开始就将整个大图加载到bitmap里造成内存溢出,实际上很多时候都是因为这个原因出现的OOM,比如拍照。
下一篇,还有最后一篇,主要针对bitmap说下怎么避免内存溢出和提高效率的情况下的缩放。