4

I am creating an application that requests a small amount of data from a webserver in JSON format, deserializes the data using Json.net, and returns a list of a custom object. I am debugging the application using an android phone.

A listview is then created using the created list. The ui code seems to work with premade objects inserted into the list instead of the async method. The async method crashed the application.

public partial class Membership : ContentPage
{
    int increment = 1;

    public Membership()
    {
        //begin task
        var task = GetMembers(increment);

        //irrelevant stuff happening

        //wait for task to complete to continue execution
        List<Person> people = task.Result;


        // Create the ListView.
        ListView listView = new ListView
        {

            // Source of data items.
            ItemsSource = people,

            // Define template for displaying each item.
            // (Argument of DataTemplate constructor is called for 
            //      each item; it must return a Cell derivative.)
            ItemTemplate = new DataTemplate(() =>
            {
                // Create views with bindings for displaying each property.
                Label nameLabel = new Label();
                Label statusLabel = new Label();


                nameLabel.SetBinding(Label.TextProperty, "name");
                statusLabel.SetBinding(Label.TextProperty, "status");

                // Return an assembled ViewCell.
                return new ViewCell
                {
                    View = new StackLayout
                    {
                        Padding = new Thickness(0, 5),
                        Orientation = StackOrientation.Horizontal,
                        Children =
                            {
                                //boxView,
                                new StackLayout
                                {
                                    VerticalOptions = LayoutOptions.Center,
                                    Spacing = 0,
                                    Children =
                                    {
                                        nameLabel,
                                        statusLabel
                                    }
                                    }
                            }
                    }
                };
            })
        }; 

        // Build the page.
        this.Content = new StackLayout
        {
            Children =
            {
                header,
                listView
            }
        };


    }

And the async task in question:

async Task<List<Person>> GetMembers(int increment)
{
        string jsonString;

        using (var client = new HttpClient())
        {
            //gets the members from the server in json format
            //increment defines which set of members (1-50, 51-100, etc)
            var responseString = await client.GetStringAsync("GENERIC URL" + "increment=" + increment.ToString());
            jsonString = responseString;
        }

        List<Person> memberList = JsonConvert.DeserializeObject<List<Person>>(jsonString);

        return memberList;
}

Now, I've tested this code by bypassing the async task and creating a list of a couple of predefined People, and it worked just fine.

I understand using the task.Result; method will block the application until the async task completes, but, when unit testing there were no problems with speed so I am a bit confused. Any help would be appreciated.

BHigzz
  • 45
  • 1
  • 5
  • 2
    Do you have any ErrorMessage? What means Application Crashing? Make sure that Task.Result can cause Deadlocks: http://stackoverflow.com/questions/17248680/await-works-but-calling-task-result-hangs-deadlocks – Sebi Mar 17 '17 at 06:04
  • No error messages, it just freezes. I do believe you might be correct about the deadlock part. I will have to research alternative ways to do this task. Thanks. – BHigzz Mar 17 '17 at 06:09
  • 1
    Yes this is a deadlock because two threads want to access same Context. You simply can await the call and make the Membership-Method async as well. – Sebi Mar 17 '17 at 06:11
  • 1
    I posted it as answer, so it's better documented. – Sebi Mar 17 '17 at 06:21

1 Answers1

3

Because you say:

"No error messages, it just freezes."

In comments, I'm sure you have a deadlock situation here. The Problem with .Result and .Wait() is well described in this SO-Ticket:

await works but calling task.Result hangs/deadlocks

Your continuation tries to access a Context which is blocked by .Result. You can rebuild your Method like this:

public async Task InitializeMembership()
{
    //begin task
    var task = GetMembers(increment);

    //irrelevant stuff happening

    //wait for task to complete to continue execution
    List<Person> people = await task;

    //Do further stuff    
}

As you can see I would use a Method and not the constructor because I think an async constructor isn't best practice. I hope this can solve your issue.

Community
  • 1
  • 1
Sebi
  • 3,879
  • 2
  • 35
  • 62
  • You were 100% correct Sebi, I appreciate your help. The main problem I had was not being able to cascade async to the top level method Membership because it was essentially my main function and could not be async. Putting the ui code into the async InitializeMembership() function solved this. – BHigzz Mar 17 '17 at 06:34
  • @BHigzz Your welcome. Don't forget to mark the answer as solved please ;-). – Sebi Mar 17 '17 at 06:37