草庐IT

android - 位图的 Parcelable 相关大小是多少?

coder 2023-12-03 原文

在 API 级别 19+ 的设备上,我们有 getByteCount() getAllocationByteCount() ,每个都返回 Bitmap 的大小以字节为单位。后者考虑了 Bitmap 的事实。实际上可以表示比它的字节数更小的图像(例如,Bitmap 最初保存了一个更大的图像,但后来与 BitmapFactory.OptionsinBitmap 一起使用来保存一个更小的图像)。

在大多数Android IPC场景中,尤其是那些涉及Parcelable的场景。 ,我们有一个 1MB 的“活页夹交易限制”。

用于确定是否给定 Bitmap对于 IPC 来说足够小,我们是否使用 getByteCount()getAllocationByteCount() ?

我的直觉告诉我们我们使用 getByteCount() ,因为这应该是 Bitmap 中当前图像的字节数正在接受,但我希望有人有更权威的答案。

最佳答案

写入包裹的图像数据大小为getByteCount()加上尺寸Bitmap的颜色表,如果有的话。还有大约 48 个字节的 Bitmap写入包裹的属性。以下代码分析和测试为这些陈述提供了基础。

native 函数写了BitmapParcelthis file 的第 620 行开始.该函数包含在此处并添加了解释:

static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
                                     jlong bitmapHandle,
                                     jboolean isMutable, jint density,
                                     jobject parcel) {
    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
    if (parcel == NULL) {
        SkDebugf("------- writeToParcel null parcel\n");
        return JNI_FALSE;
    }

    android::Parcel* p = android::parcelForJavaObject(env, parcel);

以下七个int s 是写入包裹的第一个数据。在下面描述的测试#2 中,这些值是从样本包中读取的,以确认为 Bitmap 写入的数据大小。 .
p->writeInt32(isMutable);
p->writeInt32(bitmap->colorType());
p->writeInt32(bitmap->alphaType());
p->writeInt32(bitmap->width());
p->writeInt32(bitmap->height());
p->writeInt32(bitmap->rowBytes());
p->writeInt32(density);

如果位图具有颜色图,则将其写入宗地。精确确定位图的包裹大小也必须解决这个问题。
if (bitmap->colorType() == kIndex_8_SkColorType) {
    SkColorTable* ctable = bitmap->getColorTable();
    if (ctable != NULL) {
        int count = ctable->count();
        p->writeInt32(count);
        memcpy(p->writeInplace(count * sizeof(SkPMColor)),
               ctable->lockColors(), count * sizeof(SkPMColor));
        ctable->unlockColors();
    } else {
        p->writeInt32(0);   // indicate no ctable
    }
}

现在我们进入问题的核心:为位图图像写入了多少数据?金额由对 bitmap->getSize() 的调用决定.下面分析该函数。注意这里的值存储在 size ,在后面的代码中用于为图像数据写入blob,并将数据复制到blob所指向的内存中。
size_t size = bitmap->getSize();

使用 blob 将可变大小的数据块写入包裹。如果块小于 40K,则在包裹中“就地”写入。使用 ashmem 将大于 40K 的块写入共享内存,以及 ashmem 的属性地区写在包裹里。 blob 本身只是一个包含一些成员的小描述符,这些成员包括指向块的指针、其长度和指示块是就地还是共享内存的标志。 WriteableBlob 的类定义位于 this file 的第 262 行. writeBlob(的定义) 位于 this file 的第 747 行. writeBlob()确定数据块是否小到可以就地写入。如果是这样,它会扩展包裹缓冲区以腾出空间。如果没有,一个 ashmem区域已创建和配置。在这两种情况下,blob 的成员(指针、大小、标志)都被定义为稍后在复制块时使用。请注意,对于这两种情况,size定义将被复制的数据的大小,就地或共享内存。当writeBlob()完成,目标数据缓冲区定义在 blob和写入包裹的值,描述图像数据块的存储方式(就地或共享内存),以及对于共享内存,ashmem 的属性地区。
android::Parcel::WritableBlob blob;
android::status_t status = p->writeBlob(size, &blob);
if (status) {
    doThrowRE(env, "Could not write bitmap to parcel blob.");
    return JNI_FALSE;
}

现在设置块的目标缓冲区后,可以使用 blob 中的指针复制数据。请注意 size定义复制的数据量。另请注意,只有一个 size .相同的值用于就地和共享内存目标。
bitmap->lockPixels();
const void* pSrc =  bitmap->getPixels();
if (pSrc == NULL) {
    memset(blob.data(), 0, size);
} else {
    memcpy(blob.data(), pSrc, size);
}
bitmap->unlockPixels();

blob.release();
return JNI_TRUE;
}

Bitmap_writeToParcel的分析到此结束.现在很明显,虽然小 (<40k) 图像是就地写入,而较大的图像被写入共享内存,但两种情况下写入的数据大小是相同的。查看该大小的最简单和最直接的方法是使用=""><40k>

确定什么的第二种方法 size是需要了解SkBitmap::getSize() .这是上面分析的代码中用来获取图像块大小的函数。
SkBitmap::getSize()this file 的第 130 行定义.这是:
size_t getSize() const { return fHeight * fRowBytes; }

与此解释相关的同一文件中的另外两个函数是 height() ,在第 98 行定义:
int height() const { return fHeight; }

rowBytes() ,在第 101 行定义:
int rowBytes() const { return fRowBytes; }

我们在 Bitmap_writeToParcel 中看到了这些函数的使用当位图的属性写入包裹时:
p->writeInt32(bitmap->height());
p->writeInt32(bitmap->rowBytes());

通过对这些函数的理解,我们现在看到通过转储包裹中的前几个整数,我们可以看到 fHeight 的值。和 fRowBytes ,从中我们可以推断出 getSize() 返回的值.

下面的第二个代码 fragment 执行此操作并进一步确认写入 Parcel 的数据大小。对应于 getByteCount() 返回的值.

测试 #1

此测试创建一个小于 40K 的位图以生成图像数据的就地存储。然后检查包裹数据大小以显示 getByteCount()确定存储在包裹中的图像数据的大小。

下面代码中的前几条语句是产生一个 Bitmap小于 40K。
logcat输出确认写入Parcel的数据的大小对应于 getByteCount() 返回的值.
byteCount=38400 allocatedByteCount=38400 parcelDataSize=38428
byteCount=7680 allocatedByteCount=38400 parcelDataSize=7708

产生输出的代码如下所示:
    // Setup to get a mutable bitmap less than 40 Kbytes
    String path = "someSmallImage.jpg";
    Bitmap bm0 = BitmapFactory.decodeFile(path);
    // Need it mutable to change height
    Bitmap bm1 = bm0.copy(bm0.getConfig(), true);
    // Chop it to get a size less than 40K
    bm1.setHeight(bm1.getHeight() / 32);

    // Now we have a BitMap with size < 40K for the test
    Bitmap bm2 = bm1.copy(bm0.getConfig(), true);

    // What's the parcel size?
    Parcel p1 = Parcel.obtain();
    bm2.writeToParcel(p1, 0);

    // Expect byteCount and allocatedByteCount to be the same
    Log.i("Demo", String.format("byteCount=%d allocatedByteCount=%d parcelDataSize=%d",
            bm2.getByteCount(), bm2.getAllocationByteCount(), p1.dataSize()));

    // Resize to make byteCount and allocatedByteCount different
    bm2.setHeight(bm2.getHeight() / 4);

    // What's the parcel size?
    Parcel p2 = Parcel.obtain();
    bm2.writeToParcel(p2, 0);

    // Show that byteCount determines size of data written to parcel
    Log.i("Demo", String.format("byteCount=%d allocatedByteCount=%d parcelDataSize=%d",
            bm2.getByteCount(), bm2.getAllocationByteCount(), p2.dataSize()));

    p1.recycle();
    p2.recycle();

