Android Bitmap深入介绍(一)---基础

在Android应用开发中,我们经常需要跟图片打交道,而图片一个很麻烦的问题是占用内存非常大,经常导致OOM,了解Bitmap相关信息,不同sdk版本中Android图片处理的变化,以及一些优化处理的方式对我们平时开发中对图片的会非常有帮助。

这篇先介绍Bitmap基础内容,关于像素,存储信息,以及加载。

像素

Bitmap的存储可以说包括两个部分,像素以及长,宽,颜色等描述信息。像素是Bitmap最占用内存的地方,长宽和像素位数是用来描述图片的,可以通过这些信息计算出图片的像素占用的内存大小。具体到Bitmap的API是下面这几个接口:

public final int getWidth()
public final int getHeight()
public final Config getConfig()

Config是一个枚举类型,表示图片像素类型,总共有下面几种类型:ALPHA_8 (1),RGB_565 (3),ARGB_4444 (4),ARGB_8888 (5);。表示每一个像素图片组成。实际上下面两种方式获取的数值是相等的:

int b = 1;
switch (bitmap.getConfig()) {
    case ALPHA_8:
        b = 1;
        break;
    case ARGB_4444:
        b = 2;
        break;
    case ARGB_8888:
        b = 4;
        break;
}
int bytes1 = bitmap.getWidth() * bitmap.getHeight() * b;

int bytes2 = bitmap.getByteCount(); //从api12才有的接口

这是由Bitmap相关参数可以计算出Bitmap所占用的像素数,实际上我们放入drawable里面的图片都是已经知道了图片的长宽以及像素组成的,但是直接在Android外面算出的图片像素数量与通过上面的代码计算会有出入的。因为Android对图片做了缩放,这个跟你将图片放入的drawable位置相关。

我们都知道android资源目录中会有drawable-hdpi, drawable-xhdpi,drawable-xxhdpi等目录。这里每个目录都会对应一个density。下面看BitmapFactory.decodeResource方法加载Bitmap的例子:

BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test4, options);
Log.i(LOGTAG, "options: " + options.inDensity + "," + options.inTargetDensity);

decodeResource就是Android内部对Resource的加载方式,这里就不从源码上面一步一步介绍了,它最终会调用decodeResourceStream方法,直接看decodeResourceStream:

public static Bitmap decodeResourceStream(Resources res, TypedValue value,
        InputStream is, Rect pad, Options opts) {
    if (opts == null) {
        opts = new Options();
    }
    if (opts.inDensity == 0 && value != null) {
        final int density = value.density;
        if (density == TypedValue.DENSITY_DEFAULT) {
            opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
        } else if (density != TypedValue.DENSITY_NONE) {
            opts.inDensity = density;
        }
    }
    if (opts.inTargetDensity == 0 && res != null) {
        opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
    }
    return decodeStream(is, pad, opts);
}

options.inDensity表示图片自身默认的像素密度,TypedValue会有一个density,对应着图片来自于哪个drawable目录,因为每一个drawable目录(drawable-hdpi,drawable-xhdpi,drawable-xxhdpi)都对应着一种屏幕,而屏幕就有density,TypedValue的density对应着DisplayMetrics的densityDpi,densityDpi表示每英尺的像素数。options.inTargetDensity是当前手机屏幕对应的densityDpi,最终的像素数是:

bytes = 原始图片宽*(options.inTargetDensity/options.inDensity)*原始图片长*(options.inTargetDensity/options.inDensity)*每个像素点位数

存储与传输

Android图片在不同的sdk版本中存储的地方是不一样的。在2.3及2.3以前,图片像素是存储在native内存中。Android内存分为Java堆和native内存。Android会限制每个应用能使用的最大内存。但是Android对内存的限制是Java堆和native内存的和,把像素数据存放在native区,虚拟机无法自动进行垃圾回收,必须手动使用bitmap.recycle()导致很容易内存泄漏。因为Android的设备monitor也只能够看到Java堆的内存变化,这样其实也不方便调试Bitmap内存。比如在应用中新创建一个图片,根本无法在monitor中看到内存变化。

