在 Xamarin (C#) 中使方法异步

问题描述

我有这种从互联网下载数据的方法

    public ObservableCollection<Magnetka> ParseJSON() {
            ObservableCollection<Models.Magnetka> Markers = new ObservableCollection<Models.Magnetka>();
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("(URL)");
            request.Headers.Add("Authentication-Token","(KEY)");
            request.Method = "GET";
            request.ContentType = "application/json";
            CookieContainer cookieContainer = new CookieContainer();
            request.CookieContainer = cookieContainer;
            var response = request.GetResponse();
            var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
            ......... JSON parsing)

            return Markers;

现在我发现这并不太快,它会冻结应用程序两秒钟左右,所以我需要使它异步。但是,我在另一个类中这样调用方法

        GetMarkers getMarkers = new GetMarkers();
        Markers = getMarkers.ParseJSON();

如何使我的方法异步?我不太理解这个概念,如果我让 ParseJSON() 异步任务,我不知道把“await”关键字放在哪里。 有人可以帮我吗?

解决方法

首先,标记方法async并使用GetResponse的异步版本

public async Task<ObservableCollection<Magnetka>> ParseJSON() 
{
    ...
    var response = await request.GetResponseAsync();
    ...

    return Markers;
}

然后当你调用它时使用 await 关键字

Markers = await getMarkers.ParseJSON();
,

你可以将你的方法改为这样:

public async Task<ObservableCollection<Magnetka>> ParseJSON()
{
    var markers = new ObservableCollection<Magnetka>();
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create("(URL)");
    request.Headers.Add("Authentication-Token","(KEY)");
    request.Method = "GET";
    request.ContentType = "application/json";
    CookieContainer cookieContainer = new CookieContainer();
    request.CookieContainer = cookieContainer;
    using var response = await request.GetResponseAsync().ConfigureAwait(false);
    using var responseStream = response.GetResponseStream();
    using var streamReader = new StreamReader(responseStream);
    var json = await streamReader.ReadToEndAsync().ConfigureAwait(false);

    // json parsing

    return markers;
}

首先,将方法标记为异步。这表明编译器为此方法设置状态机以进行继续。不过还没有发生任何异步事件。

您需要做的另一件事是将您的返回值包装在 Task 中,这类似于其他一些语言中的承诺。任务是 await-able,这是发生异步事情的地方。

从使用 WebRequest 中的同步方法切换到使用 Async 变体并等待它们的结果会使您的方法异步。

所以来自:

request.GetResponse()

致:

await request.GetResponseAsync()

返回一个可等待的 Task<WebResponse>

流阅读器代码也是如此,您也可以使用其异步 API。

使用异步代码的一个棘手问题是,它迫使您一直这样做。但是,生命周期方法和事件也有例外,您不必更改签名即可返回 Task

您可能会在这里注意到的另一件事。我在异步方法调用的末尾添加了 ConfigureAwait(false)。我这样做是因为开箱即用,从等待的方法返回后,代码将尝试返回到原始上下文。如果原始上下文是 UI 线程,这可能会导致一些问题,例如死锁。或者,尝试在上下文之间进行大量切换可能会导致性能不佳。添加 ConfigureAwait(false) 只会在运行 await 的相同上下文中返回,只要我们不需要或期望在该上下文中返回就可以。

至于在何处调用 ParseJson() 可能同样重要。考虑将其推迟到某个生命周期方法或事件中,而您没有做太多其他事情。

这在 Android 上可能是 OnResume(),在 iOS 上可能是 ViewWillAppear。如果你使用的是MVVM,使用一个Command来封装它。