1

デバイスの画面ロックをオフにするまではうまくいっていましたが、断続的にうまくいかなくなりました。

問題を追跡し、いくつかの回避策を念頭に置いていますが、問題を回避または削除するための「ベスト プラクティス」があるかどうかを知りたいです。

問題:
アプリケーションのステータスに基づいて画像を変更するアプリケーションがあります。画像は巨大ではありませんが、かなり大きく (231k ~)、リソースとして保存されます。画面を数回回転させた後 (1 つの 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) );
    }

上記のコードは、問題を再現するために使用した唯一の方法です。

解決を試みながら、サンプルを拡張して、imageView が OnDestry でリリースされるようにしました。

    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)が、これは私が読んだ通常のアクティビティのライフサイクルを壊してしまいますが、これはベストプラクティスではありませんか?

Web を精査したにもかかわらず、同様の問題や例にまだ遭遇していないのは奇妙だと思います。他のほとんどの人がこれにどのように対処しているのか疑問に思っています。

ご意見やご感想をお待ちしております。

4

2 に答える 2

2

私はMonoフレームワークを直接操作したことがないため、この問題に真にネイティブな観点からしかアプローチできません。

説明されている症状は、アクティビティのメモリリークを100%示していますが、コードは実際の証拠を示していません。1つのアクティビティと4行のコードのみを含むプロジェクトで問題を本当に生み出すことができるのであれば、Xamarinに提出する必要があるフレームワークのバグであるように思われます。純粋なJavaで同じ単純なプロジェクトを作成して、使用している同じデバイス/エミュレーターで結果がどのように機能するかを確認しようとしましたか?問題がAndroidの特定のバージョンにローカライズされているかどうかを知ることも興味深いでしょう。ネイティブアプリケーションプロジェクトで、この特定の動作をこれまで見たことがありません。

厄介な部分は、ガベージコレクションを強制すると問題がなくなるというあなたの声明です。そうです、そうする必要はありませんが、GCを数回ヒットしても、真のメモリリーク(つまり、リリースされていない参照)は引き続き発生します。各ローテーションでアクティビティを破棄して再作成するというAndroidのパラダイムでは、古いアクティビティがしばらく存続していても、メモリが不足していると、新しいインスタンス用のスペースを確保するために、アクティビティ(およびそのすべての参照)がすばやく収集されます。これが発生しておらず、システムによってトリガーされたGCパスでも各アクティビティが過去に存在している場合は、Monoによって生成されたネイティブコードに参照がスタックしている可能性があります。

興味深いことに、技術的には、回避策は、Bitmapクリアされない静的フィールドにをアタッチすることにより、実際に真のリークを引き起こします。しかし、私はそれがより効率的な動きのように見えることに同意します。より簡単な回避策は、構成の変更を手動で処理するようにアクティビティをコーディングすることでもあります(Monoが異なるかどうかはわかりませんが、これはandroid:configChanges="orientation"マニフェストファイルに追加することで実現されます)。これにより、回転ごとにアクティビティが再作成されなくなりますが、横向きと縦向きでレイアウトが異なる場合は、ビュー階層を再ロードする必要がある場合もあります。ただし、これを行う必要がある場合でも、Acitivityインスタンスは同じになりBitmap、静的フィールドに頼ることなく安全に保存できます。

ただし、ネイティブJavaプロジェクトで同じプロジェクトの問題を再現できない場合は、Monoのバグを報告します。

于 2012-11-14T21:35:46.847 に答える
0

コード全体がないとわかりにくいですが、明らかにメモリ リークが発生しているようです。画面の回転 (アクティビティの破棄/作成による) が原因であることが知られています。Romain Guy によるこの記事と、昨年の IO のこの講演を読むことをお勧めします。

于 2012-11-14T16:53:06.577 に答える