等待CLGeocoder.GeocodeAddressAsync永远不会返回

本文关键字:返回 永远 CLGeocoder GeocodeAddressAsync 等待 | 更新日期: 2023-09-27 18:03:48

我正在测试Apple的前向地理编码服务的准确性,以覆盖我们数据模型上的纬度/经度缺失或无效的罕见情况,但我们仍然知道物理地址。Monotouch提供了CLGeocoder.GeocodeAddress(String, CLGeocodeCompletionHandler)GeocodeAddressAsync(String),但是当我调用它们时,completionHandler永远不会被调用,异步方法永远不会返回。

我的应用程序日志中没有任何内容表明有问题,并且将调用包含在try-catch块中不会出现任何异常。映射集成在项目选项中启用。缺少捕获网络流量(无论如何可能是SSL),我没有办法。

下面是加载placemark并尝试对地址进行地理编码的代码:
    private void ReloadPlacemarks()
    {
        // list to hold any placemarks which come back with empty/invalid coordinates
        List<ServiceCallWrapper> geoList = new List<ServiceCallWrapper> ();
        mapView.ClearPlacemarks ();
        List<MKPlacemark> placemarks = new List<MKPlacemark>();
        if (serviceCallViewModel.ActiveServiceCall != null) {
            var serviceCall = serviceCallViewModel.ActiveServiceCall;
            if (serviceCall.dblLatitude != 0 && serviceCall.dblLongitude != 0) {
                placemarks.Add (serviceCall.ToPlacemark ());
            } else {
                // add it to the geocode list
                geoList.Add (serviceCall);
            }
        }
        foreach (var serviceCall in serviceCallViewModel.ServiceCalls) {
            if (serviceCall.dblLatitude != 0 && serviceCall.dblLongitude != 0) {
                placemarks.Add (serviceCall.ToPlacemark ());
            } else {
                //add it to the geocode list
                geoList.Add (serviceCall);
            }
        }
        if (placemarks.Count > 0) {
            mapView.AddPlacemarks (placemarks.ToArray ());
        }
        if (geoList.Count > 0) {
            // attempt to forward-geocode the street address
            foreach (ServiceCallWrapper s in geoList) {
                ServiceCallWrapper serviceCall = GeocodeServiceCallAddressAsync (s).Result;
                mapView.AddPlacemark (serviceCall.ToPlacemark());
            }
        }
    }
    private async Task<ServiceCallWrapper> GeocodeServiceCallAddressAsync(ServiceCallWrapper s)
    {
        CLGeocoder geo = new CLGeocoder ();
        String addr = s.address + " " + s.city + " " + s.state + " " + s.zip;
        Console.WriteLine ("Attempting forward geocode for service call UID: " + s.call_uid + " with address: " + addr);
                    //app hangs on this
        CLPlacemark[] result = await geo.GeocodeAddressAsync(addr);
                    //code updating latitude and longitude (omitted)
        return s;
    }

等待CLGeocoder.GeocodeAddressAsync永远不会返回

你的问题是这一行:

ServiceCallWrapper serviceCall = GeocodeServiceCallAddressAsync (s).Result;

调用Task<T>.Result会导致死锁。我在我的博客上详细解释了这一点,但它的要点是await将(默认情况下)在获得控制时捕获一个"上下文",并将使用该上下文来完成async方法。在这种情况下,"context"是UI上下文。因此,当响应进来时,UI线程被阻塞(等待Result),并且async方法不能继续,因为它正在等待在UI线程上运行。

解决方案是一直使用async。换句话说,将每个Task<T>.ResultTask.Wait替换为await:

private async Task ReloadPlacemarksAsync()
{
  ...
  ServiceCallWrapper serviceCall = await GeocodeServiceCallAddressAsync (s);
  ...
}

注意你的void ReloadPlacemarks现在是Task ReloadPlacemarksAsync,所以这个改变会影响你的调用者。async将在代码库中"增长",这是正常的。有关更多信息,请参阅我在MSDN上关于异步最佳实践的文章。