当我将MvxBindableSpinner放入MvxBindableListView时,为什么gref会变得太高?

时间:2012-12-12 15:20:33

标签: xml xamarin.android mvvmcross

我正在使用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>

是否发生了这种情况,因为每次滚动时都必须重建微调器项目?因为它所绑定的列表在列表中的每个项目中都是不同的。因此,在一个列表项上,微调器列表可以是六个项目,另一个可以是3个项目,依此类推。

2 个答案:

答案 0 :(得分:4)

我还没有对您所看到的行为进行全面分析 - 如果没有完整的代码示例则很难做到。

但是,尤其要感谢JonPryor Xamarin forums我相信我现在至少可以更好地了解一般情况下GREF的情况 - 所以我可以回答你'为什么'这个问题。 / p>


绑定列表的一般情况是GREF递增:

在您的示例中,每个列表项本身都包含一个绑定列表 - 这将导致所需GREF数量的倍增 - 这就是您看到报告问题的原因。


有了这种理解,显而易见的问题可能是“我们怎样解决这个问题?”

这不是一个简单的问题,但我认为有一些方法可以解决这个问题。

首先,我们可以与Xamarin讨论这个问题 - 可能是应该增加可用的GREF的数量 - 因为这些对象将在Java中的内存中,那么在C#中引用它们可能没有任何害处好?

其次,我们可以考虑改变实现UI绑定的方式,以便永久引用不会存储到所有对象 - 例如,如果我们知道一次性绑定(例如,对于标签),那么我们可能会看一下不对此功能使用XML数据绑定的路由。例如,我们可以使用新的View手动执行此绑定。

第三,我们可以考虑更改绑定代码本身,以便对于单向绑定(从ViewModel到View),它在更新时使用FindViewById<TView>检索Android视图,而不是使用对视图的保留引用。这会更慢,但会减少所需的GREF数量。在明确声明的“一次性”绑定的情况下,此功能可能是最可实现的。

第四,这是您作为应用开发者可能最容易访问的解决方案,您可以查看更改UI实现,以便应用不使用这些绑定的子列表 - 例如它可以改为使用标签 - 它只按需创建微调器(通过处理代码中的Click事件)。

我确信除此之外还有其他选择......


在此分析中我问自己的一个问题是,这个问题是否是MvvmCross独有的,或者是否是所有MonoDroid应用程序都可能出现的问题。

我不是百分百肯定,但我认为答案是这是一个影响所有MonoDroid应用程序的一般性问题。但是,我也认为MvvmCross在这个问题上增加了一点:

  • 通过坚持TextViews / labels
  • 之类的引用
  • 使您更容易编写引用大量Java对象的代码。

我也不认为这完全只是一个MvvmCross或MonoDroid问题。虽然由于MonoDroid的实现而突出了这个GREF限制,但这里的根本问题实际上是尝试一次做太多 - 所以你真的可以通过对设计/实现进行流式处理来提高应用程序的性能,以便减少使用观点。虽然它可能不喜欢它,但我认为MonoDroid 帮我们一个忙[/ strong> - 它指出我们的UI实现有点“胖”,我们应该考虑在我们的应用程序代码中优化它。


我会在发现更多信息时更新这个答案,但我希望上述信息已经让您对这种情况的“原因”有了很好的了解。

答案 1 :(得分:3)

(这可能是对Stuart's excellent answer的评论,但我需要更多空间......)

问题出在MvxBindableListAdapter.GetItem()

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

这个问题是每次调用它都会创建一个新的GREF(因为每次调用都会实例化一个新对象)。 “修复”(解决方法?):Don't Do That™

例如,ApiDemo/Tutorials/GridViewTutorial.cs示例只返回null

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

这是有效的,因为没有(后果)调用GetItem();相反,GridViewTutorial.cs会从GridViewTutorial.ImageAdapter.GetView()返回一个(可能重复使用的)值:

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()方法(让它返回null)并使用其他机制从适配器获取托管数据。

如果确实需要从BaseAdapter.GetItem()返回一个值,那么您应该确保不会不必要地重新创建值。使用缓存或其他“存储”机制来减少实例化的对象数量。