【问题标题】:'jobject' must not be IntPtr.Zero when setting textview's text in MonoDroid在 MonoDroid 中设置 textview 的文本时,'jobject' 不能是 IntPtr.Zero
【发布时间】:2013-11-14 09:44:47
【问题描述】:

我正在将 MvvmCross 与 MonoDroid 一起使用。

在视图模型中的计时器中,我每分钟调用一次 RaisePropertyChanged("MinutesRemaining") - MinutesRemaining 是一个整数,指定当前条目结束前的持续时间(是的,这是在 UI 线程上调用的!)。

MinutesRemaining 使用 MvvmCross 绑定到 TextView

在 Xamarin 更新 4.10.1 之前,应用程序将完全崩溃,并且没有错误消息打印到跟踪中 - 它现在在调试时正确中断,并在调用 PropertyChanged 事件时给出以下错误:

MvxBind:Error:281.24 Problem seen during binding execution for binding Text for MinutesRemaining - problem ArgumentException: 'jobject' must not be IntPtr.Zero.
Parameter name: jobject
  at Android.Runtime.JNIEnv.CallVoidMethod (IntPtr jobject, IntPtr jmethod, Android.Runtime.JValue[] parms) [0x00010] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.10.1-branch/9d03ce3e/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.g.cs:499 
  at Android.Widget.TextView.set_TextFormatted (ICharSequence value) [0x00034] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.10.1-branch/9d03ce3e/source/monodroid/src/Mono.Android/platforms/android-14/src/generated/Android.Widget.TextView.cs:1814 
  at Android.Widget.TextView.set_Text (System.String value) [0x00013] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.10.1-branch/9d03ce3e/source/monodroid/src/Mono.Android/platforms/android-14/src/generated/Android.Widget.TextView.cs:1823 
  at Cirrious.MvvmCross.Binding.Droid.Target.MvxTextViewTextTargetBinding.SetValueImpl (System.Object target, System.Object toSet) [0x00000] in <filename unknown>:0 
  at Cirrious.MvvmCross.Binding.Bindings.Target.MvxConvertingTargetBinding.SetValue (System.Object value) [0x00000] in <filename unknown>:0 
  at Cirrious.MvvmCross.Binding.Bindings.MvxFullBinding.UpdateTargetFromSource (System.Object value) [0x00000] in <filename unknown>:0 

它第一次正确绑定 - 只有在随后的 RaisePropertyChanged 调用中才会发生这种情况。相同的代码也适用于 Windows 8 和 Windows Phone。

更新

在用于上述场景的适配器中使用 JavaFinalise 解决了该问题(可在此处找到:MVVMCross Binding Crashes Android Application)。我现在遇到的问题是相同的结果,但是适配器中的第一个视图绑定到父视图模型中的属性(而不是项目)。

用于绑定的代码如下:

public class SubjectFilterAdapter : MvxAdapter {
    private EntityListFragment<TEntity, TViewModel> _owner;

    public SubjectFilterAdapter(Context context, EntityListFragment<TEntity, TViewModel> owner) : base(context, (IMvxAndroidBindingContext)owner.BindingContext) {

        _owner = owner;
    }

    protected override View GetBindableView(View convertView, object dataContext, int templateId) {
        var view = base.GetBindableView(convertView, dataContext, templateId);

        if (templateId == ItemTemplateId && GetPosition(dataContext) == 0) {
            var set = _owner.CreateBindingSet<EntityListFragment<TEntity, TViewModel>, TViewModel>();

            set.Bind(view.FindViewById<TextView>(Resource.Id.SelectedScheduleText))
                .To(x => x.SelectedScheduleText).WithClearBindingKey("SelectedScheduleTextFilterBinding");

            set.Apply();
        }

        return view;
    }

    protected override void JavaFinalize() {
        if (this.BindingContext != null)
            this.BindingContext.ClearAllBindings();
        base.JavaFinalize();
    }
}

一开始(对于前几次更改)它可以正常工作,但之后会引发上述异常。使用 MvvmCross 3.0.14-beta3.

谢谢!

