1

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

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

问题:
我有一个应用程序可以根据应用程序状态更改图像。图像不是很大,但相当大(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),但这会破坏我读过的正常活动生命周期不是最佳实践吗?

我觉得很奇怪,在网上搜索过,我还没有遇到过类似的问题或例子。我想知道大多数其他人如何处理这个问题?

非常感谢任何想法或评论。

4

2 回答 2

2

我只能从真正原生的角度来解决这个问题,因为我没有直接使用 Mono 框架。

所描述的症状 100% 表明 Activity 中存在内存泄漏,但代码没有显示任何真实证据。如果您真的可以在仅包含一个 Activity 和那四行代码的项目中产生问题,那么在我看来,这可能是一个框架错误,应该向 Xamarin 提交。您是否尝试过在纯 Java 中创建相同的简单项目,以查看在您使用的同一设备/模拟器上的结果如何?知道问题是否本地化到特定版本的 Android 也会很有趣。我以前从未在本机应用程序项目中看到过这种特殊行为。

尴尬的部分是您声明强制垃圾收集会使问题消失。你是对的,你不应该这样做,但是即使你多次点击 GC,真正的内存泄漏(即未释放的引用)仍然会持续存在。在每次旋转时销毁和重新创建 Activity 的 Android 范例是这样的,即使旧的 Activity 存在一段时间,当内存紧张时,它(及其所有引用)也会很快被收集起来,为新实例腾出空间。如果这没有发生,并且即使系统触发 GC 通过,每个 Activity 仍然存在,则可能是 Mono 生成的本机代码中存在卡住的引用。

有趣的是,从技术上讲,您的解决方法实际上确实引入了真正的泄漏,方法是将 附加Bitmap到永远不会清除的静态字段。但是,我同意相比之下,这似乎是一个更有效的举措。一个更简单的解决方法也可能是编写您的 Activity 以手动处理配置更改(我不知道 Mono 是否不同,但这是通过添加android:configChanges="orientation"到清单文件来完成的)。这将防止您的 Activity 在每次旋转时重新创建,但如果您有不同的横向和纵向布局,它可能还需要您重新加载视图层次结构。但是,即使您必须这样做,Acitivity 实例也将是相同的,您可以安全地保存Bitmap而不使用静态字段。

但是,如果您无法在本地 Java 项目中重现相同项目的问题,我会报告 Mono 错误。

于 2012-11-14T21:35:46.847 回答
0

没有整个代码很难看到,但显然听起来你有内存泄漏。已知屏幕旋转(由于活动的破坏/创建)会导致这些。您可能想阅读Romain Guy 的这篇文章以及去年 IO 的演讲。

于 2012-11-14T16:53:06.577 回答