ReactiveUI WhenActivated 在 Android 上抛出 ObjectDisposedException

ReactiveUI WhenActivated throws ObjectDisposedException on Android

我们目前遇到 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\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\s\src\ReactiveUI\ReactiveCommand.cs:721 
   at ReactiveUI.ReactiveCommand.System.Windows.Input.ICommand.Execute (System.Object parameter) [0x00000] in D:\a\s\src\ReactiveUI\ReactiveCommand.cs:622 
   at Xamarin.Forms.TapGestureRecognizer.SendTapped (Xamarin.Forms.View sender) [0x00018] in D:\a\s\Xamarin.Forms.Core\TapGestureRecognizer.cs:44 
   at Xamarin.Forms.Platform.Android.TapGestureHandler.OnTap (System.Int32 count) [0x00028] in D:\a\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\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\s\Xamarin.Forms.Platform.Android\GestureManager.cs:87 
  at Xamarin.Forms.Platform.Android.GestureManager.OnTouchEvent (Android.Views.MotionEvent e) [0x0005c] in D:\a\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\s\Xamarin.Forms.Platform.Android\VisualElementRenderer.cs:38 

有人知道发生了什么以及如何解决这个问题吗?

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

how to fix this?

我见过很多 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 in general you don't want to necessarily dispose of everything (more vital on Mobile Apps for sure) due to performance issues.

Scope 确定您是否需要费心处理订阅。这是另一个 .

例如,您肯定想要处理以下订阅,特别是如果该服务具有完整的应用程序生命周期。否则,该服务将在视图模型被销毁后继续保留该订阅。

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

相比之下,对 LabelClickCommand 的 ThrownExceptions 和 IsExecuting 的订阅具有本地作用域,因此您甚至不需要真正处理它们,就像我在上面所做的那样。