Understanding the toast in a loop using Thread.sleep

Xenon Kfr :

I'm new in Android development. Just wanted to test following code where clicking the button do some tasks. Toasts are popping out after the log shows the loop is ended as Toast is asynchronous. But

  • When I comment out the Thread.sleep() (looping through without thread.sleep()),all toasts are showing from 0 to 14
  • When Thread.sleep(1000), then toasts are showing from 7 to 14
  • When Thread.sleep(500), then toasts are showing from 3 to 14
  • When Thread.sleep(2000), only 14th toast is shown

why does it behave like this? And so far as I know, this all happened in UI thread. If there is one thread, then how toast works asynchronously?

Thank you

button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int i=0;
                int j=0;
                Log.d("tag", "sec "+i);
                while(i++<15){
                    Log.d("tag", "sec "+i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    Toast.makeText(getApplicationContext(),"num"+(j),Toast.LENGTH_SHORT).show();
                    j++;
                 }

            }
        });
Chrisvin Jem :

Toast is asynchronous

To begin with, Toast's are not asynchronous by default.

why does it behave like this?

The lifetime of a toast created with Toast.LENGTH_SHORT is 2 seconds. Now, if your UI thread is locked up (because of the Thread.sleep), then the toast won't appear on the screen. It would 'die' before it is shown.

How to make it work as expected

Run the whole block inside a newly created Thread and only create & display the Toast in the main thread, You'll need to do something along the following lines,

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 0;
                int j = 0;
                Log.d("tag", "sec " + i);
                while (i++ < 15) {
                    Log.d("tag", "sec " + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    final int finalJ = j;
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), "num" + (finalJ), Toast.LENGTH_SHORT).show();
                        }
                    });
                    j++;
                }
            }
        }).start();
    }
});

You could also achieve similar results without using a different thread by using Handler.postDelayed

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        final int[] i = {0};
        final int[] j = {0};
        final int delayTime = 1000;

        Log.d("tag", "sec "+ i[0]);
        final Handler handler = new Handler();
        final Runnable runnable = new Runnable() {
            @Override
            public void run() {
                if(i[0]++<15) {
                    Log.d("tag", "sec " + i[0]);
                    Toast.makeText(getApplicationContext(), "num" + (j[0]), Toast.LENGTH_SHORT).show();
                    j[0]++;
                    handler.postDelayed(this, delayTime);
                }
            }
        };
        handler.postDelayed(runnable, delayTime);
    }
});

P.S - Never use Thread.sleep in main thread, it blocks the UI and results in poor UX.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=162134&siteId=1