2

I have a problem.

I created an Android page with some buttons and a GridView. Now the GridView gets filled with a json from my website and I want to reload the GridView every 3 seconds, without slowing down the app!!! So I tried this:

In the Page1 load void: RunOnUiThread(() => LoadOrdersAsync());

And the function:

public async Task LoadOrdersAsync()
{
    while (true)
    {
        //Creating SortedList

        if (OrderListAdapter == null)
        {

            //Fill the DataSource of the ListView with the Array of Names
            OrderListAdapter = new OrderListAdapter(this, SortedOrderList);
            GridviewOrders.Adapter = OrderListAdapter;
        }
        else
        {
            OrderListAdapter.refresh(SortedOrderList);
        }

        // don't run again for at least 3 seconds
        await Task.Delay(3000);
    }
}

The problem is that everything get's filled, but the UI is lagging, because of that thread. How can I fix this problem?

3 Answers3

2

You cannot refresh your grid view without interupting ui, since refreshing grid view has to be done on ui thread.

If app is laggy then you want fill your grid view with too much data at once so it is taking time to render all rows.

The solution for you is to prepare some paging mechanism, i mean, load only visible amout of data + few more and when the user attemps to scroll down and he is trying to scroll more than available rows then add another rows to your gridview and do it over and over when the user is scrolling down.

I had common problem in a past, i was writting xamarin forms app and i had a list of 100 000 records. If you load all at once you will hang for few seconds, so i have came up with paging, and filling more when needed and it was really nice.

You could add some busy indicator for user expirience.

Edit 1:

At first as others have already mentioned you need to use ui thread only to update data in control. I have mention about paging mechanism (but you havent said what amout of rows do you expect), but at first check the code below if you will have still problems then you need to do paging. You can try for ex updating your list only with 20 elements to test if it will help.

//page1 load
private void Load()
{
    LoadOrdersAsync();
}

private async Task LoadOrdersAsync()
{
    while (true)
    {
        //Creating SortedList
        var SortedOrderList = //add your logic

        RunOnUiThread( ()=>        
        {
            if (OrderListAdapter == null)
            {

                //Fill the DataSource of the ListView with the Array of Names
                OrderListAdapter = new OrderListAdapter(this, SortedOrderList);
                GridviewOrders.Adapter = OrderListAdapter;
            }
            else
            {
                OrderListAdapter.refresh(SortedOrderList);
            }
        });

        // don't run again for at least 3 seconds
        await Task.Delay(3000);
    }
}
Paweł Górszczak
  • 524
  • 1
  • 5
  • 12
  • look at the edit and try, and check result, check the diference. – Paweł Górszczak Dec 31 '18 at 15:14
  • With your code it is still lagging. The problem is more that my OrderListAdapter is a really big code... But that code is nessecary, so even if I have 4 rows. It still takes to long –  Dec 31 '18 at 15:41
  • Do you have debuger? Debug it or log time and check what is taking so long and the come back with precise question. You have gave us only this part of code, and you expect that everyone will somehow figureout how the code which you havent post here look like. – Paweł Górszczak Dec 31 '18 at 17:21
0

EDIT: added two TODO comments which should print messages on thread start and stop. Don't use breakpoints, they won't help you after debug. You really need to output error conditions if you want to have a stable user experience.

This may help outputting a string message:

Toast.MakeText(ApplicationContext, message, ToastLength.Long).Show();

I cannot answer your question but I can try to put you right on track.

Comment or reduce your code to the bare minimum as follows:

public async Task LoadOrdersAsync()
{
    // TODO: print "thread started"

    while (true)
    {
        // TODO: print "RUN!"

        await Task.Delay(3000);
    }

    // TODO: print "thread stopped"
}

Replace the TODO comment with an actual text output, such as a flash/toast message.

Once you're sure this runs as expected, the actual problem lies in your payload. When exceptions are thrown in secondary threads they are suppressed, the thread is killed but there's no immediate output.

Encapsulate your code in a try..catch clause and output the exception message as you did before. For usability keep the error output in place. You'll at least want to tell the future users of your app that something in the background isn't working. This is why you should keep a working error output in place.

This is the code scaffolding:

public async Task LoadOrdersAsync()
{
    // TODO: print "thread started"

    while (true)
    {
        try
        {
            // TODO: all your actual payload code goes here
        } catch (Exception ex)
        {
            // TODO: print ex.Message;
            break; // you may or may not want to break the loop at this point
        }

        await Task.Delay(3000);
    }

    // TODO: print "thread stopped"
}

I hope this will help you in your endeavours!

pid
  • 11,472
  • 6
  • 34
  • 63
  • I see that my code doesn't start up the task, because a breakpoint somewhere in that code doesn't breaks on that point!? –  Dec 30 '18 at 16:14
  • As I understand your sentence you have a breakpoint and the *bare minimum* code doesn't run? Why don't you kill all break points? Am I missing something? Please be more clear about what is going on. – pid Dec 30 '18 at 16:20
  • I inserted the try/catch inside the function. But the code didn't break on the `break;`, so I have set a breakpoint manually in Visual Studio, but it still doesn't get broken. That means that it never enters the function right? –  Dec 30 '18 at 16:21
  • Debugging async code with breakpoints may be more difficult than expected. The debugger should attach to the right thread. So your code may run but you don't see it because the breakpoint isn't active. I'll fix 2 things in my code so it helps you a bit more! – pid Dec 30 '18 at 16:23
  • I've edited my answer. Do the toast messages get displayed? That's an absolute requirement for this approach... – pid Dec 30 '18 at 16:29
  • This may help: https://stackoverflow.com/questions/7803256/android-toast-message-is-not-showing – pid Dec 30 '18 at 16:35
  • Especially this: "you also should enable 'Show notification' for the application in order Toast to work for you" – pid Dec 30 '18 at 16:36
  • The toast message gets dispalyed –  Dec 30 '18 at 21:51
0

In general you should minimize what is done in RunOnUiThread to the bare minimum. It means any calculations shouldn't be done in it, but just the UI related work.

So you should do something like:

var timer = new System.Threading.Timer(
{
  //Do any work that is not UI related
  RunOnUiThread(() => LoadOrders());
}, null, 0, 3000);

And the function would be:

public void LoadOrders()
{

    if (OrderListAdapter == null)
    {

        //Fill the DataSource of the ListView with the Array of Names
        OrderListAdapter = new OrderListAdapter(this, SortedOrderList);
        GridviewOrders.Adapter = OrderListAdapter;
    }
    else
    {
        OrderListAdapter.refresh(SortedOrderList);
    }
}
Ivan Ičin
  • 9,672
  • 5
  • 36
  • 57