我们目前遇到WhenActivated
的问题,其版本为8。我们不是在使用路由基础结构,而是在使用内部MVVM框架。为了支持ISupportActivation
,我们在基础页面中侦听OnAppearing
和OnDisapearing
事件,并手动调用Activate()
和Deactivate()
。
以下内容曾经在版本8的Alpha版本中可用,但现在不再存在。在iOS中没有问题。
下面的代码说明了这个问题,在MainPage上绑定了一个ReactiveCommand,然后我们导航到Target hello world页面。可以找到一个仓库here
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();
}
});
}
}
}
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
任何人都知道发生了什么事以及如何解决此问题?
答案 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订阅具有本地范围,因此您甚至不需要像我上面那样处理这些。