简体   繁体   中英

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();
    }
}

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 (ie 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.

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM