我的 Windows Phone 8 天气应用程序的后台代理出现问题。
每当运行后台代理时,都会在满足某些条件(与我遇到的问题无关)时发出新的 http 天气请求。当这些条件不满足时,将使用缓存的天气数据。
此外,如果您将活动磁贴的位置设置为“当前位置”,后台代理将使用反向地理编码来确定您当前所在位置的区域名称。无论是使用新数据还是缓存数据,即每次运行我的应用程序的后台代理时,都会执行此操作。
我遇到的问题是,每当使用缓存数据时,动态磁贴都不会更新。但它似乎不会导致异常发生,因为应用程序的后台代理永远不会被阻止,即使动态磁贴无法更新的次数超过两次。
这是从调度代理调用的后台代理的视图模型的“公共异步任务 getWeatherForTileLocation()”方法的相关摘录:
预定代理摘录:
protected async override void OnInvoke(ScheduledTask task)
{
LiveTileViewModel viewModel = new LiveTileViewModel();
await viewModel.getWeatherForTileLocation();
// Etc.
}
getWeatherForTileLocation() 摘录:
// If the default location is 'Current Location', then update its coordinates.
if ((int)IsolatedStorageSettings.ApplicationSettings["LocationDefaultId"] == 1)
{
try
{
// Get new coordinates for current location.
await this.setCoordinates();;
}
catch (Exception e)
{
}
}
// Depending on the time now, since last update (and many other factors),
// must decide whether to use cached data or fresh data
if (this.useCachedData(timeNow, timeLastUpdated))
{
this.ExtractCachedData(); // This method works absolutely fine, trust me. But the live tile never updates when it's run outside debugging.
// Not because of what it does, but because of how fast it executes.
}
else
{
// a httpClient.GetAsync() call is made here that also works fine.
}
setCoordinates 方法,以及从中调用的反向地理编码相关方法:
public async Task<string> setCoordinates()
{
// Need to initialise the tracking mechanism.
Geolocator geolocator = new Geolocator();
// Location services are off.
// Get out - don't do anything.
if (geolocator.LocationStatus == PositionStatus.Disabled)
{
return "gps off";
}
// Location services are on.
// Proceed with obtaining longitude + latitude.
else
{
// Setup the desired accuracy in meters for data returned from the location service.
geolocator.DesiredAccuracyInMeters = 50;
try
{
// Taken from: http://bernhardelbl.wordpress.com/2013/11/26/geolocator-getgeopositionasync-with-correct-timeout/
// Because sometimes GetGeopositionAsync does not return. So you need to add a timeout procedure by your self.
// get the async task
var asyncResult = geolocator.GetGeopositionAsync();
var task = asyncResult.AsTask();
// add a race condition - task vs timeout task
var readyTask = await Task.WhenAny(task, Task.Delay(10000));
if (readyTask != task) // timeout wins
{
return "error";
}
// position found within timeout
Geoposition geoposition = await task;
// Retrieve latitude and longitude.
this._currentLocationLatitude = Convert.ToDouble(geoposition.Coordinate.Latitude.ToString("0.0000000000000"));
this._currentLocationLongitude = Convert.ToDouble(geoposition.Coordinate.Longitude.ToString("0.0000000000000"));
// Reverse geocoding to get your current location's name.
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
this.setCurrentLocationName();
});
return "success";
}
// If there's an error, may be because the ID_CAP_LOCATION in the app manifest wasn't include.
// Alternatively, may be because the user hasn't turned on the Location Services.
catch (Exception ex)
{
if ((uint)ex.HResult == 0x80004004)
{
return "gps off";
}
else
{
// Something else happened during the acquisition of the location.
// Return generic error message.
return "error";
}
}
}
}
/**
* Gets the name of the current location through reverse geocoding.
**/
public void setCurrentLocationName()
{
// Must perform reverse geocoding i.e. get location from latitude/longitude.
ReverseGeocodeQuery query = new ReverseGeocodeQuery()
{
GeoCoordinate = new GeoCoordinate(this._currentLocationLatitude, this._currentLocationLongitude)
};
query.QueryCompleted += query_QueryCompleted;
query.QueryAsync();
}
/**
* Event called when the reverse geocode call returns a location result.
**/
void query_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLocation>> e)
{
foreach (var item in e.Result)
{
if (!item.Information.Address.District.Equals(""))
this._currentLocation = item.Information.Address.District;
else
this._currentLocation = item.Information.Address.City;
try
{
IsolatedStorageSettings.ApplicationSettings["LiveTileLocation"] = this._currentLocation;
IsolatedStorageSettings.ApplicationSettings.Save();
break;
}
catch (Exception ee)
{
//Console.WriteLine(ee);
}
}
}
我已经调试了很多次代码,并且没有发现任何问题。调用时的http请求很好,缓存数据提取很好,反向地理编码总是返回一个位置(最终)。
但我确实注意到,当我使用缓存数据时,当前位置的名称是在计划任务创建更新的活动磁贴之后但在计划任务完成之前检索的。
也就是说,在调度代理中运行此代码后,将检索位置的名称:
extendedData.WideVisualElement = new LiveTileWideFront_Alternative()
{
Icon = viewModel.Location.Hourly.Data[0].Icon,
Temperature = viewModel.Location.Hourly.Data[0].Temperature,
Time = viewModel.Location.Hourly.Data[0].TimeFull.ToUpper(),
Summary = viewModel.Location.Hourly.Data[0].Summary + ". Feels like " + viewModel.Location.Hourly.Data[0].ApparentTemperature + ".",
Location = IsolatedStorageSettings.ApplicationSettings["LiveTileLocation"].ToString().ToUpper(),
PrecipProbability = viewModel.Location.Hourly.Data[0].PrecipProbabilityInt
};
但之前:
foreach (ShellTile tile in ShellTile.ActiveTiles)
{
LiveTileHelper.UpdateTile(tile, extendedData);
break;
}
NotifyComplete();
显然由于内存限制,此时我无法创建更新的视觉元素。
相比之下,当我不使用缓存数据时,反向地理编码查询总是设法在 http 请求代码完成之前返回一个位置。
因此,当视图模型的 getWeatherForTileLocation() 方法在调度代理中使用“等待”时,我决定确保该方法在检索到当前位置的名称之前不会返回任何内容。我在方法的页脚中添加了一个简单的 while 循环,该循环只会在 _currentLocation 字段收到一个值后终止,即反向地理编码完成:
// Keep looping until the reverse geocoding has given your current location a name.
while( this._currentLocation == null )
{
}
// You can exit the method now, as you can create an updated live tile with your current location's name now.
return true;
当我调试时,我认为这个循环运行了大约 300 万次迭代(无论如何是一个非常大的数字)。但是这个 hack(我不知道如何描述它)在我调试时似乎有效。也就是说,当我的构建目标是我的 Lumia 1020 时,当我从它创建一个新鲜的动态磁贴时,它调用:
ScheduledActionService.Add(periodicTask);
ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(1));
以确保我不必等待第一个计划任务。当我调试第一个计划任务时,一切正常:1)发出反向地理编码请求,2)正确提取缓存数据,3)hacky while循环不断迭代,4)当反向地理编码返回位置名称时停止,5 ) 磁贴成功更新。
但使用缓存数据的后续后台代理调用似乎不会更新磁贴。只有在使用非缓存数据时,动态磁贴才会更新。此时我应该提醒您,反向地理编码查询总是设法在 http 请求代码完成之前返回一个位置,即 hacky 循环仅迭代一次。
关于我需要做什么以确保在使用缓存数据时正确更新实时磁贴的任何想法(阅读:当数据处理时,在进行反向地理编码查询后,比 http 请求快得多)?另外,有没有比我的 while 循环更优雅的方法来阻止 getWeatherForTileLocation() 退出?我确定有!
抱歉,这篇文章很长,但希望尽可能彻底!
在过去的 72 小时里,这一直让我彻夜难眠(字面意思),因此非常感谢您的帮助和指导。
非常感谢。
巴迪