On
a phone, slow or lost data connections are a fact of everyday life.
Ideally, phone applications should detect such connections and provide a
recovery mechanism to deal with them. Two potential ways to deal with
slow or lost connectivity on the phone are: (1) let the user decide
whether the application should retry what it was doing before the
connection timed out or lost, and (2) provide an automated retry
mechanism.
Rx.NET can aid in both of those
scenarios. Furthermore, Rx.NET includes a special Timeout operation
that will generate a timeout error if it does not receive data, such as a
web service callback, from its Observable within a user-specified interval. Let's take a look at the Timeout operation in action. Let's change the WireUpWeatherEvents function to time out if it does not get any data for two seconds:
Replace the WireUpEvents() function of the WeatherRx application with the following code:
private void WireUpWeatherEvents()
{
var weather = GetWeatherSubject();
weather.ObserveOn(Deployment.Current.Dispatcher)
.Timeout(TimeSpan.FromSeconds(2))
.Subscribe(evt =>
{
if (evt.EventArgs.Result.Details != null)
{
lblWeatherFahrenheit.Text = "Current Weather, Fahrenheit: " + evt.EventArgs.Result.Details[0].MinTemperatureF.ToString() + " - " + evt.EventArgs.Result.Details[0].MaxTemperatureF.ToString();
lblCelsius.Text = "Current Weather, Celsius: " + evt.EventArgs.Result.Details[0].MinTemperatureC.ToString() + " - " + evt.EventArgs.Result.Details[0].MaxTemperatureC.ToString();
imgWeather.Source = new BitmapImage(new Uri(evt.EventArgs.Result.Details[0].WeatherImage, UriKind.Absolute));
}
},
ex => {
Deployment.Current.Dispatcher.BeginInvoke(() => lblStatus.Text = ex.Message);
}
);
}
Now run the application and
notice how after two seconds, it immediately times out and displays the
timeout exception text on the emulator. What happened? You did not even
get a chance to specify the zip code to show the weather for.
Our code needs a little
refactoring, or changing around. In the code so far, you subscribed to
the web service's events immediately on application launch, and since
you did not get any data two seconds after the launch of the
application, that subscription timed out. The change that you need to
make is to subscribe to the web service's events right before you invoke
that web service, yet you have to be careful to create this
subscription just once.
Remove the call to WireUpWeatherEvents from the MainPage constructor and place it within the WireUpKeyEvents function to make that function look like the following:
private void WireUpKeyEvents()
{
var keys = Observable.FromEvent<KeyEventArgs>(txtZipCode, "KeyUp").Throttle(TimeSpan.FromSeconds(1)).DistinctUntilChanged();
keys.ObserveOn(Deployment.Current.Dispatcher).Subscribe(evt =>
{
if (txtZipCode.Text.Length >= 5)
{
WireUpWeatherEvents();
weatherClient.GetWeatherByZipCodeAsync(txtZipCode.Text);
}
});
}
Now the timeout feature
should work properly. Notice, however, that it will most likely take
slightly more than two seconds to return a valid response from the
Weather service.
Rx.NET also provides a Retry method that optionally takes a parameter for the number of times to retry to re-subscribe to the Observable collection. If you don't specify that parameter, Rx.NET will try to re-subscribe to the Observable
collection indefinitely. One way to deal with an absent or slow
connection is to retry the subscription two or three times, and then, if
unsuccessful, give the user the option to either retry once more or
cancel.