测试#2

此测试将位图存储到包裹中,然后转储前几个整数以获取可以推断图像数据大小的值。
logcat添加注释的输出:

//位图属性
bc=12000000 abc=12000000 hgt=1500 wid=2000 rbyt=8000 dens=213

//高度改变后的属性。 byteCount 改变了,分配的ByteCount 没有。
bc=744000 abc=12000000 hgt=93 wid=2000 rbyt=8000 dens=213

//转储包裹数据。包裹数据大小为 48。图像太大,无法就地。
pds=48 mut=1 ctyp=4 atyp=1 hgt=93 wid=2000 rbyt=8000 dens=213

//显示从宗地数据派生的 getSize() 值。它等于 getByteCount()。
bitmap->getSize()= 744000 getByteCount()=744000

产生此输出的代码:
String path = "someImage.jpg";
Bitmap bm0 = BitmapFactory.decodeFile(path);
// Need it mutable to change height
Bitmap bm = bm0.copy(bm0.getConfig(), true);

// For reference, and to provide confidence that the parcel data dump is
// correct, log the bitmap attributes.
Log.i("Demo", String.format("bc=%d abc=%d hgt=%d wid=%d rbyt=%d dens=%d",
        bm.getByteCount(), bm.getAllocationByteCount(),
        bm.getHeight(), bm.getWidth(), bm.getRowBytes(), bm.getDensity()));
// Change size
bm.setHeight(bm.getHeight() / 16);
Log.i("Demo", String.format("bc=%d abc=%d hgt=%d wid=%d rbyt=%d dens=%d",
        bm.getByteCount(), bm.getAllocationByteCount(),
        bm.getHeight(), bm.getWidth(), bm.getRowBytes(), bm.getDensity()));

// Get a parcel and write the bitmap to it.
Parcel p = Parcel.obtain();
bm.writeToParcel(p, 0);

// When the image is too large to be written in-place,
// the parcel data size will be ~48 bytes (when there is no color map).
int parcelSize = p.dataSize();

// What are the first few ints in the parcel?
p.setDataPosition(0);
int mutable   = p.readInt(); //1
int colorType = p.readInt(); //2
int alphaType = p.readInt(); //3
int width     = p.readInt(); //4
int height    = p.readInt(); //5 bitmap->height()
int rowBytes  = p.readInt(); //6 bitmap->rowBytes()
int density   = p.readInt(); //7

Log.i("Demo", String.format("pds=%d mut=%d ctyp=%d atyp=%d hgt=%d wid=%d rbyt=%d dens=%d",
        parcelSize,  mutable, colorType, alphaType, height, width, rowBytes, density));

// From code analysis, we know that the value returned
// by SkBitmap::getSize() is the size of the image data written.
// We also know that the value of getSize() is height()*rowBytes().
// These are the values in ints 5 and 6.
int imageSize = height * rowBytes;

// Show that the size of image data stored is Bitmap.getByteCount()
Log.i("Demo", String.format("bitmap->getSize()= %d getByteCount()=%d", imageSize, bm.getByteCount()));

p.recycle();

关于android - 位图的 Parcelable 相关大小是多少?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31416191/

