3

mvvmcross を使用して Android 用のアプリを開発しています。

このアプリケーションでは、スピナーを含むリストが必要です。エミュレーターでアプリをテストすると問題ないように見えますが、スクロールするとすぐにメモリ不足になります。これは、gref が 2000 を超えるためです。実際のデバイスでは gref が高くなる可能性があることはわかっていますが、それでも何か間違ったことをしているに違いないと思います.

BindableList

    <cirrious.mvvmcross.binding.android.views.MvxBindableListView
          android:id="@+id/propertyHolder"
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:layout_below="@id/obsBtLayout"
          android:layout_above="@id/photoframe"          
          local:MvxBind="
          {
            'ItemsSource':{'Path':'PPHolders'},
            'ItemClick':{'Path':'PropertyClickedCommand'}
          }"
          local:MvxItemTemplate="@layout/listitem_property"
        />

ListItem_Property.axml (削除済み)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"              
  xmlns:local="http://schemas.android.com/apk/res/AIPApp.UI.Droid"
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:background="@drawable/ListItemSelector"           
  android:descendantFocusability="beforeDescendants"
  >

  <cirrious.mvvmcross.binding.android.views.MvxBindableSpinner
    android:layout_gravity="center_horizontal"
    android:layout_width="200dip"
    android:layout_height="wrap_content"    
    local:MvxDropDownItemTemplate="@layout/spinneritem_propdropdown"
    local:MvxItemTemplate="@layout/spinneritem_prop"
    local:MvxBind="
    {
      'ItemsSource':{'Path':'CodeTableValues'},      
      'SelectedItem':{'Path':'ObservedCodeTable'},
      'Visibility':{'Path':'IsCodeTableValue','Converter':'Visibility'}
    }"/>     

</LinearLayout>

これは、スクロールするたびにスピナー アイテムを再構築する必要があるためですか? それがバインドされているリストは、リストのすべての項目で異なるためです。したがって、ある listitem では、スピナー リストは 6 項目の長さになり、別の listitem では 3 項目の長さになります。

4

2 に答える 2

4

私はあなたが見ている振る舞いの完全な分析をまだ行っていません.コードの完全なサンプルがなければ、それを行うのは非常に困難です.

ただし、特にXamarin フォーラムの JonPryor に感謝します。少なくとも、一般的なケースで GREF に何が起こっているかをよりよく理解できるようになったと思います。そのため、「なぜ」という質問に答えることができます。


バインドされたリストの一般的なケースは、GREF がインクリメントされることです。

  • バインディングのセットごとに 1 回 - バインディングはハイブリッド C#/Java コンテナー オブジェクトに格納されるため - https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Binding.Droid/MvxJavaContainer.cs
  • すべての ListView アイテムに対して 1 回 - リストで使用されているため
  • バインドされたプロパティまたはイベントを持つ ListView 内のすべての View オブジェクトに対して 1 回 - たとえば、各 ListView アイテム内でaTextViewおよび aにバインドしている場合、C# はこれらのビューへの参照を格納します。Button

あなたの例では、各リスト項目自体にバインドされたリストが含まれます。これにより、必要な GREF の数が増加します。これが、問題が報告されている理由です。


この理解があれば、「どうすればこれを解決できるのか?」という疑問が当然生じるでしょう。

これは簡単に答えられる質問ではありませんが、この問題に取り組む方法はいくつかあると思います。

まず、この問題について Xamarin と話し合うことができます。使用可能な GREF の数を増やす必要があるかもしれません。これらのオブジェクトは Java のメモリ内にあるため、C# でも参照されても害はないのではないでしょうか?

第二に、永続的な参照がすべてのオブジェクトに保存されないように、UI バインディングの実装方法を変更することを検討できます。たとえば、1 回限りのバインディング (ラベルなど) を知っている場合は、は、この機能に XML データ バインディングを使用しません。たとえば、新しいビューを使用して、このバインディングを手動で実行できます。

FindViewById<TView>3 番目に、バインディング コード自体を変更して、ビューへの保持された参照を使用する代わりに、更新時にAndroid ビューを取得するように (ViewModel からビューへの) 一方向バインディングを検討することができます。これは遅くなりますが、必要な GREF の数を減らすことができます。この機能は、明示的に指定された「1 回限り」のバインディングの場合に最も実現しやすい可能性があります。

