我自学了使用Xamarin平台(我也是C#的新手)。我正在创建一个用户登录的应用程序,并被迫创建一个配置文件。我想让ActionBar(我使用材料工具栏)包含一个菜单项NSURLSession
。当用户点击DONE
时,我的应用会验证输入数据并将数据发送到我的Parse后端。这样做的问题是Parse API需要DONE
才能执行此操作。为了确定用户在ActionBar中单击await profile.SaveAsync();
,我需要覆盖DONE
,这不是异步的。
通过创建将处理所有Parse连接的OnOptionsItemSelected(IMenuItem item)
,我找到了解决方法。然后,我在private async void ActionDone()
中的ActionDone()
语句中致电switch
。但是,我认为这是用OnOptionsItemSelected
绑定UI线程。我读过这个坏主意(主要是在其他StackOverflow帖子上)。还有另一种让ActionBar等待的方法吗?或者我是否安全,因为为了继续,需要保存配置文件,因此保持用户界面是可接受的"?
OnOptionsItemSelected
await
ActionDone
public override bool OnOptionsItemSelected(IMenuItem item)
{
// verify nothing else was clicked
switch(item.ItemId)
{
case Resource.Id.action_done:
ActionDone();
break;
default:
// log error message
break;
}
return base.OnOptionsItemSelected(item);
}
我的所有解析调用都在共享库中,所以我也可以在iOS中使用它们
private async void ActionDone()
{
Task<ApiHandler.CreateProfileStruct> createProfileTask = ApiHandler.CreateProfile(mNameEdit.Text, mPhoneEdit.Text, mEmailEdit.Text, mSeriesEdit.Text, mLocationEdit.Text);
var result = await createProfileTask;
// toast the result...
Toast.MakeText(this, result.message, ToastLength.Long).Show();
// check if profile was created
if (result.enumValue == ApiHandler.CreateProfileEnum.Success)
{
StartActivity(typeof(MainActivity));
Finish();
}
}
我应该补充一点,我已经以这种方式实现了代码,并且它可以工作(据我所知)。根据我所读到的内容,我认为这不是最好的策略。
答案 0 :(得分:2)
回应你的评论:
你的意思是OnOptionsItemSelected()中的返回有可能在await结束之前执行
是。实际上,这可能是结果(即您希望操作是异步的,因此典型的情况是操作异步完成)。
或者我是否安全,因为为了继续,需要保存配置文件,因此保持UI是“可接受的”?
我不会说阻止UI线程永远可以接受。当然,它可以更简单地实现:只要您在UI线程可以自由执行时进行异步操作,那么您必须担心UI的状态,用户是否可以单击或以其他方式发出可能会或可能不会发出的命令在异步操作正在进行时有效等等。
但坦率地说,这正是async
/ await
功能旨在简化的问题。您可以以线性方式编写代码,在异步操作期间(如果需要)重新配置UI,然后在完成后轻松地将其放回原位。
在您的代码现在编写时,异步操作不会阻止UI线程。但OnOptionsItemSelected()
方法 返回,在该操作完成之前首先调用基本实现。
在你的情况下,这是否是一个问题,我不知道。这里没有足够的背景。但...
该方法中唯一的其他操作是调用基本实现并返回该实现的结果。只要该基本实现中没有任何内容依赖于异步操作的结果,并且只要在异步操作完成之前从方法返回基本实现的返回值,就不会误导调用者(如果基础实现不依赖于异步操作,我认为不会),应该没问题。
如果基本实现确实依赖于异步操作的结果,那么可以将整个方法体包装在async
方法中,这样就可以await
进行异步操作并推迟调用基数执行直到操作完成。例如:
public override bool OnOptionsItemSelected(IMenuItem item)
{
var ignoreTask = OnOptionsItemSelectedAsync(item);
return true;
}
private async Task OnOptionsItemSelectedAsync(IMenuItem item)
{
try
{
// verify nothing else was clicked
switch(item.ItemId)
{
case Resource.Id.action_done:
await ActionDone();
break;
default:
// log error message
break;
}
bool result = base.OnOptionsItemSelected(item);
// If some action should be taken depending on "result",
// that can go here
}
catch (Exception e)
{
// The caller is ignoring the returned Task, so you definitely want
// to observe exceptions here. If you have known exceptions that can
// be handled reasonably, add an appropriate "catch" clause for that
// exception type. For any other exceptions, just report/log them as
// appropriate for your program, and rethrow. Ultimately you'd like
// that to cause the entire process to crash with an "unobserved task
// exception" error, which is what you want for an exception you didn't
// anticipate and had no way to actually handle gracefully. Note that
// in .NET 4.5 and later, unobserved exceptions WILL NOT crash the process,
// unless you set the ThrowUnobservedTaskExceptions option to "true"
// in your App.config file. IMHO, doing so is a VERY good idea.
throw;
}
}
// This has to be awaitable...you should eschew "async void"
// methods anyway, so this is a change you'd want to make regardless.
private async Task ActionDone() { ... }
但是你会注意到,在上面,实际重写的方法仍然需要返回一些值。换句话说,您最终不得不欺骗调用者,并且(可选)稍后必须处理基本实现的实际结果。