从3.0开始Android将图片保存在Java堆中,新加载一张图片的时候,也能够立刻从monitor反映出来。另外Java的垃圾回收机制也能够自动回收。然后在4.0后,图片又有了一些变化,那就是在parcel传输的时候,当图片很大时,它会使用ashmem来进行图片的传输,具体可以看我这篇文章Android4.0之后Parcel传输Bitmap源码分析。在6.0的时候,图片的存储又有了很大的变化,底层已经明显增加了将图片保存ashmem的接口了,具体可以可以看我这篇文章Android6.0 Bitmap存储以及Parcel传输源码分析

BitmapFactory

BitmapFactory是用来加载图片的,这个类主要分为三种图片的加载,先把它的API拿出来看一下:


    public static Bitmap decodeResourceStream(Resources res, TypedValue value,
            InputStream is, Rect pad, Options opts) 
    public static Bitmap decodeResource(Resources res, int id, Options opts) 
    public static Bitmap decodeResource(Resources res, int id) 

    public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
    public static Bitmap decodeByteArray(byte[] data, int offset, int length) {

    public static Bitmap decodeFile(String pathName, Options opts) 
    public static Bitmap decodeFile(String pathName)
    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) 
    public static Bitmap decodeStream(InputStream is) 

    public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts)
    public static Bitmap decodeFileDescriptor(FileDescriptor fd)

我们直接看BitmapFactory提供的nativeDecode接口:

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

BitmapFactory对File的decode都会转换为InputStream采用nativeDecodeStream来decode,对Resource的decode会采用decodeAsset,而如果FileDesciptor可以转换为native的fd,会通过nativeDecodeFileDescriptor来decode,另外ByteArray会直接采用nativeDecodeByteArray来decode。需要注意的是,对Resource的decode,BitmapFactory会设置Option的相关参数,最终进行相应的缩放,图片的大小会跟原图有所区别。 具体的内容建议去看看BitmapFactory,了解每种方式的区别,才能够更好地使用接口,选择的时候采用更有效率的方法。

BitmapFactory.Options

下面看一下Options类,我们在加载的时候,可以通过这个参数对图片进行一些处理,前面已经说了inDensity和inTargetDensity。下面看看其他的参数。

inPurgeable

这个参数的用途是当需要使用Bitmap的时候再加载Bitmap,不需要的时候回收Bitmap。在4.1中,使用inPurgeable,加载图片后内存基本不会增高,而不使用inPurgeable加载图片后内存会有明显的增加。

inSampleSize

这是表示采样大小,长和宽会对应乘以1/inSampleSize。用于将图片缩小加载出来的,以免站占用太大内存,适合缩略图。

inJustDecodeBounds

这个设置了true后,用于获取图片的宽度长度信息。下面是个例子:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeFile(path, options);
// options.outWidth 和 options.outHeight就能够获取结果
inPreferredConfig

用于配置图片解码方式,对应的类型Bitmap.Config。如果非null,则会使用它来解码图片

inBitmap

在Android 3.0开始引入了inBitmap设置,通过设置这个参数,在图片加载的时候可以使用之前已经创建了的Bitmap,以便节省内存,避免再次创建一个Bitmap。在Android4.4,新增了允许inBitmap设置的图片与需要加载的图片的大小不同的情况,只要inBitmap的图片比当前需要加载的图片大就好了。

总结

