У меня интересная проблема с приложением для Windows Phone 7.
Прямо сейчас я отправляю около двух десятков или около того запросов на небольшие (несколько килобайт) xml-файлы, чтобы сохранить быстро меняющуюся информацию (точнее, адреса шин). У меня есть таймер каждые 10 секунд для асинхронной отправки запросов.
Полученные данные обрабатываются с помощью асинхронного обратного вызова для проверки того, что запрос вернулся с файлом, и сохраняют файл в IsolatedStorage. Когда необходимы данные (с помощью модели представления пользовательского интерфейса), файл считывается из IsolatedStorage, анализируется во внутренние объекты (в основном, GeoCoordinate и некоторые строки) и используется.
1) Я немного столкнулся с ограничениями центрального процессора телефона. Если я проанализирую все данные шины, чтобы определить, какие автобусные маршруты активны в потоке пользовательского интерфейса, поток заметно замедлится. Я переместил работу в bakground работник, как это:
public class RunningWorkerHelper
{
public BusRouteIdModel Route { get; set; }
public Visibility Visible { get; set; }
}
public class RunningWorker
{
BackgroundWorker bw;
Queue<BusRouteIdModel> workQueue;
public RunningWorker()
{
bw = new BackgroundWorker();
workQueue = new Queue<BusRouteIdModel>();
bw.DoWork += CheckIfRunning;
bw.RunWorkerCompleted += CheckRunningCompleted;
}
public void QueueWorkItem(BusRouteIdModel route)
{
workQueue.Enqueue(route);
StartWorkItem();
}
void StartWorkItem()
{
if (!bw.IsBusy && workQueue.Count > 0)
bw.RunWorkerAsync(workQueue.Dequeue());
}
void CheckIfRunning(object sender, DoWorkEventArgs args)
{
BusRouteDataService server= BusRouteDataService.Current;
var route = (BusRouteIdModel)args.Argument;
if (route == null) return;
var vehicleFile = server.GetBusVehiclesFile(route);
var vehiclesOnRoute = RouteDataParser.ExtractBusVehicles(vehicleFile);
var helper = new RunningWorkerHelper { Route = route };
if (vehiclesOnRoute.Count > 0)
{
helper.Visible = Visibility.Visible;
}
else
{
helper.Visible = Visibility.Collapsed;
}
args.Result = helper;
}
void CheckRunningCompleted(object sender, RunWorkerCompletedEventArgs args)
{
var helper = (RunningWorkerHelper)args.Result;
if (helper != null)
helper.Route.IsRunning = helper.Visible;
StartWorkItem();
}
}
вот BusRouteIdModel (для справки)
public class BusRouteIdModel : BusRouteId, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
Visibility _isRunning;
/// <summary>
/// True when there is a bus active on this route
/// </summary>
public Visibility IsRunning
{
get { return _isRunning; }
set
{
if (value == _isRunning) return;
_isRunning = value;
OnPropertyChanged(new PropertyChangedEventArgs("IsRunnning"));
}
}
/// <summary>
/// True while path data for the route isn't available yet
/// </summary>
Visibility _isLoading;
public Visibility IsLoading
{
get { return _isLoading; }
set
{
if (value == _isLoading) return;
_isLoading = value;
OnPropertyChanged(new PropertyChangedEventArgs("IsLoading"));
}
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
if (PropertyChanged != null)
PropertyChanged(this, args);
}
}
public class BusRouteId
{
public String Uid { get; set; }
public String Name { get; set; }
}
Это данные, связанные с элементом списка на странице (я использую MVVM Light Toolkit):
public const string RouteIdsPropertyName = "RouteIds";
private ObservableCollection<BusRouteIdModel> _routeIds = null;
public ObservableCollection<BusRouteIdModel> RouteIds
{
get { return _routeIds; }
private set
{
if (_routeIds == value) return;
var oldValue = _routeIds;
_routeIds = value;
RaisePropertyChanged(RouteIdsPropertyName);
}
}
И я называю своего работника вот так (идентификатор args.Uid - это автобусный маршрут, который получил новые данные):
foreach (var route in RouteIds)
if (route.Uid == args.Uid)
runningWorker.QueueWorkItem(route);
2) Таинственным образом, изменение IsRunning не отражается в пользовательском интерфейсе. Что я упустил?
3) Как я могу обобщить этот Queue + BackgroundWorker для решения других задач? (Я бы использовал библиотеку пула задач, но в Silverlight ее нет.) Я хочу иметь одного работника, чтобы поток пользовательского интерфейса не истощался на странице карты.