Question

I want to use a Portable Class Library in a Windows Phone 8 app. The PCL contains async methods only and that's fine. But I want to call one of the methods from the Application_Launching event handler and wait for the result, so that I can instantly use it, as soon as the main page gets loaded.

These similar questions didn't help me:

For easy reproduction the following code is similar but more simple:

The PCL:

public class TestAsyncClass
{
    public async Task<string> SendRequestAsync()
    {
        using (HttpClient client = new HttpClient())
        {
            HttpResponseMessage response = await client.GetAsync("http://www.google.com").ConfigureAwait(false);
            return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
        }
    }
}

As you can see, there's ConfigureAwait(false) for the method calls that are being awaited, so there shouldn't be a deadlock because of some captured context that the method wants to return to. At least that's how I understood Stephen Cleary's blog article on that topic: http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

The App.xaml.cs:

private async void Application_Launching(object sender, LaunchingEventArgs e)
{
    TestAsyncLib.TestAsyncClass tac = new TestAsyncLib.TestAsyncClass();
    //string result = await tac.SendRequestAsync(); // GlobalData.MyInitString is still empty  in MainPage.xaml.cs OnNavigatedTo event handler
    string result = tac.SendRequestAsync().Result; // Gets stuck
    GlobalData.MyInitString = result;
}

As written in the comments, when calling the method asynchronously, GlobalData.MyInitString is still empty when trying to access it in the MainPage.xaml.cs OnNavigatedTo event handler, because the UI thread gets the focus immediately and launches the main page before the library method is able to return any results. And calling the method synchronously causes the library method to get stuck.

Here's the MainPage.xaml.cs:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    System.Diagnostics.Debug.WriteLine(GlobalData.MyInitString);
}

For completeness, GlobalData.cs:

public static class GlobalData
{
    public static string MyInitString { get; set; }
}

I'm thankful for any help!

Was it helpful?

Solution 2

I'm answering my own question now, but thanks to eX0du5 for pointing me to the right direction.

eX0du5 pointed out that void methods can't be awaited because they're not returning a Task. Searching for "Application_Launching returning Task" got me to a great msdn blog article that explained everything in detail:

http://blogs.msdn.com/b/andy_wigley/archive/2013/07/31/beware-the-perils-of-async-await-in-application-lifecycle-event-handlers-in-fact-in-any-event-handlers.aspx

The thing is: Event handlers can't return Tasks and the recommendation for calling async methods within app lifecycle event handlers is: "Don't."

Also, in the last paragraph of the blog article the author says that using .Wait() on the async method might work, but it can deadlock when the UI thread gets the scheduler's attention.

So both issues I had with my two different approaches are explained. What to do now? eX0du5's suggestion (saving the Task instead of the data to the GlobalData static class) is a workaround. In case the data that gets requested at the launch of the app needs to be accessed from different pages this seems like good solution, and in case the data is only needed on a page that doesn't get loaded after launching the app it even seems like a very good solution for prefetching data. I just checked that Tasks get executed when they get created and not only when awaited or used with .Result or .Wait() with this code.

For my purpose though, as I was only using the GlobalData for using data in the MainPage.xaml.cs that gets loaded when launching the app, it seems to be less confusing and more concise to not use the Application_Launching event handler alltogether and instead do everything in the OnNavigatedTo event handler. That's where Noseratio's comment on my question comes in handy and also this question on SO: When should I load data in a Windows Phone 8 application? .

OTHER TIPS

You are not able to use the fetched value in your OnNavigatedTo because OnNavigatedTo is called in parallel to your async method which you started in Application_Launching.

This is because Application_Launching has return type void and void methods cannot be awaited - because they have nothing meaningful to return ;-)

That means the app is started and you can trigger some things to do. Then the app determines which page to load and fires your OnNavigatedTo method.

Now you can either trigger your async method there, or hold a global variable which you can access that holds the task status of your result and check against this one.

Let me give an example for that task stuff. Consider your Application_Launched method:

MyStaticClass.Result = tac.SendRequestAsync();

the data definition in MyStaticClass is:

public static Task<string> Result;

Now in your Phone page OnNavigatedTo:

if (MyStaticClass.Result != null)
{
    string myResult = await MyStaticClass.Result;
}

Instead of a static class or property you may also use a singleton.

But keep in mind, the OnNavigatedTo method is also a void method and your viewModel will already get read requests from the underlying UI even though your data load might not yet be finished.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top