Android中一张图片需要占用多少内存 |
您所在的位置:网站首页 › 一张照片内存多大 › Android中一张图片需要占用多少内存 |
前置概念-屏幕密度
搞清楚 DisplayMetrics 的两个变量, density 是显示的逻辑密度,是密度与独立像素单元的比例因子, densityDpi 是屏幕每英寸对应多少个点 关于DisplayMetrics更多细节点击这里 图片占内存多少的计算原理找到每个像素占用的字节数*总像素数即可 Android API 有个方便的方法可以获取到占用的内存大小 public final int getByteCount() { // int result permits bitmaps up to 46,340 x 46,340 return getRowBytes() * getHeight(); }getHeight 就是图片的高度(单位:px) 那么getrowBytes()呢 public final int getrowBytes() { if (mRecycled) { Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!"); } return nativeRowBytes(mFinalizer.mNativeBitmap); } #Bitmap.cpp static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) { SkBitmap* bitmap = reinterpret_cast(bitmapHandle) return static_cast(bitmap->rowBytes()); } #SkBitmap.h /** Return the number of bytes between subsequent rows of the bitmap. */ size_t rowBytes() const { return fRowBytes; } # SkBitmap.cpp size_t SkBitmap::ComputeRowBytes(Config c, int width) { return SkColorTypeMinRowBytes(SkBitmapConfigToColorType(c), width); } # SkImageInfo.h static int SkColorTypeBytesPerPixel(SkColorType ct) { static const uint8_t gSize[] = { 0, // Unknown 1, // Alpha_8 2, // RGB_565 2, // ARGB_4444 4, // RGBA_8888 4, // BGRA_8888 1, // kIndex_8 }; SK_COMPILE_ASSERT(SK_ARRAY_COUNT(gSize) == (size_t)(kLastEnum_SkColorType + 1), size_mismatch_with_SkColorType_enum); SkASSERT((size_t)ct //实际上,我们这里的opts是null的,所以在这里初始化。 if (opts == null) { opts = new Options(); } if (opts.inDensity == 0 && value != null) { //密度等于TypedValue.DENSITY_NONE,那么就没有与资源相关的密度,它不应该被缩放 final int density = value.density; if (density == TypedValue.DENSITY_DEFAULT) { //密度等于这个值,那么这个密度应该被视为系统的默认密度值 opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;//默认密度160 } else if (density != TypedValue.DENSITY_NONE) {//不等于此值需要缩放 opts.inDensity = density; //这里density的值如果对应资源目录为hdpi的话,就是240 } } if (opts.inTargetDensity == 0 && res != null) { //inTargetDensity就是当前的手机的密度,比如三星s6时就是640 opts.inTargetDensity = res.getDisplayMetrics().densityDpi; } return decodeStream(is, pad, opts); }我们重点关注两个值 inDensity 和 inTargetDensity,他们与BitmapFactory.cpp文件里面的 density 和 targetDensity相对应 inDensity 就是原始资源的 density,inTargetDensity 就是屏幕的 density。 接着,用到了 nativeDecodeStream 方法,其中最关键的 doDecode 函数的代码: static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) { ...... if (env->GetBooleanField(options, gOptions_scaledFieldID)) { const int density = env->GetIntField(options, gOptions_densityFieldID);//对应hdpi的时候,是240 const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);//三星s6的为640 const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID); if (density != 0 && targetDensity != 0 && density != screenDensity) { scale = (float) targetDensity / density; } } } const bool willScale = scale != 1.0f; ...... SkBitmap decodingBitmap; if (!decoder->decode(stream, &decodingBitmap, prefColorType,decodeMode)) { return nullObjectReturn("decoder->decode returned false"); } //这里这个decodingBitmap就是解码出来的bitmap,大小是图片原始的大小 int scaledWidth = decodingBitmap.width(); int scaledHeight = decodingBitmap.height(); if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) { scaledWidth = int(scaledWidth * scale + 0.5f); scaledHeight = int(scaledHeight * scale + 0.5f); } if (willScale) { const float sx = scaledWidth / float(decodingBitmap.width()); const float sy = scaledHeight / float(decodingBitmap.height()); // TODO: avoid copying when scaled size equals decodingBitmap size SkColorType colorType = colorTypeForScaledOutput(decodingBitmap.colorType()); // FIXME: If the alphaType is kUnpremul and the image has alpha, the // colors may not be correct, since Skia does not yet support drawing // to/from unpremultiplied bitmaps. outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, colorType, decodingBitmap.alphaType())); if (!outputBitmap->allocPixels(outputAllocator, NULL)) { return nullObjectReturn("allocation failed for scaled bitmap"); } // If outputBitmap's pixels are newly allocated by Java, there is no need // to erase to 0, since the pixels were initialized to 0. if (outputAllocator != &javaAllocator) { outputBitmap->eraseColor(0); } SkPaint paint; paint.setFilterLevel(SkPaint::kLow_FilterLevel); SkCanvas canvas(*outputBitmap); canvas.scale(sx, sy); canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint); } ...... }density 其实是decodingBitmap的 densityDpi ,跟这张图片的放置的目录有关(比如 hdpi 是240,xxhdpi 是480), targetDensity 实际上是我们加载图片的目标 densityDpi,三星s6为640。sx 和sy 实际上是约等于 scale 的,因为 scaledWidth 和 scaledHeight 是由 width 和 height 乘以 scale 得到的。我们看到 Canvas 放大了 scale 倍,然后又把读到内存的这张 bitmap 画上去,相当于把这张 bitmap 放大了 scale 倍。 所以回到上面 一张522*686的PNG 图片,我把它放到 drawable-xxhdpi 目录下,在三星s6上加载,占用内存2547360B,其中 density 对应 xxhdpi 为480,targetDensity 对应三星s6的密度为640: 522/480 * 640 * 686/480 *640 * 4 = 2546432B 值还是不一样 精度影响内存占用 outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight, colorType, decodingBitmap.alphaType()));最终输出的 outputBitmap 的大小是scaledWidth*scaledHeight, if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) { scaledWidth = int(scaledWidth * scale + 0.5f); scaledHeight = int(scaledHeight * scale + 0.5f); }在我们的例子中, scaledWidth = int( 522 * 640 / 480f + 0.5) = int(696.5) = 696 scaledHeight = int( 686 * 640 / 480f + 0.5) = int(915.16666…) = 915 915 * 696 * 4 = 2547360 Bitmap 在内存当中占用的大小的影响因素色彩格式,如果是 ARGB8888 那么就是一个像素4个字节,如果是 RGB565 那就是2个字节 原始文件存放的资源目录 目标屏幕的密度 如何优化知道了原因,那么据此即可优化内存使用。 详情可以查看优化Bitmap内存占用 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |