Multithreading Xamarin-在异步API调用之前,我应该如何处理命令中的UI状态更改

Multithreading Xamarin-在异步API调用之前,我应该如何处理命令中的UI状态更改,multithreading,xamarin,xamarin.forms,Multithreading,Xamarin,Xamarin.forms,我通常对Xamarin表单使用命令模式。最近,我所研究的UI涉及调用远程API的命令,虽然它们这样做了,但它们表明正在以多种方式进行操作。例如,ActivityIndicator应该开始旋转,当API调用返回时,页面应该禁用按钮,等等 我直观地编写了一个异步ICommand,如下所示: VerifyCodeCommand = new Command(async () => { await VerifyCode(_email,_code

我通常对Xamarin表单使用命令模式。最近,我所研究的UI涉及调用远程API的命令,虽然它们这样做了,但它们表明正在以多种方式进行操作。例如,ActivityIndicator应该开始旋转,当API调用返回时,页面应该禁用按钮,等等

我直观地编写了一个异步ICommand,如下所示:

VerifyCodeCommand = new Command(async () =>
            {
                await VerifyCode(_email,_code);
            });
 Sending = true;

            (VerifyCodeCommand as Command).ChangeCanExecute();

            await Task.Run(async () =>

            {

                try

                {

                  // await api call here

                }

                catch (Exception ex)

                {

                    App.TrackError(ex, "Login", "VerifyCode API call failed.");

                }

                finally

                {

                    Sending = false;

                    (VerifyCodeCommand as Command).ChangeCanExecute();

                }

            });







        }
在VerifyCode函数中,我会将绑定到
ActivityIndicator
的IsRunning属性的属性更改为true,
等待API调用,然后在完成时将
ActivityIndicator
的IsRunning/Visible更改回false。 例如: 另一个示例是显示API失败状态(例如:红色警告),然后在API调用之前立即重置它

不幸的是,这不起作用,而且在
设备.beginInvokeMainThread
上设置绑定属性也不起作用,因为(可能)ICommand持有UI线程(?)。我对这个简单的例子所做的是在命令中将属性设置为
true
,然后执行
Task。运行
函数体的其余部分,即API调用并重置指示器,如下所示:

VerifyCodeCommand = new Command(async () =>
            {
                await VerifyCode(_email,_code);
            });
 Sending = true;

            (VerifyCodeCommand as Command).ChangeCanExecute();

            await Task.Run(async () =>

            {

                try

                {

                  // await api call here

                }

                catch (Exception ex)

                {

                    App.TrackError(ex, "Login", "VerifyCode API call failed.");

                }

                finally

                {

                    Sending = false;

                    (VerifyCodeCommand as Command).ChangeCanExecute();

                }

            });







        }
这种方法是必要的还是缺少了Xamarin(表单)中的一些功能?如果总是有必要以这种方式更新UI,那么“模式化”的合理方法是什么?例如,命令是否应该始终有两个实际的lambda,一个用于初始UI状态设置,一个用于后台调用?为什么简单的单线程异步方法在这里不起作用——毕竟API调用正在等待


我想为什么问题可以归结为:为什么在主线程上调用带有wait的异步API调用看起来好像主线程没有被释放来进行UI更新(这似乎是Xamarin UI lifecycle引入了一些限制)这方面的标准解决方案是什么?

我认为您的问题在于,您将等待任务过度地放在了命令的异步性质上

以当前应用程序为例: 我的所有ViewModels都有一个属性
bool IsBusy

属性
公共ICommand RefreshStoredDataCommand{get;}

在构造函数中:
refreshstoredatacommand=new命令(async()=>wait executerfreshdatacommand(),()=>!IsBusy)

对于所示的示例,我在视图中有一个
ListView
控件,用于设置

RefreshCommand ="{Binding RefreshStoredDataCommand}"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"

显然,对于您的代码,您应该绑定活动指示器。

请标记您更新的IsRunning属性与绑定到ActivityIndicator的属性相同。您可以添加一个断点来检查这一点。对于此功能,您还可以使用来完成此工作。@y3z1问题不在于属性名称-问题在于,除非异步API调用通过Task推送到一个新线程上,否则不会发生任何事情。RunApi调用可能需要很长时间,请使用
wait Task.Run
在非ui线程中执行耗时的工作,这可以使以下步骤正常工作。好的,在这一切结束后,API调用将同步运行。感谢您的努力,但使用上述代码,ActivityIndicator不会更新。实际上我已经测试过了,如果异步进程有“//Long调用”,那么这不是一个任务。运行(()=>//Long调用)生成一个新线程,那么UI不会更新。我不明白为什么。我的印象是,如果等待异步任务,主线程将被释放,这样UI就可以更新了,但是没有。该区域注释了我的位置“长时间调用异步任务是我调用远程http数据库访问任务的地方。当任务正在进行时,
列表视图的循环指示器肯定会出现并循环。我会用活动指示器检查。更改
IsBusy
值时是否记得触发
NotifyPropertyChanged
?是的,如果//long calls部分为Task.Run,则所有操作都有效。因此,没有像NotifyPropertyChanges或错误命名的属性这样的小错误。从字面上说,从wait DoSomething()改为wait Task。运行(()=>DoSomething)就可以了。对不起,我发现了问题。我觉得我浪费了你的时间。我的实际通话毕竟是同步运行的。我应该基本上删除我的问题。我会投票给你的答案。@Frank没问题。