admin 管理员组

文章数量: 887021

android获取位图字节数,Android

1.基本概念

densityDpi?

这是屏幕像素密度,一英寸屏幕有多个像素点。

通过以下方法可以获取

float densityDpi= getResources().getDisplayMetrics().densityDpi;

打印日志,我手机的densityDpi是480。

2020-08-28 11:20:07.242 20647-20647/com.yang.memorytest D/test: densityDpi=480.0

2.图片占用内存大小如何计算?

这里讨论的是占用内存大小,跟存储文件大小不是一个概念。

以下是Bitmap提供的两个获取图片占用内存大小的方法,

public final int getByteCount();

public final int getAllocationByteCount();

(1)getByteCount(),获取图片占用内存大小的理论值;

(2)getAllocationByteCount(),获取图片占用内存大小的实际值;

怎么理解理论值跟实际值呢?

这跟Bitmap内存复用有关。如果图片加载到内存,是存放在新开辟的内存空间里,那么理论值跟实际值相等,如果是复用其他Bitmap的内存空间,而这个空间比图片所需的空间大,那么实际值大于理论值。

有兴趣的,可以自己研究下 BitmapFactory.Options的inBitmap属性。

除了以上在运行时调用方法来获取图片占用大小以外,还可以自己计算。

图片大小=长宽每个像素占的字节数

ARGB8888的图片,每个像素占4个字节;

RGB565的图片,每个像素占2个字节;

有一张宽640 长1136的图片,存放在drawable-xxhdpi文件下,刚好匹配我手机的480dpi,ARGB8888加载为例,

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

Log.d("test", "bitmap.getWidth()=" + bitmap.getWidth());

Log.d("test", "bitmap.getHeight()=" + bitmap.getHeight());

Log.d("test", "bitmap size=" + bitmap.getWidth() * bitmap.getHeight() * 4);

Log.d("test", "bitmap.getAllocationByteCount()=" + bitmap.getAllocationByteCount());

Log.d("test", "bitmap.getByteCount()=" + bitmap.getByteCount());

BitmapFactory.decodeResource默认情况下采用ARGB8888加载,

从日志中可以看出,图片占用内存2908160B,跟getByteCount、getByteCount获取的结果一致。

2020-08-28 14:37:50.529 24368-24368/com.yang.memorytest D/test: bitmap.getWidth()=640

2020-08-28 14:37:50.529 24368-24368/com.yang.memorytest D/test: bitmap.getHeight()=1136

2020-08-28 14:37:50.529 24368-24368/com.yang.memorytest D/test: bitmap size=2908160

2020-08-28 14:37:50.529 24368-24368/com.yang.memorytest D/test: bitmap.getAllocationByteCount()=2908160

2020-08-28 14:37:50.529 24368-24368/com.yang.memorytest D/test: bitmap.getByteCount()=2908160

缩放系数

当图片资源所在文件夹与手机dpi不一致时,图片加载进来会被缩放。

缩放系数=手机dpi/图片资源所在文件夹dpi

序号

文件夹

dpi

匹配dpi区间

1

drawable-mdpi

160

120-160,包含160

2

drawable-hdpi

240

160-240,包含240

3

drawable-xhdpi

320

240-320,包含320

4

drawable-xxhdpi

480

320-480,包含480

5

drawable-xxxhdpi

640

480-640,包含640

6

drawable

160

160

如果手机是480dpi,图片放在drawable-xxhdpi,缩放系数是480/480=1;

如果手机是480dpi,图片放在drawable-xxxhdpi,缩放系数是480/640=0.75;

图片大小计算公式要发生变化:

图片大小=长宽每个像素占的字节数*缩放系数

同样以加载宽640 长1136的图片,用ARGB8888加载为例,图片放在drawable-xxxhdpi文件夹下(手机是480dpi),

加载代码不变,打印结果如下:

从日志可以看出,图片被缩放了,宽由640缩放成480,系数是0.75。

2020-08-28 14:54:53.132 24371-24371/com.yang.memorytest D/test: bitmap.getWidth()=480

2020-08-28 14:54:53.132 24371-24371/com.yang.memorytest D/test: bitmap.getHeight()=852

2020-08-28 14:54:53.132 24371-24371/com.yang.memorytest D/test: bitmap size=1635840

2020-08-28 14:54:53.132 24371-24371/com.yang.memorytest D/test: bitmap.getAllocationByteCount()=1635840

2020-08-28 14:54:53.132 24371-24371/com.yang.memorytest D/test: bitmap.getByteCount()=1635840

如果图片放在drawable-nodpi、raw、assert文件下,加载时不做任何缩放,原样输出。

2.源码分析

源码分析基于Android-28

BitmapFactory.decodeResource,

主要工作:

(1)构建value获取图片参数,包含图片所在文件夹dpi;

(2)res.openRawResource,加载图片流;

(3)调用res.openRawResource解析图片;

public static Bitmap decodeResource(Resources res, int id, Options opts) {

validate(opts);

Bitmap bm = null;

InputStream is = null;

try {

final TypedValue value = new TypedValue();

is = res.openRawResource(id, value);

bm = decodeResourceStream(res, value, is, null, opts);

} catch (Exception e) {

}

......

return bm;

}

BitmapFactory.decodeResourceStream

主要工作:

(1)判断条件density == TypedValue.DENSITY_DEFAULT是否成立,成立,那就是图片在drawable文件夹下,opts.inDensity赋值为160;

(2)判断条件density != TypedValue.DENSITY_NONE是否成立,成立,那就是图片没有在drawable-nodpi文件夹下,opts.inDensity就是文件夹dpi;

(3)当图片在drawable-nodpi文件夹下,opts.inDensity赋值为0;

(4)opts.inTargetDensity赋值为手机dpi。

public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,

@Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {

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

}

BitmapFactory.decodeStream->decodeStreamInternal->BitmapFactory.cpp.nativeDecodeStream->doDecode

主要工作:

(1)根据图片所在dpi、手机dpi,计算缩放系数,scale = (float) targetDensity / density(手机dpi除以图片资源所在文件夹dpi);

(2)如果设置了BitmapFactory.Options inSampleSize,根据参数进行缩放;

(3)根据(1)得到的缩放系数进行缩放,结果四舍五入;

(4)注意:当图片放在drawable-nodpi文件夹下,获取到的density 为0,那么scale默认就是1,不进行缩放。

static jobject doDecode(JNIEnv* env, std::unique_ptr stream,

jobject padding, jobject options) {

...

//(1)

if (env->GetBooleanField(options, gOptions_scaledFieldID)) {

const int density = env->GetIntField(options, gOptions_densityFieldID);

const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);

const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);

if (density != 0 && targetDensity != 0 && density != screenDensity) {

scale = (float) targetDensity / density;

}

}

...

//(2)

if (needsFineScale(codec->getInfo().dimensions(), size, sampleSize)) {

willScale = true;

scaledWidth = codec->getInfo().width() / sampleSize;

scaledHeight = codec->getInfo().height() / sampleSize;

}

....

if (scale != 1.0f) {

willScale = true;

scaledWidth = static_cast(scaledWidth * scale + 0.5f);

scaledHeight = static_cast(scaledHeight * scale + 0.5f);

}

...

}

总结:

(1)图片大小=长宽每个像素占的字节数*缩放系数;

(2)缩放系数=手机dpi/图片资源所在文件夹dpi;

(3)如果图片放在drawable-nodpi、raw、assert文件下,加载时不做任何缩放,原样输出。

以上分析有不对的地方,请指出,互相学习,谢谢哦!

本文标签: android获取位图字节数 Android