在我关闭设备上的屏幕锁定之前一切进展顺利,然后事情开始间歇性地出错。
我已经设法追踪到问题并考虑了一些解决方法,但我想知道是否有避免或消除问题的“最佳实践”。
问题:
我有一个根据应用程序状态更改图像的应用程序。
图像不是很大但很大(231k~)并作为资源存储。
在几次屏幕旋转后(我在一个使用单个 ImageView 的项目中计算了 27 次),加载图像失败并出现类型为“Java.Lang.OutOfMemoryError”的异常
剥离到最简单的项目,以下演示了问题:
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
//get a reference to the ImageView
var imageView = FindViewById<ImageView>(Resource.Id.imageView1);
imageView.SetImageBitmap( Android.Graphics.BitmapFactory.DecodeResource( this.Resources, Resource.Drawable.Ready) );
}
上面的代码是我用来重现问题的唯一方法。
在尝试解决时,我扩展了示例,以便在 OnDestry 中发布 imageView:
protected override void OnDestroy ()
{
base.OnDestroy ();
imageView.SetImageBitmap( null );
imageView.DestroyDrawingCache();
imageView.Dispose();
}
除非我添加了我不想做的 GC.Collect(),否则这没有任何区别。
目前我想到的最好的解决方法是修改代码如下:
static Bitmap _ready = null;
private Bitmap GetReadyImage {
get {
if (_ready == null) {
_ready = Android.Graphics.BitmapFactory.DecodeResource (this.Resources, Resource.Drawable.Ready);
}
return _ready;
}
}
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
//get a reference to the ImageView
imageView = FindViewById<ImageView>(Resource.Id.imageView1);
imageView.SetImageBitmap( GetReadyImage );
}
这依赖于对每个位图的静态引用和每个位图的属性访问器。
我什至可以编写一个将图像存储在静态列表中的方法,以节省为每个不同的属性/变量编写属性访问器。
我或许可以添加标志 ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize |ConfigChanges.KeyboardHidden)
但这会破坏正常的 Activity 生命周期,我读过这不是最佳实践?
我觉得很奇怪,在网上搜索后,我从来没有遇到过类似的问题或例子。我想知道大多数其他人是如何处理这个问题的?
非常感谢任何想法或评论。
最佳答案
因为我没有直接使用 Mono 框架,所以我只能从真正原生的角度来解决这个问题。
所描述的症状 100% 表明 Activity 中存在内存泄漏,但代码没有显示真正的证据。如果您真的可以用仅包含一个 Activity 和那四行代码的项目产生问题,那么在我看来这可能是一个框架错误,应该提交给 Xamarin。您是否尝试过使用纯 Java 创建相同的简单项目以查看结果在您使用的相同设备/模拟器上的表现如何?了解问题是否局限于特定版本的 Android 也很有趣。我以前从未在 native 应用程序项目中看到过这种特殊行为。
尴尬的部分是你说强制垃圾收集可以使问题消失。你是对的,你不应该这样做,但即使你多次点击 GC,真正的内存泄漏(即未释放的引用)仍然会持续存在。在每次旋转时销毁和重新创建 Activity 的 Android 范例是这样的,即使旧的 Activity 存在一段时间,当内存紧张时,它(及其所有引用)也会迅速被收集以为新实例腾出空间。如果这没有发生,并且每个 Activity 都存在于过去甚至系统触发的 GC 通过,则可能在 Mono 生成的 native 代码中有一个卡住的引用。
有趣的是,从技术上讲,您的解决方法实际上确实引入了真正的泄漏,方法是将 Bitmap
附加到一个永远不会被清除的静态字段。但是,我同意相比之下,这似乎是一个更有效的举措。一个更简单的解决方法可能是编写您的 Activity 以手动处理配置更改(我不知道 Mono 是否不同,但这是通过将 android:configChanges="orientation"
添加到 list 文件来实现的).这将防止您的 Activity 在每次旋转时都被重新创建,但如果您有不同的横向和纵向布局,它也可能需要您重新加载 View 层次结构。然而,即使您必须这样做,Acitivity 实例也将是相同的,您可以安全地保存 Bitmap
,而无需求助于静态字段。
但是,如果您无法在 native Java 项目中使用同一项目重现该问题,我会报告 Mono 错误。
关于android - 在 Android 的 Mono 屏幕旋转后设置图像时出现内存错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13383090/