第 4 に、これはアプリ開発者として最もアクセスしやすいソリューションです。アプリがこれらのバインドされたサブリストを使用しないように UI 実装を変更することを検討できます。たとえば、代わりにラベルを使用できます。スピナー オンデマンド (コードで Click イベントを処理することにより)。

これ以外にも選択肢はあると思いますが…


この分析中に私が自問した 1 つの質問は、この問題が MvvmCross に固有のものなのか、それともすべての MonoDroid アプリケーションで見られる問題なのかということです。

100% 確信があるわけではありませんが、答えは、これはすべての MonoDroid アプリに影響する一般的な問題だということだと思います。ただし、MvvmCross が問題に少し追加されているとも思います。

  • TextViews/labels のようなものの参照を保持することによって
  • 多くの Java オブジェクトを参照するコードを簡単に記述できるようにすることで、

また、これが完全に MvvmCross または MonoDroid の問題であるとは思いません。MonoDroid の実装のためにこの GREF 制限が強調されていますが、ここでの根本的な問題は、実際には一度に多くのことをしようとすることの 1 つです。ビュー。そうは思わないかもしれませんが、MonoDroid はここで私たちに有利に働いていると思います。それは、私たちの UI 実装が少し「太っている」ことを指摘しており、アプリ コードでそれを最適化することを検討する必要があることを示しています。


詳細がわかり次第、この回答を更新しますが、上記の情報で、この状況の「理由」について十分な洞察が得られることを願っています。

于 2012-12-21T18:16:32.340 に答える
3

(これはStuart の優れた回答に対するコメントかもしれませんが、もっと余裕が必要でした...)

問題はMvxBindableListAdapter.GetItem()内にあります:

public override Object GetItem(int position)
{
    return new MvxJavaContainer<object>(GetSourceItem(position));
}

これの問題は、呼び出されるたびに新しい GREF を作成することです (呼び出しのたびに新しいオブジェクトがインスタンス化されるため)。"修正" (回避策?): Don't Do That™</a>。

たとえば、ApiDemo/Tutorials/GridViewTutorial.csサンプルは単に次を返しますnull

public override Java.Lang.Object GetItem (int position)
{
    return null;
}

これは、(結果として)何も呼び出さないため機能しますGetItem()。代わりに、GridViewTutorial.ImageAdapter.GetView()GridViewTutorial.csから (再利用される可能性がある) 値を返します。

public override View GetView (int position, View convertView, ViewGroup parent)
{
    ImageView imageView;

    if (convertView == null) {  // if it's not recycled, initialize some attributes  
        imageView = new ImageView (context);
        imageView.LayoutParameters = new GridView.LayoutParams (85, 85);
        imageView.SetScaleType (ImageView.ScaleType.CenterCrop);
        imageView.SetPadding (8, 8, 8, 8);
    } else {
        imageView = (ImageView)convertView;
    }

    imageView.SetImageResource (thumbIds[position]);
    return imageView;
}

次に、LabelledSections/SeparatedListAdapter.csサンプルがあります。これは、によって返された値を "キャッシュ" しますSeparatedListAdapter.GetItem()(値は実際には "別の場所" で作成されます)。

public override Java.Lang.Object GetItem (int position)
{
    int op = position;
    foreach (var section in sections.Keys) {
        var adapter = sections [section];
        int size = adapter.Count + 1;
        if (position == 0)
            return section;
        if (position < size)
            return adapter.GetItem (position - 1);
        position -= size;
    }
    return null;
}

値は、 SeparatedListAdapters.sectionsディクショナリに格納されます。

を実装するときはBaseAdapter.GetItem()、まず「呼び出し元に値が必要ですか?」と尋ねる必要があります。多くの場合、呼び出し元は独自のコードであるため、メソッドを「スキップ」してGetItem()(メソッドに return を返すnull)、別のメカニズムを使用してアダプタから管理対象データを取得できる場合があります。

から値を返す必要がある場合BaseAdapter.GetItem()は、不必要に値を再作成しないようにする必要があります。キャッシュまたはその他の「ストレージ」メカニズムを使用して、インスタンス化されるオブジェクトの数を減らします。

于 2012-12-21T19:26:35.230 に答える