Concurrent and continuous asynchronous WebClient requests in Windows Phone

Since the third week of April NaNoWriMo Camp, during which I disgracefully failed to reach half of my original word count yet another time, I have been wasting my time on the long overdue interest in ASP.NET web design and, in a tad bit of nostalgia, in Windows Phone app development. It’s all about the cloud and web technologies these days, I’m sure what I learned in the past two weeks would be put to good use in the future when I dwell more into sensors and networks development as professional.

In the meantime, I’ll write a bit about the love-hate relationship between the old WebClient class on the (relatively) new mobile platform.

The Task

So I had a web server, Azure cloud server, and a web app written in ASP.NET that can return a value in a range for each of the 15 industry sensors available. I were to write a Windows Phone app that can fetch data from multiple web sources concurrently and continuously. I would love to write the latest and the most sparkling platform–Windows 10 Universal App–but sadly, my jacked and hacked up laptop could not weather the newer emulators.

Hence, I resorted to the tried and tested WP 8.1 Silverlight platform for this experiment. I had experiences with WebClient and HttpRequest classes beforehand. I wrote a few apps that make use of TCP and UDP connections in Java, JavaScript, C# and VB. But, concurrency and continuity are unexpectedly tough for Windows Phone, especially now that they’re dropping synchronous for asynchronous cloud programming.

Concurrency & Continuity

I expected WebClient.DownloadString but what I got from the trimmed down .NET Core for Windows Phone was WebClient.DownloadStringAsync. The former method does not exist in the library at all. There is the option to fetch an add-on from NuGet or download external libraries that provide the synchronous method. Then again, I have a policy against adding more kilobytes to my project if I know there’s a way to solve the problem internally. Maybe, I’m just eccentric in this regard, so there’s that.

The implementation of DownloadStringAsync is quite simple on its own. It involves creating a WebClient object, attaching a handler for DownloadStringCompleted event and starting the asynchronous request to a URI (defined in control.path object in my case). Here’s the basic code:

WebClient wc = new WebClient();
public void Update()
 {
wc.CancelAsync();
 wc.DownloadStringCompleted += HttpCompleted;
 // Append salt to the uri to prevent response caching
 wc.DownloadStringAsync(new Uri(control.path));
}
 private void HttpCompleted(object sender, DownloadStringCompletedEventArgs e)
 {
 if (e.Error == null)
 {
 control.value = e.Result;
 }
 else
 {
 wc.CancelAsync();
 status.Text = "Failed to update: " + control.path;
 }
 }

The above code works for a single, maybe sparse requests, but not when I want a lot of them all at once. The solution off the top of my mind was to make a separate class called ProcessUpdater:

 public class ProcessUpdater
 {
 ItemViewModel control;
 WebClient wc = new WebClient();
 TextBlock status;
public ProcessUpdater(ItemViewModel item, TextBlock stat)
 {
 control = item; // getter
 status = stat;
}
// Update() and HttpCompleted() implementation here
}

which takes in a MVVC data model item and a TextBlock for displaying status. Invoking ProcessUpdater.Update() would start a http request to the address of the data model object corresponding to one of the 15 sensors passed to and stored within the updater object. This object-oriented setup can handle any number of sensors and the objects can be reused any number of time after initial construction. Putting them on their own worker thread, inside an infinite while loop, perhaps some Thread.sleep in between asyn requests, is the no-brainer way out of concurrency and continuity.

Not so fast!

Except that, it was a mobile phone I was talking about. Resources are scarce, and in Windows Phone, the OS will terminate any RAM- or CPU-hogging apps without warning. So there goes down the drain the idea of running multiple background worker threads. I tried that, somehow, I wasn’t able to make threading works for Windows Phone at all. Start a do-nothing thread, the app crashes. Call a sleep on the current thread, the app crashes. Use a while(true) loop anywhere, the app also crashes.

Instantly, I realized the phone must be treated like any other IC in embedded programming, even worse since I can get away with infinite loop and wait() function in many IC families. At first, I tried the recursive method and let the async handler callback Update() function in a loop. The problem is, I forgot how resource-intensive an ever-expanding recursion method is. That’s one lesson re-learned.

I scrambled over the internet and found DispatchTimer class for a periodic trigger. It’s scary since I have no idea to tell at compiled time, how long would it take to complete an async WebClient request. If the requests are taking longer than the interval at which they’re queued by DispatchTimer, I could potentially end up with a huge stack of unresolved requests. And that was exactly what happened when I set the timer at 1 second interval and lost internet connection. The request timeout errors were returned at a much longer interval than DispatchTimer and the app was terminated after a while.

Finally, I had to put a flag boolean in the updater to prevent further request creation until the previous request is completed. After some further optimizations, the app was able to handle 200 milliseconds interval for 2 hours, which is good enough for me.

#region Asychronous Webclient updater class
 public class ProcessUpdater
 {
 ItemViewModel control;
 WebClient wc = new WebClient();
 TextBlock status;
 private bool flag = false;

public ProcessUpdater(ItemViewModel item, TextBlock stat)
 {
 control = item; // getter
 status = stat;
// create 2s dispatch timer event for periodic data update
 DispatcherTimer Timer = new DispatcherTimer();
 Timer.Tick += new EventHandler(dispatcherTimer_Tick);
 Timer.Interval = TimeSpan.FromMilliseconds(500);
 Timer.Start();
}

 private void dispatcherTimer_Tick(object sender, EventArgs e)
 {
 if (flag == false)
 Update();
 }

public void Update()
 {
 if (control.visibility == Visibility.Visible)
 {
 flag = true;
 wc.CancelAsync();
 wc.DownloadStringCompleted += HttpCompleted;
 // Append salt to the uri to prevent response caching
 wc.DownloadStringAsync(new Uri(control.path + "?" + DateTime.Now.Ticks.ToString()));
 }
 }
 private void HttpCompleted(object sender, DownloadStringCompletedEventArgs e)
 {
 if (e.Error == null)
 {
 control.value = e.Result;
 if (status.Text == "Failed to update: " + control.path)
 status.Text = "";
 // Update();
 }
 else
 {
 wc.CancelAsync();
 status.Text = "Failed to update: " + control.path;
 //MessageBox.Show(control.path + " update failed: " + e.Error);
 }
flag = false;
}
}
 #endregion

There’s also further gating conditions that prevents request creation when the display UI elements are hidden by user. The source code to the project can be found here:

https://github.com/Fujihita/Industrial-Process-Monitor

Extra: Bypassing the cache

Notice the URI code

control.path + "?" + DateTime.Now.Ticks.ToString()

I encountered one last problem with continuous web requests using WebClient class. Even though the server response had changed, and I could see from my browser that the data had changed, the response the app was picking up did not. It was cached. Subsequent requests to the same source were not sent to the live server, the internal WebClient class would return the cached content to save bandwidth.

After a quick search, I found the quick solution from StackOverflow. All I need to do is to salt the URI with a randomized QueryString which would cause the URI to change every time and bypass the cache policy.

Cool hack indeed!

Advertisements

Published by

fujihita

Self-learner, designer, author and programmer.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s