有关android - 位图的 Parcelable 相关大小是多少?的更多相关文章

  1. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  2. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  3. ruby - 可以通过多少种方法将方法添加到 ruby​​ 对象? - 2

    当谈到运行时自省(introspection)和动态代码生成时,我认为ruby​​没有任何竞争对手,可能除了一些lisp方言。前几天,我正在做一些代码练习来探索ruby​​的动态功能,我开始想知道如何向现有对象添加方法。以下是我能想到的3种方法:obj=Object.new#addamethoddirectlydefobj.new_method...end#addamethodindirectlywiththesingletonclassclass这只是冰山一角,因为我还没有探索instance_eval、module_eval和define_method的各种组合。是否有在线/离线资

  4. HBase Region 简介和建议数量&大小 - 2

    Region是HBase数据管理的基本单位,region有一点像关系型数据的分区。region中存储这用户的真实数据,而为了管理这些数据,HBase使用了RegionSever来管理region。Region的结构hbaseregion的大小设置默认情况下,每个Table起初只有一个Region,随着数据的不断写入,Region会自动进行拆分。刚拆分时,两个子Region都位于当前的RegionServer,但处于负载均衡的考虑,HMaster有可能会将某个Region转移给其他的RegionServer。RegionSplit时机:当1个region中的某个Store下所有StoreFile

  5. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  6. ruby-on-rails - Ruby 中意外的大小写行为 - 2

    我在一段非常简单的代码(如我所想)中得到了一个错误的值:org=4caseorgwhenorg=4val='H'endputsval=>nil请不要生气,我希望我错过了一些非常明显的东西,但我真的想不通。谢谢。 最佳答案 这是典型的Ruby错误。case有两种被调用的方法,一种是你传递一个东西作为分支的基础,另一种是你不传递的东西。如果您确实在case中指定了一个表达式语句然后评估所有其他条件并与===进行比较.在这种情况下org评估为false和org===false显然不是真的。所有其他情况也是如此,它们要么是真的,要么是假的。

  7. ruby - 使用 Ruby,计算 n x m 数组的每一列中有多少个 true 的简单方法是什么? - 2

    给定一个nxmbool数组:[[true,true,false],[false,true,true],[false,true,true]]有什么简单的方法可以返回“该列中有多少个true?”结果应该是[1,3,2] 最佳答案 使用转置得到一个数组,其中每个子数组代表一列,然后将每一列映射到其中的true数:arr.transpose.map{|subarr|subarr.count(true)}这是一个带有inject的版本,应该在1.8.6上运行,没有任何依赖:arr.transpose.map{|subarr|subarr.in

  8. ruby - 改变替换的大小写 - 2

    我有以下内容:text.gsub(/(lower)(upper)/,'\1\2')我可以将\2替换为大写吗?类似于:sed-e's/\(abc\)/\U\1/'这在Ruby中可行吗? 最佳答案 查看gsub文档:str.gsub(模式){|匹配|block}→new_str在block形式中,当前匹配字符串作为参数传入,$1、$2、$`、$&、$'等变量将被适当设置。block返回的值将替换为每次调用的匹配项。"alowerupperb".gsub(/(lower)(upper)/){|s|$1+""+$2.upcase}

  9. ruby-on-rails - 在具有 ActiveRecord 条件的相关模型中按字段排序 - 2

    我正在尝试按Rails相关模型中的字段进行排序。我研究的所有解决方案都没有解决如果相关模型被另一个参数过滤?元素模型classItem相关模型:classPriority我正在使用where子句检索项目:@items=Item.where('company_id=?andapproved=?',@company.id,true).all我需要按相关表格中的“位置”列进行排序。问题在于,在优先级模型中,一个项目可能会被多家公司列出。因此,这些职位取决于他们拥有的company_id。当我显示项目时,它是针对一个公司的,按公司内的职位排序。完成此任务的正确方法是什么?感谢您的帮助。PS-我

  10. ruby - Sinatra:哈希的未定义方法字节大小 - 2

    很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visitthehelpcenter.关闭9年前。我正在创建一个Sinatra应用程序,它采用上传的CSV文件并将其内容放入哈希中。当我像这样在我的app.rb中引用这个散列时:hash=extract_values(path_to_filename)我不断收到此错误消息:undefinedmethod`bytesize'forHash:0x007fc5e28f2b90#object_idfile:utils.rblocation:bytesiz

随机推荐