nix
nix

Reputation: 2285

Why isn't the label text changed instantly?

I was trying to test the speed of ModernHttpClient, when I encountered the following: the label isn't updated and neither the button is disabled at once in the following code (tested on Nexus 5x), but rather waits for the click to be processed until the end! What is that, a mistake from my part, a bug or a weird "optimization" feature?

Page1.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ModernClientBenchmark.Page1">
  <StackLayout>
    <Button Text="Run the test!" Clicked="OnTestClicked" x:Name="btn"></Button>
    <Label Text="here we are" x:Name="labl"/>
  </StackLayout>
</ContentPage>

Page1.xaml.cs:

public partial class Page1 : ContentPage
{
    private const int TESTQ = 3;

    public Page1()
    {
        InitializeComponent();
    }

    private async void OnTestClicked(object sender, EventArgs e)
    {
        btn.IsEnabled = false;
        var l1 = new double[TESTQ];
        var l2 = new double[TESTQ];
        for (int i = 0; i < TESTQ; i++)
        {
            Device.BeginInvokeOnMainThread(() => labl.Text = "Step " + i);
            l2[i] = (await ManagedNetworkSpeedAsync(true)).Milliseconds;
            //l1[i] = (await NativeNetworkSpeedAsync(true)).Milliseconds;
        }

        //var avg1 = l1.Sum() / TESTQ;
        //var avg2 = l2.Sum() / TESTQ;

        var a = 234;

    }

    public static async Task<TimeSpan> NativeNetworkSpeedAsync(bool secure)
    {
        using (var client = new HttpClient(new NativeMessageHandler()))
        {
            return await RunTestAsync(secure, client);
        }
    }

    public static async Task<TimeSpan> ManagedNetworkSpeedAsync(bool secure)
    {
        using (var client = new HttpClient())
        {
            return await RunTestAsync(secure, client);
        }
    }

    public static async Task<TimeSpan> RunTestAsync(bool secure, HttpClient client)
    {
        try
        {

            var start = DateTime.Now;
            for (var i = 0; i <= 1; i++)
            {
                var result = client.GetStreamAsync(secure ? "https://xamarin.com" : "http://httpbin.org/ip").Result;
                result.Dispose();
            }
            return DateTime.Now - start;
        }
        catch (Exception ex)
        {
            var a = 234;
        }
        return new TimeSpan();
    }
}

Upvotes: 1

Views: 269

Answers (2)

SushiHangover
SushiHangover

Reputation: 74144

You are on already on the UIThread and are blocking yourself, you need to place your work onto the thread pool and update the UI from there, an await Task.Run block will do the job.

This will give you the results that you expect:

private async void OnTestClicked(object sender, EventArgs e)
{
    btn.IsEnabled = false;
    var l2 = new double[TESTQ];
    await Task.Run(async () => {
        for (int i = 0; i < TESTQ; i++)
        {
            Device.BeginInvokeOnMainThread(() => labl.Text = "Step " + i);
            l2[i] = (await ManagedNetworkSpeedAsync(true)).Milliseconds;
        }
    });
    foreach (var x in l2)
    {
        System.Diagnostics.Debug.WriteLine(x);
    }
    btn.IsEnabled = true;
}

Upvotes: 1

Mat&#237;as Fidemraizer
Mat&#237;as Fidemraizer

Reputation: 64933

Since you're starting an asynchronous operation and it's being synchronized with the UI thread, the whole label won't change until UI thread has time to perform the whole change.

This is the regular behavior of modern applications, where you don't want to block the UI for long running operations (i.e. asynchronous operations like requesting external resources through HTTP).

BTW, you can disable your button before starting the asynchronous operation and enable it again once it has been finished.

Upvotes: 1

Related Questions