Я пытаюсь проверить точность службы геокодирования Apple перед тем, чтобы покрыть редкий случай, когда широта/долгота у нас на нашей модели данных отсутствует или недействительна, но мы все еще знаем физический адрес. Monotouch предоставляет CLGeocoder.GeocodeAddress(String, CLGeocodeCompletionHandler)
и GeocodeAddressAsync(String)
но когда я их CLGeocoder.GeocodeAddress(String, CLGeocodeCompletionHandler)
завершенияHandler никогда не вызывается, и метод async никогда не возвращается.
Ничто в моем журнале приложений не указывает на проблему, и включение вызова в блок try-catch не вызвало каких-либо исключений. Интеграция карт включена в параметрах проекта. Если не считать захвата сетевого трафика (это, вероятно, SSL), у меня нет идей.
Здесь код, который загружает метки и пытается выполнить геокодирование адресов:
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;
}
Ваша проблема в этой строке:
ServiceCallWrapper serviceCall = GeocodeServiceCallAddressAsync (s).Result;
Task<T>.Result
, вы вызываете тупик. Я полностью объясняю это в своем блоге, но суть этого заключается в том, что await
(по умолчанию) захватить "контекст", когда он даст управление, и будет использовать этот контекст для завершения async
метода. В этом случае "контекст" представляет собой контекст пользовательского интерфейса. Таким образом, поток пользовательского интерфейса блокируется (ожидание Result
), когда приходит ответ, и метод async
не может продолжаться, потому что он ожидает выполнения в потоке пользовательского интерфейса.
Решение состоит в том, чтобы использовать async
полностью. Другими словами, заменить каждую Task<T>.Result
и Task.Wait
с await
:
private async Task ReloadPlacemarksAsync()
{
...
ServiceCallWrapper serviceCall = await GeocodeServiceCallAddressAsync (s);
...
}
Обратите внимание, что ваши void ReloadPlacemarks
теперь Task ReloadPlacemarksAsync
, поэтому это изменение влияет на ваших абонентов. async
будет "расти" через кодовую базу, и это нормально. Для получения дополнительной информации см. Статью MSDN о лучших методах async.