android - 在MonoDroid中设置textview的文本时,“jobject”不得为IntPtr.Zero

标签 android xamarin.android xamarin mvvmcross

我将MvvmCross与MonoDroid一起使用。

在 View 模型的计时器中,每分钟我都会调用RaisePropertyChanged("MinutesRemaining")-MinutesRemaining是一个整数,指定直到当前条目结束为止的持续时间(以分钟为单位)(是的,这在UI线程上被调用!)。

使用MvvmCross将MinutesRemaining绑定(bind)到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 

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

更新

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

用于绑定(bind)的代码如下:
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

谢谢!

最佳答案

通过将listitem/单元绑定(bind)上下文与父上下文混合在一起,您将进入一个非常高级的领域。

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

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

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

  • OnCreate仅在首次启动Activity时被调用一次
  • 仅当不再显示OnDestroy时,才会调用
  • Activity

  • MvvmCross拦截这些事件,并:
    OnCreate中的
  • ,它将ViewModel设置为ActivityDataContext。用户代码-通常在SetContentView中夸大的Xml代码-然后创建绑定(bind)。这些绑定(bind)存储在BindingContext
  • ActivityOnDestroy中的
  • ,MvvmCross销毁该BindingContext中的所有绑定(bind)

  • 在我们感兴趣的用户界面中,Activity拥有ListView,并且ListView为其设置了Adapter。在这种情况下,DataContextListView及其Adapter与它的父级相同。

    ListView的生命周期内,列表可能需要显示很多项目。随时显示的项目可能会更改-既由于用户的触摸操作,也由于 View 模型的更改。要显示这些项目,ListViewAdapter询问View。对于每个项目,它显示Adapter提供了View,并且这些View可以重复使用(使用convertView参数)。但是,有时这些View也不会被重用-在这种情况下,即使在删除Java/Dalvik View并完成Java处理之后,View对象有时仍可以存在于C#中。

    MvvmCross截获其GetView中的MvxAdapter调用。对于每个调用,它不仅返回View,而且还返回MvxListItemView。这是一个添加了ViewBindingContext-并允许MvvmCross用户将每个MvxListItemView绑定(bind)到其列表项DataContext

    重用MvxListItemView时,对于MvvmCross只需更改其DataContext就很简单。

    当不重新使用MvxListItemView时-从UI中删除它,然后将JavaFinalize d-MvvmCross截获OnDetachedFromWindow事件,并使用此事件将DataContext切换为null。它是在OnDetachedFromWindow而不是JavaFinalize上执行此操作的,因为可以确保在UI线程上进行Window调用,并且(对我而言)感觉更干净了。

    请注意,此行为在最近的发行版中已发生了细微变化-但以上说明仅适用于v3.0.14

    有了适当的背景,您当前正在尝试做的是在ListItemViewBindingContext内为Activity的内容创建一个绑定(bind)。

    这意味着绑定(bind)实际上并没有对ListItemView的生命周期有很好的了解-因此,即使从屏幕上删除了ListItemView并完成了绑定(bind),该绑定(bind)仍可以保持 Activity 状态。

    要解决此问题...
  • 我认为最简单的方法是更改​​列表项的DataContext。如果您的ListItemView绑定(bind)是简单的常规绑定(bind)-如果它包含MinutesRemaining属性-那么您不应遇到这些生命周期错误。
  • 您可以尝试在@Jamie的答案(https://stackoverflow.com/a/20031690/373321)中找到的高级绑定(bind)-但是,我认为此答案仍然不太正确-我认为:
  • 它不能正确处理listitemview被删除/最终处理的情况-如果第一个列表项在当前代码下在屏幕外滚动,那么我相信您仍然可以看到问题。要使用基于适配器的代码,那么我认为适配器需要以某种方式从listitemview中获取回调,以防将该 View 从UI中删除或确定下来。
  • 该答案中的JavaFinalize有点“顽皮”,因为它在ActivityFinalize期间清除了父Adapter的BindingContext。这样做也许可以,但实际上不是必须的-Activity自己的OnDestroy应该可以处理该问题。
  • 关于android - 在MonoDroid中设置textview的文本时,“jobject”不得为IntPtr.Zero,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19974267/

    相关文章:

    android - 在android中使用.sql文件?

    android camera2 处理每一帧并显示其预览

    android - 如何减少android中的视频文件大小?

    c# - .net maui 中的自定义控件和渲染器会发生什么?

    c# - Android EditText 设置最大长度

    javascript - 带有 javascript 代码提示的 Eclipse Android 项目

    android - 应用程序可以包含用户可以控制应用程序权限的页面吗?

    c# - 通知不振动或显示灯光

    android - 按钮上形状背景的波纹效果

    c# - 安卓进度条消失