这篇先主要介绍了Bitmap相关基本的信息,Bitmap,BitmapFactory和Options类,以及bitmap的存储。

  • 12
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
android Bitmap用法总结 Bitmap用法总结 1、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); // canvas.setBitmap(bitmap); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); drawable.draw(canvas); return bitmap; } 2、从资源中获取Bitmap Resources res=getResources(); Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.pic); 3、Bitmap → byte[] private byte[] Bitmap2Bytes(Bitmap bm){ ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.PNG, 100, baos); return baos.toByteArray(); } 4、byte[] → Bitmap private Bitmap Bytes2Bimap(byte[] b){ if(b.length!=0){ return BitmapFactory.decodeByteArray(b, 0, b.length); } else { return null; } } 5、保存bitmap static boolean saveBitmap2file(Bitmap bmp,String filename){ CompressFormat format= Bitmap.CompressFormat.JPEG; int quality = 100; OutputStream stream = null; try { stream = new FileOutputStream("/sdcard/" + filename); } catch (FileNotFoundException e) { // TODO Auto-generated catch block Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. e.printStackTrace(); } return bmp.compress(format, quality, stream); } 6、将图片按自己的要求缩放 // 图片源 Bitmap bm = BitmapFactory.decodeStream(getResources() .openRawResource(R.drawable.dog)); // 获得图片的宽高 int width = bm.getWidth(); int height = bm.getHeight(); // 设置想要的大小 int newWidth = 320; int newHeight = 480; // 计算缩放比例 float scaleWidth = ((float) newWidth) / width; float scaleHeight = ((float) newHeight) / height; // 取得想要缩放的matrix参数 Matrix matrix = new Matrix(); matrix.postScale(scaleWidth, scaleHeight); // 得到新的图片 Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true); // 放在画布上 canvas.drawBitmap(newbm, 0, 0, paint); 相关知识链接:http://www.eoeandroid.com/thread-3162-1-1.html 7、bitmap的用法小结 BitmapFactory.Options option = new BitmapFactory.Options(); option.inSampleSize = 2; //将图片设为原来宽高的1/2,防止内存溢出 Bitmap bm = BitmapFactory.decodeFile("",option);//文件流 URL url = new URL(""); InputStream is = url.openStream(); Bitmap bm = BitmapFactory.decodeStream(is); android:scaleType: android:scaleType是控制图片如何resized/moved来匹对ImageView的size。ImageView.ScaleType / android:scaleType值的意义区别: CENTER /center 按图片的原来size居中显示,当图片长/宽超过View的长/宽,则截取图片的居中部分 显示 CENTER_CROP / centerCrop 按比例扩大图片的size居中显示,使得图片长(宽)等于或大于View的长 (宽) CENTER_INSIDE / centerInside 将图片的内容完整居中显示,通过按比例缩小或原来的size使得图片 长/宽等于或小于View的长/宽 Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. FIT_CENTER / fitCenter 把图片按比例扩大/缩小到View的宽度,居中显示 FIT_END / fitEnd 把图片按比例扩大/缩小到View的宽度,显示在View的下部分位置 FIT_START / fitStart 把图片按比例扩大/缩小到View的宽度,显示在View的上部分位置 FIT_XY / fitXY 把图片 不按比例 扩大/缩小到View的大小显示 MATRIX / matrix 用矩阵来绘制,动态缩小放大图片来显示。 //放大缩小图片 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; } //将Drawable转化为Bitmap public static Bitmap drawableToBitmap(Drawable drawable){ int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); Bitmap bitmap = Bitmap.createBitmap(width, height, drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0,0,width,height); drawable.draw(canvas); return bitmap; Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. } //获得圆角图片的方法 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; } //获得带倒影的图片方法 public static Bitmap createReflectionImageWithOrigin(Bitmap bitmap){ final int reflectionGap = 4; int width = bitmap.getWidth(); int height = bitmap.getHeight(); Matrix matrix = new Matrix(); matrix.preScale(1, -1); Bitmap reflectionImage = Bitmap.createBitmap(bitmap, 0, height/2, width, height/2, matrix, false); Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height/2), Config.ARGB_8888); Canvas canvas = new Canvas(bitmapWithReflection); canvas.drawBitmap(bitmap, 0, 0, null); Paint deafalutPaint = new Paint(); Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. canvas.drawRect(0, height,width,height + reflectionGap, deafalutPaint); canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null); Paint paint = new Paint(); LinearGradient shader = new LinearGradient(0, bitmap.getHeight(), 0, bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff, 0x00ffffff, TileMode.CLAMP); paint.setShader(shader); // Set the Transfer mode to be porter duff and destination in paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); // Draw a rectangle using the paint with our linear gradient canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint); return bitmapWithReflection; } }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值