小部件更新时绑定事务失败

3
我正在更新小部件中的一个位图(整个小部件只有一个ImageView),代码如下:
remoteViews.setImageViewBitmap(...)

在一些罕见的情况下(在每天使用 6 个月中发生了 3 次),我会遇到 "!!! FAILED BINDER TRANSACTION !!!"。然后只有电话重启才能解决这个问题。卸载并重新安装无法帮助,只有重启。
我检查了图标的大小,它只有 56 千字节,所以它符合 IPC 内存限制。当我移除 setImageViewBitmap(...) 时,小部件再次工作,但是位图没有更新。因此,问题在于位图本身。即使位图很小,什么可能导致绑定程序交易失败?
目前,我通过将图标保存到 /data 并仅发送 URI 到小部件来解决此问题。但是我想知道,在我明显没有达到 IPC 内存限制的情况下,可能出现什么问题?
编辑: 我忘记提到,它发生在 Android 2.3.5 和 2.3.7 上。
3个回答

4

似乎我们无法在一个intent中解析超过1 MB的内容。

Binder事务失败,因为它太大了。在远程过程调用期间,调用的参数和返回值作为存储在Binder事务缓冲区中的Parcel对象进行传输。如果参数或返回值太大而无法适应事务缓冲区,则调用将失败并抛出TransactionTooLargeException异常。

Binder事务缓冲区具有有限的固定大小,目前为1MB,该大小由进程中所有正在进行的事务共享。因此,即使大多数单个事务的大小都是适度的,在有许多事务正在进行时也可能抛出此异常。

当远程过程调用引发TransactionTooLargeException异常时,有两种可能的结果。要么客户端无法将其请求发送到服务(如果参数太大而无法适应事务缓冲区,则最有可能),要么服务无法将其响应发送回客户端(如果返回值太大而无法适应事务缓冲区,则最有可能)。无法确定实际发生了哪种结果。客户端应假定部分失败已发生。

避免TransactionTooLargeException异常的关键是保持所有事务相对较小。尽量减少创建远程过程调用的参数和返回值的Parcel所需的内存量。避免传输大型字符串数组或大型位图。如果可能,请尝试将大请求分成较小的部分。

如果您正在实现服务,则可以对客户端执行的查询施加大小或复杂性限制。例如,如果结果集可能变得很大,则不要允许客户端一次请求超过几条记录。或者,不要一次返回所有可用数据,而是首先返回基本信息,并根据需要让客户端稍后请求其他信息。


加粗并没有起到帮助的作用,反而让人难以看清。 (我不是给你点踩的那个人) - plackemacher

2
这是由于所有对RemoteViews的更改都是串行化的(例如setInt和setImageViewBitmap)。位图也会被序列化到内部bundle。不幸的是,这个bundle有一个非常小的大小限制。 您可以通过以下方法缩小图像大小来解决此问题:
public static Bitmap scaleDownBitmap(Bitmap photo, int newHeight, Context context) {

final float densityMultiplier = context.getResources().getDisplayMetrics().density;        

int h= (int) (newHeight*densityMultiplier);
int w= (int) (h * photo.getWidth()/((double) photo.getHeight()));

photo=Bitmap.createScaledBitmap(photo, w, h, true);

return photo;
}

选择一个足够小的新高度(每个在屏幕上需要占据的平方像素大约为100),并将其用于您的小部件,这样您的问题就会得到解决 :)

1
如果您正在重复使用remoteViews变量:每次在同一个ImageView上更新位图时,这都会被记录为单独的remote views操作。没有办法清除或去重与RemoteViews相关的操作列表。在这种情况下,您唯一能做的就是重新创建remoteVies而不是无限地重用它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接