【问题标题】:Xamarin: How to return data to Shared Project from NSUrlSession task from Xamarin iOS Project?Xamarin:如何从 Xamarin iOS 项目的 NSUrlSession 任务将数据返回到共享项目?
【发布时间】:2019-12-25 03:09:28
【问题描述】:

我在 Xamarin iOS 项目中有一个类,例如:

public class NetworkUtility : INetworkUtility
{

    public void GetData()
    {

        NSUrl url = new NSUrl("https://reqres.in/api/users?page=2");
        NSUrlRequest request = new NSUrlRequest(url);
        NSUrlSession session = null;
        NSUrlSessionConfiguration myConfig = NSUrlSessionConfiguration.DefaultSessionConfiguration;
        myConfig.MultipathServiceType = NSUrlSessionMultipathServiceType.Handover;
        session = NSUrlSession.FromConfiguration(myConfig);
        NSUrlSessionTask task = session.CreateDataTask(request, (data, response, error) => {
            Console.WriteLine(data);

        });

        task.Resume();
    }
}

我是从共享项目中的一些 Xaml 代码中调用它,例如:

DependencyService.Get<INetworkUtility>().GetStringData();

现在在GetData() 函数中,我在匿名函数部分从网络服务获取数据:

NSUrlSessionTask task = session.CreateDataTask(request, (data, response, error) => {
                Console.WriteLine(data);

            });

如何将此数据返回给调用者(即返回到后面的 Xaml 代码)?

如果我将GetData() 函数的返回类型更改为字符串或任务并尝试在 lambda 表达式内返回,则会出现编译错误。

我阅读并且各种论坛都说我需要实现 NSUrlDelegate 来处理响应。我为此尝试了多种变体,但没有成功。请指教。

另外,(我认为这是不可能的或正确的方法)但是有没有办法将整个 NSUrlSession 返回到共享项目并只从共享项目本身调用数据任务方法?

【问题讨论】:

    标签: c# xamarin nsurlsession nsurlsessiondownloadtask


    【解决方案1】:

    有一个简单的解决方案。在INetworkUtility 接口中声明GetData() 方法,如下所示:

    Task<string> GetData();
    

    在您的 iOS NetworkUtility 类中创建一个 TaskCompletionSource 并返回该任务。然后,在您的 lambda 表达式中,当一切都设置并完成后,您可以设置任务结果。

    public Task<string> GetData()
    {
        TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
        NSUrl url = new NSUrl("https://reqres.in/api/users?page=2");
        NSUrlRequest request = new NSUrlRequest(url);
        NSUrlSession session = null;
        NSUrlSessionConfiguration myConfig = NSUrlSessionConfiguration.DefaultSessionConfiguration;
        myConfig.MultipathServiceType = NSUrlSessionMultipathServiceType.Handover;
        session = NSUrlSession.FromConfiguration(myConfig);
        NSUrlSessionTask task = session.CreateDataTask(request, (data, response, error) => {
            //Console.WriteLine(data);
            //tell the TaskCompletionSource that we are done here:
            tcs.TrySetResult(data); //assuming data is a string but i guess you can convert it if not
        });
    
        task.Resume();
        return tcs.Task;
    }
    

    在您的共享代码中,您可以创建一个等待结果任务的异步函数:

    public async Task ReceiveDataShared() {
        string yourData = await DependencyService.Get<INetworkUtility>().GetData();
    }
    

    编辑:我猜可能存在一个极端情况,即永远不会调用 lambda 表达式和 TrySetResult(...)。在这种情况下,您的共享代码将永远等待。为防止这种情况,您可以像这样设置超时:

    Task<string> dataTask = DependencyService.Get<INetworkUtility>().GetData();
    await Task.WhenAny(dataTask, Task.Delay(10000)); // 10.000ms timeout
    if(dataTask.IsCompletedSuccessfully) {
        //all good:
        string dataString = dataTask.Result;
    }else{
        //we hit the time limit
    }
    

    【讨论】:

    • 工作就像一个魅力。谢谢你帮助我。
    • 但是这种情况不起作用:Task dataTask = DependencyService.Get().GetData();它说“无法将类型'string'隐式转换为'System.Threading.Task.Task'。但我想我会弄清楚这部分。感谢TaskCompletionSource部分。
    • 此错误消息意味着您没有将GetData() 签名更改为Task&lt;string&gt; 而不是string,或者您在该行中使用了await 关键字。在这种情况下,await 关键字意味着您不希望返回 Task&lt;string&gt; 对象,该对象包含有关您正在进行的任务的信息,而是希望“等待”您的字符串结果,并且只有当任务完成时,您想继续执行。
    • 哇.. 好收获。即使在超时的情况下,我也没有删除“等待”关键字。再次感谢。你太棒了!
    猜你喜欢
    • 2020-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多