【问题讨论】:

  • 这个问题是否出现在列表中?还是在其他一些控制?您是否在最新代码中看到了这一点 - 例如3.0.14-beta3 二进制文件? (这不包括该链接问题中的 JavaFinalize,但确实包括列表周围的其他更改)
  • JavaFinalize 修复了其中一个 - 另一个未修复(使用视图模型而不是该项目的数据上下文绑定到适配器内的文本视图)。我已经更新到 beta3 二进制文件,问题仍然存在 :(

标签: android xamarin.android xamarin mvvmcross


【解决方案1】:

通过将 listitem/cell 绑定上下文与父上下文混合,您进入了一个相当高级的领域。

为了帮助尝试解释/调试您正在发生的事情,您需要了解所有父生命周期、listitem/cell 生命周期以及相应 MvvmCross 绑定上下文的生命周期。

在父生命周期级别,这通常是 Android ActivityFragment。为了简单起见,我将在此答案的其余部分使用Activity

这个Activity 有几个关键的生命周期事件

  • OnCreate 仅在 Activity 首次启动时调用一次
  • OnDestroy 仅在 Activity 不再显示时调用一次。

MvvmCross 拦截这些事件并:

  • OnCreate 内,它将ViewModel 设置为ActivityDataContext。用户代码——通常是在SetContentView 中扩展的 Xml 代码——然后创建绑定。这些绑定存储在ActivityBindingContext
  • OnDestroy 内,MvvmCross 破坏了BindingContext 内的所有绑定

在我们感兴趣的用户界面中,Activity 拥有一个ListView,而ListView 有一个Adapter 为其设置。在这种情况下,ListView 及其 AdapterDataContext 与其父级相同。

ListView 的生命周期内,列表可能需要显示很多项目。任何时候显示的项目都可能发生变化——无论是因为用户触摸操作还是因为视图模型的变化。为了显示这些项目,ListViewAdapter 询问Views。对于每个项目,它显示Adapter 提供View,并且这些Views 可以重复使用(使用convertView 参数)。然而,有时,这些Views 也没有被重用 - 在这种情况下,即使在 Java/Dalvik View 已被删除并且 Java 最终确定之后,视图对象有时也可能在 C# 中继续存在。

MvvmCross 在其MvxAdapter 中拦截GetView 调用。对于每个调用,它不仅返回View,而且还返回MvxListItemView。这是一个 View 加上一个 BindingContext - 这允许 MvvmCross 用户将每个 MvxListItemView 绑定到其列表项 DataContext

MvxListItemView 被重用时,MvvmCross 只需简单地更改其 DataContext

MvxListItemView 未被重用时 - 当它从 UI 中删除然后 JavaFinalized - MvvmCross 拦截 OnDetachedFromWindow 事件并使用它来将 DataContext 切换到 null。它在 OnDetachedFromWindow 而不是 JavaFinalize 上执行此操作,因为 Window 调用保证在 UI 线程上进行,并且(对我而言)感觉是一个更干净的地方来执行此操作。

请注意,在最近的版本中,其中一些行为发生了微妙的变化 - 但上面的描述对于 v3.0.14 是正确的


有了该背景,您目前正在尝试做的是为ActivityBindingContext 内的ListItemView 的内容创建一个绑定。

这意味着绑定并没有真正了解ListItemView 的生命周期 - 因此即使在ListItemView 已从屏幕上删除并(可能)完成之后,绑定也可以说是活动的。

要解决这个问题...

  • 我认为最简单的方法是更改​​列表项的 DataContext。如果您的 ListItemView 绑定是一个简单的普通绑定 - 如果它包含 MinutesRemaining 属性 - 那么您不应该遇到这些生命周期错误。
  • 您可以尝试在@Jamie 的答案 (https://stackoverflow.com/a/20031690/373321) 中找到的高级绑定 - 但是,我认为这个答案仍然不太正确 - 我认为:
    • 它不能正确处理 listitemview 被删除/最终确定的情况 - 如果在当前代码下第一个列表项滚动到屏幕外,那么我相信您仍然可以看到问题。要使用基于适配器的代码,我认为适配器需要以某种方式从 listitemview 获取回调,以防该视图从 UI 中删除或最终确定。
    • 该答案中的JavaFinalize“有点顽皮”,因为它在AdapterFinalize 期间清除了父Activitys BindingContext。这样做可能没问题,但实际上没有必要 - Activity 自己的 OnDestroy 应该可以处理。

【讨论】:

  • 感谢斯图尔特的广泛回答! :) - 我会考虑添加额外的处理 listitemview。
【解决方案2】:

通过将上述适配器代码更新为:

    public class SubjectFilterAdapter : MvxAdapter {
        private EntityListFragment<TEntity, TViewModel> _owner;

        private MvxFluentBindingDescriptionSet<EntityListFragment<TEntity, TViewModel>, TViewModel> _scheduleBindingSet; 

        public SubjectFilterAdapter(Context context, EntityListFragment<TEntity, TViewModel> owner)
            : base(context, (IMvxAndroidBindingContext)owner.BindingContext) {

            _owner = owner;
        }

        protected override View GetBindableView(View convertView, object dataContext, int templateId) {
            var view = base.GetBindableView(convertView, dataContext, templateId);

            if (templateId == ItemTemplateId && GetPosition(dataContext) == 0) {
                if (_scheduleBindingSet != null) {
                    _owner.BindingContext.ClearBindings("SelectedScheduleTextFilterBinding");

                    _scheduleBindingSet = null;
                }

                _scheduleBindingSet = _owner.CreateBindingSet<EntityListFragment<TEntity, TViewModel>, TViewModel>();

                _scheduleBindingSet.Bind(view.FindViewById<TextView>(Resource.Id.SelectedScheduleText))
                    .To(x => x.SelectedScheduleText).WithClearBindingKey("SelectedScheduleTextFilterBinding");

                _scheduleBindingSet.Apply();
            }

            return view;
        }

        protected override void JavaFinalize() {
            if (this.BindingContext != null)
                this.BindingContext.ClearAllBindings();
            base.JavaFinalize();
        }
    }

【讨论】:

  • 我认为这还不够……我认为您还需要处理从列表中简单地删除列表项视图的情况。在适配器 finalize 中清除绑定上下文也不是一件好事。当我从今天的旅行回来时会完全回答这个问题。
  • 谢谢斯图尔特,非常感谢 - 我会取消它作为答案的标记:)
猜你喜欢
  • 2016-01-17
  • 1970-01-01
  • 2014-12-26
  • 2023-03-15
  • 2012-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-08
相关资源
最近更新 更多