android - 在 Android 的 Mono 屏幕旋转后设置图像时出现内存错误

标签 android memory-leaks imageview xamarin.android xamarin

在我关闭设备上的屏幕锁定之前一切进展顺利,然后事情开始间歇性地出错。

我已经设法追踪到问题并考虑了一些解决方法,但我想知道是否有避免或消除问题的“最佳实践”。

问题:
我有一个根据应用​​程序状态更改图像的应用程序。 图像不是很大但很大(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/

相关文章:

android - ImageView 在横向模式下被拉长

java - 处理中找不到任何设备

c++ - 执行 memcpy 时内存泄漏

c++ - 了解 LeakSanitizer 输出

java - 在Android中获取图像的点击区域

Android - PNG 图像具有 Alpha channel ,但在 ImageView 中时显示白色背景。我该如何修复?

java - 创建自定义 Android 通知而不使用 RemoteView

android - PerformException Error Performing Single Click Works with 5dp 边距

android - 是否可以为 android studio 中的编辑器设置默认缩放比例?

c# - 如何防止线程使用的对象被释放?如何控制对象的生命周期?