ReactiveUI WhenActivated在Android上引发ObjectDisposedException

时间:2018-07-23 23:01:18

标签: reactiveui

我们目前遇到WhenActivated的问题,其版本为8。我们不是在使用路由基础结构,而是在使用内部MVVM框架。为了支持ISupportActivation,我们在基础页面中侦听OnAppearingOnDisapearing事件,并手动调用Activate()Deactivate()

以下内容曾经在版本8的Alpha版本中可用,但现在不再存在。在iOS中没有问题。

下面的代码说明了这个问题,在MainPage上绑定了一个ReactiveCommand,然后我们导航到Target hello world页面。可以找到一个仓库here

BaseContentPage

using System;
using ReactiveBug.ViewModels;
using ReactiveUI;
using ReactiveUI.XamForms;
using Xamarin.Forms;

namespace ReactiveBug.Pages
{
    public class BaseContentPage<T> : ReactiveContentPage<T> where T : ReactiveBaseModel
    {
        public BaseContentPage()
        {
            this.Events().Appearing.Subscribe(args =>
            {
                if (ViewModel is ISupportsActivation activation)
                {
                    Console.WriteLine($"Activating {GetType().Name}");

                    activation.Activator?.Activate();
                }
            });

            this.Events().Disappearing.Subscribe(args =>
            {
                if (ViewModel is ISupportsActivation activation)
                {
                    Console.WriteLine($"Deactivating {GetType().Name}");

                    activation.Activator.Deactivate();
                }
            });
        }
    }
}

MainPage VM(带有绑定到命令的标签的空白页)

using System;
using System.Reactive;
using System.Threading.Tasks;
using ReactiveBug.Pages;
using ReactiveUI;
using Xamarin.Forms;

namespace ReactiveBug.ViewModels
{
    public class MainPageViewModel : ReactiveBaseModel
    {
        private ReactiveCommand<Unit, Unit> _labelClickCommand;

        public MainPageViewModel(INavigation navigation) : base(navigation)
        {
            this.WhenActivated(d => { d(RxSetupLabelClickCommand()); });
        }

        public ReactiveCommand<Unit, Unit> LabelClickCommand
        {
            get => _labelClickCommand;
            set => this.RaiseAndSetIfChanged(ref _labelClickCommand, value);
        }

        private async Task InternalLabelClickCommand()
        {
            Console.WriteLine($"{nameof(InternalLabelClickCommand)}");

            var p = new TargetPage();
            var vm = new TargetPageViewModel(p.Navigation);

            p.ViewModel = vm;

            await Navigation.PushAsync(p);
        }

        private IDisposable RxSetupLabelClickCommand()
        {
            Console.WriteLine($"{nameof(RxSetupLabelClickCommand)}");

            LabelClickCommand = ReactiveCommand.CreateFromTask(InternalLabelClickCommand);

            LabelClickCommand
                .IsExecuting
                .Subscribe(isExecuting => Console.Write($"{nameof(LabelClickCommand)}.IsExecuting: {isExecuting}"));

            LabelClickCommand
                .ThrownExceptions
                .Subscribe(exception =>
                    Console.WriteLine($"Error executing {nameof(LabelClickCommand)}.  Ex: {exception.ToString()}"));

            return LabelClickCommand;
        }
    }
}

在Android上运行此命令会导致以下异常堆栈跟踪:

System.ObjectDisposedException: Cannot access a disposed object.
  at System.Reactive.DisposedObserver`1[T].OnNext (T value) [0x00000] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Subjects.Subject`1[T].OnNext (T value) [0x00000] in <e9c1ccec51844dbd92b833a0b4bc960e>:0 
  at System.Reactive.SynchronizedObserver`1[T].OnNextCore (T value) [0x00011] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.ObserverBase`1[T].OnNext (T value) [0x0000d] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Subjects.Subject+AnonymousSubject`2[T,U].OnNext (T value) [0x00000] in <e9c1ccec51844dbd92b833a0b4bc960e>:0 
  at ReactiveUI.ReactiveCommand`2[TParam,TResult].<Execute>b__17_3 () [0x00000] in D:\a\1\s\src\ReactiveUI\ReactiveCommand.cs:860 
  at System.Reactive.Linq.ObservableImpl.Finally`1+_+<>c__DisplayClass2_0[TSource].<Run>b__0 () [0x0000d] in <e9c1ccec51844dbd92b833a0b4bc960e>:0 
  at System.Reactive.Disposables.AnonymousDisposable.Dispose () [0x00010] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Disposables.SingleAssignmentDisposable.set_Disposable (System.IDisposable value) [0x00028] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Producer`1[TSource].SubscribeRaw (System.IObserver`1[T] observer, System.Boolean enableSafeguard) [0x0009a] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.ObservableExtensions.SubscribeSafe[T] (System.IObservable`1[T] source, System.IObserver`1[T] observer) [0x00036] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Linq.ObservableImpl.AsObservable`1[TSource].Run (System.IObserver`1[T] observer, System.IDisposable cancel, System.Action`1[T] setSink) [0x0000f] in <e9c1ccec51844dbd92b833a0b4bc960e>:0 
  at System.Reactive.Producer`1[TSource].SubscribeRaw (System.IObserver`1[T] observer, System.Boolean enableSafeguard) [0x00071] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.ObservableExtensions.SubscribeSafe[T] (System.IObservable`1[T] source, System.IObserver`1[T] observer) [0x00036] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Subjects.ConnectableObservable`2[TSource,TResult].Connect () [0x00019] in <e9c1ccec51844dbd92b833a0b4bc960e>:0 
  at System.Reactive.Linq.ObservableImpl.RefCount`1+_[TSource].Run () [0x00053] in <e9c1ccec51844dbd92b833a0b4bc960e>:0 
  at System.Reactive.Linq.ObservableImpl.RefCount`1[TSource].Run (System.IObserver`1[T] observer, System.IDisposable cancel, System.Action`1[T] setSink) [0x00010] in <e9c1ccec51844dbd92b833a0b4bc960e>:0 
  at System.Reactive.Producer`1[TSource].SubscribeRaw (System.IObserver`1[T] observer, System.Boolean enableSafeguard) [0x00071] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.ObservableExtensions.SubscribeSafe[T] (System.IObservable`1[T] source, System.IObserver`1[T] observer) [0x00036] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Concurrency.ObserveOn`1[TSource].Run (System.IObserver`1[T] observer, System.IDisposable cancel, System.Action`1[T] setSink) [0x00034] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Producer`1[TSource].SubscribeRaw (System.IObserver`1[T] observer, System.Boolean enableSafeguard) [0x00071] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.ObservableExtensions.SubscribeSafe[T] (System.IObservable`1[T] source, System.IObserver`1[T] observer) [0x00036] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.TailRecursiveSink`1[TSource].MoveNext () [0x00194] in <e9c1ccec51844dbd92b833a0b4bc960e>:0 
  at System.Reactive.Concurrency.AsyncLock.Wait (System.Action action) [0x000d0] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.TailRecursiveSink`1[TSource].<Run>b__7_0 (System.Action self) [0x00007] in <e9c1ccec51844dbd92b833a0b4bc960e>:0 
  at System.Reactive.Concurrency.Scheduler+<>c.<Schedule>b__47_0 (System.Action`1[T] _action, System.Action`1[T] self) [0x00014] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Concurrency.Scheduler+<>c__DisplayClass49_0`1[TState].<InvokeRec1>b__0 (TState state1) [0x0001e] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Concurrency.Scheduler.InvokeRec1[TState] (System.Reactive.Concurrency.IScheduler scheduler, System.Reactive.Concurrency.Scheduler+Pair`2[T1,T2] pair) [0x0004a] in <99f8205c51c44bb480747b577b8001ff>:0 
  at (wrapper delegate-invoke) System.Func`3[System.Reactive.Concurrency.IScheduler,System.Reactive.Concurrency.Scheduler+Pair`2[System.Action`1[System.Action],System.Action`2[System.Action`1[System.Action],System.Action`1[System.Action`1[System.Action]]]],System.IDisposable].invoke_TResult_T1_T2(System.Reactive.Concurrency.IScheduler,System.Reactive.Concurrency.Scheduler/Pair`2<System.Action`1<System.Action>, System.Action`2<System.Action`1<System.Action>, System.Action`1<System.Action`1<System.Action>>>>)
  at System.Reactive.Concurrency.ImmediateScheduler.Schedule[TState] (TState state, System.Func`3[T1,T2,TResult] action) [0x00014] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Concurrency.Scheduler.Schedule[TState] (System.Reactive.Concurrency.IScheduler scheduler, TState state, System.Action`2[T1,T2] action) [0x00042] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Concurrency.Scheduler.Schedule (System.Reactive.Concurrency.IScheduler scheduler, System.Action`1[T] action) [0x0001c] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.TailRecursiveSink`1[TSource].Run (System.Collections.Generic.IEnumerable`1[T] sources) [0x00068] in <e9c1ccec51844dbd92b833a0b4bc960e>:0 
  at System.Reactive.Linq.ObservableImpl.Catch`1[TSource].Run (System.IObserver`1[T] observer, System.IDisposable cancel, System.Action`1[T] setSink) [0x0000f] in <e9c1ccec51844dbd92b833a0b4bc960e>:0 
  at System.Reactive.Producer`1[TSource].Run (System.Reactive.Concurrency.IScheduler _, System.Reactive.Producer`1+State[TSource] x) [0x00000] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Concurrency.ScheduledItem`2[TAbsolute,TValue].InvokeCore () [0x00000] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Concurrency.ScheduledItem`1[TAbsolute].Invoke () [0x0000d] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Concurrency.CurrentThreadScheduler+Trampoline.Run (System.Reactive.Concurrency.SchedulerQueue`1[TAbsolute] queue) [0x00040] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Concurrency.CurrentThreadScheduler.Schedule[TState] (TState state, System.TimeSpan dueTime, System.Func`3[T1,T2,TResult] action) [0x00046] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Concurrency.LocalScheduler.Schedule[TState] (TState state, System.Func`3[T1,T2,TResult] action) [0x0000e] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Producer`1[TSource].SubscribeRaw (System.IObserver`1[T] observer, System.Boolean enableSafeguard) [0x0005c] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.Reactive.Producer`1[TSource].Subscribe (System.IObserver`1[T] observer) [0x0000e] in <99f8205c51c44bb480747b577b8001ff>:0 
  at System.ObservableExtensions.Subscribe[T] (System.IObservable`1[T] source) [0x00023] in <99f8205c51c44bb480747b577b8001ff>:0 Unhandled Exception:

System.ObjectDisposedException: Cannot access a disposed object.


   at ReactiveUI.ReactiveCommandBase`2[TParam,TResult].ICommandExecute (System.Object parameter) [0x00048] in D:\a\1\s\src\ReactiveUI\ReactiveCommand.cs:721 
   at ReactiveUI.ReactiveCommand.System.Windows.Input.ICommand.Execute (System.Object parameter) [0x00000] in D:\a\1\s\src\ReactiveUI\ReactiveCommand.cs:622 
   at Xamarin.Forms.TapGestureRecognizer.SendTapped (Xamarin.Forms.View sender) [0x00018] in D:\a\1\s\Xamarin.Forms.Core\TapGestureRecognizer.cs:44 
   at Xamarin.Forms.Platform.Android.TapGestureHandler.OnTap (System.Int32 count) [0x00028] in D:\a\1\s\Xamarin.Forms.Platform.Android\TapGestureHandler.cs:37 
   at Xamarin.Forms.Platform.Android.InnerGestureListener.Android.Views.GestureDetector.IOnGestureListener.OnSingleTapUp (Android.Views.MotionEvent e) [0x00014] in D:\a\1\s\Xamarin.Forms.Platform.Android\InnerGestureListener.cs:140 
   at Android.Views.GestureDetector+IOnGestureListenerInvoker.n_OnSingleTapUp_Landroid_view_MotionEvent_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_e) [0x0000f] in <263adecfa58f4c449f1ff56156d886fd>:0 
   at (wrapper dynamic-method) System.Object.81a6100d-59c5-43e9-a3f0-6c38c921f2aa(intptr,intptr,intptr)
UNHANDLED EXCEPTION:
System.ObjectDisposedException: Cannot access a disposed object.
  at (wrapper dynamic-method) System.Object.81a6100d-59c5-43e9-a3f0-6c38c921f2aa(intptr,intptr,intptr)
  at (wrapper managed-to-native) Java.Interop.NativeMethods.java_interop_jnienv_call_nonvirtual_boolean_method_a(intptr,intptr&,intptr,intptr,intptr,Java.Interop.JniArgumentValue*)
  at Java.Interop.JniEnvironment+InstanceMethods.CallNonvirtualBooleanMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x00073] in <7802aa64ad574c33adca332a3fa9706a>:0 
  at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeVirtualBooleanMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0005d] in <7802aa64ad574c33adca332a3fa9706a>:0 
  at Android.Views.GestureDetector.OnTouchEvent (Android.Views.MotionEvent ev) [0x00031] in <263adecfa58f4c449f1ff56156d886fd>:0 
  at Xamarin.Forms.Platform.Android.GestureManager+TapAndPanGestureDetector.OnTouchEvent (Android.Views.MotionEvent ev) [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.Android\GestureManager.cs:87 
  at Xamarin.Forms.Platform.Android.GestureManager.OnTouchEvent (Android.Views.MotionEvent e) [0x0005c] in D:\a\1\s\Xamarin.Forms.Platform.Android\GestureManager.cs:59 
  at Xamarin.Forms.Platform.Android.VisualElementRenderer`1[TElement].OnTouchEvent (Android.Views.MotionEvent e) [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.Android\VisualElementRenderer.cs:38 

任何人都知道发生了什么事以及如何解决此问题?

1 个答案:

答案 0 :(得分:2)

我可以回答两个问题之一。

  

该如何解决?

我看过很多ReactiveUI项目,但从未见过有人处理ReactiveCommands。要记住的重要事情是处理ReactiveCommand订阅和绑定。

因此,如果您选择绑定ReactiveUI方式而不是通过xaml进行绑定,则可以在xaml中执行以下操作:

<TapGestureRecognizer x:Name="TapGesture" />

并在MainPage构造函数中:

this.WhenActivated(
    disposables =>
    {
        this.OneWayBind(ViewModel, vm => vm.LabelClickCommand, v => v.TapGesture.Command)
            .DisposeWith(disposables);
    });

并在ViewModel中仅处理任何额外的订阅:

public MainPageViewModel(INavigation navigation) : base(navigation)
{
    LabelClickCommand = ReactiveCommand
        .CreateFromTask(InternalLabelClickCommand);

    this.WhenActivated(
        disposables =>
        {
            LabelClickCommand
                .IsExecuting
                .Subscribe(isExecuting => Console.Write($"{nameof(LabelClickCommand)}.IsExecuting: {isExecuting}"))
                .DisposeWith(disposables);

            LabelClickCommand
                .ThrownExceptions
                .Subscribe(exception =>
                    Console.WriteLine($"Error executing {nameof(LabelClickCommand)}. Ex: {exception.ToString()}"))
                .DisposeWith(disposables);
        });
}

我测试了此代码,因此可以正常工作。

在您的解决方案中,我怀疑LabelClickCommand在完成执行之前已被丢弃。因为我在await语句后的右括号处设置了一个断点,但从未被击中。

但是话又说回来,您说它在iOS和ReactiveUI 8的Alpha版本中都能正常工作,所以我不确定。

很抱歉,在这方面没有解释。希望其他人可以加入。在此之前,我希望这种替代方法能够满足您的需求。

更新

如格伦在评论中所述,

  

通常,由于性能问题,Rx并不一定要丢弃所有内容(肯定对移动应用程序更重要)。

范围确定您是否需要处理订阅。这是另一个SO answer that mentions this

作为一个例子,您肯定希望处置以下订阅,尤其是如果该服务具有完整的应用程序生命周期时。否则,该服务将在视图模型被破坏后继续保留该订阅。

_someSubscription = someService
    .SomePipeline
    .Subscribe(x => ...);

相比之下,LabelClickCommand的ThrownExceptions和IsExecuting订阅具有本地范围,因此您甚至不需要像我上面那样处理这些。