95

I have an Android app running a thread. I want a Toast message to show with a message.

When I do this, I get the below exception:

Logcat trace:

FATAL EXCEPTION: Timer-0 
 java.lang.RuntimeException: Can't create handler inside thread that has not 
    called Looper.prepare()

 at android.os.Handler.<init>(Handler.java:121)
 at android.widget.Toast$TN.<init>(Toast.java:322)
 at android.widget.Toast.<init>(Toast.java:91)
 at android.widget.Toast.makeText(Toast.java:238) 

Is there a work around for pushing Toast messages from threads to the User Interface?

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
Amar
  • 997
  • 2
  • 7
  • 6

7 Answers7

125

I got this exception because I was trying to make a Toast popup from a background thread.
Toast needs an Activity to push to the user interface and threads don't have that.
So one workaround is to give the thread a link to the parent Activity and Toast to that.

Put this code in the thread where you want to send a Toast message:

parent.runOnUiThread(new Runnable() {
    public void run() {
        Toast.makeText(parent.getBaseContext(), "Hello", Toast.LENGTH_LONG).show();
    }
});

Keep a link to the parent Activity in the background thread that created this thread. Use parent variable in your thread class:

private static YourActivity parent;

When you create the thread, pass the parent Activity as a parameter through the constructor like this:

public YourBackgroundThread(YourActivity parent) {
    this.parent = parent;
}

Now the background thread can push Toast messages to the screen.

pevik
  • 4,523
  • 3
  • 33
  • 44
Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
  • 2
    How do i call a method in that Activity, which i am making a toast message in with this code? – lxknvlk Mar 22 '15 at 09:20
  • Please don't post followup questions as comments. Comments are for talking about the question at the top, and the fitness of how well my answer answers that. It is important we follow the rules. Everyone following the rules is what keeps stackoverflow great, and prevents it from turning into a circus like yahoo answers. – Eric Leschinski Mar 22 '15 at 12:44
  • 1
    For Xamarin Android, Activity.RunOnUiThread(() => Toast.MakeText(this.Activity.ApplicationContext, "Message Text", ToastLength.Short).Show()); – The Mitra Boy Apr 25 '16 at 09:21
33

Android basically works on two thread types namely UI thread and background thread. According to android documentation -

Do not access the Android UI toolkit from outside the UI thread to fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:

Activity.runOnUiThread(Runnable)  
View.post(Runnable)  
View.postDelayed(Runnable, long)

Now there are various methods to solve this problem. I will explain it by code sample

runOnUiThread

new Thread()
{
    public void run()
    {
        myactivity.this.runOnUiThread(new runnable()
        {
            public void run()
            {
                //Do your UI operations like dialog opening or Toast here
            }
        });
    }
}.start();

LOOPER

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }

AsyncTask

AsyncTask allows you to perform asynchronous work on your user interface. It performs the blocking operations in a worker thread and then publishes the results on the UI thread, without requiring you to handle threads and/or handlers yourself.

public void onClick(View v) {
    new CustomTask().execute((Void[])null);
}


private class CustomTask extends AsyncTask<Void, Void, Void> {

    protected Void doInBackground(Void... param) {
        //Do some work
        return null;
    }

    protected void onPostExecute(Void param) {
        //Print Toast or open dialog
    }
}

Handler

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue.

Message msg = new Message();


    new Thread()
    {
        public void run()
        {
            msg.arg1=1;
            handler.sendMessage(msg);
        }
    }.start();



    Handler handler = new Handler(new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            if(msg.arg1==1)
            {
                //Print Toast or open dialog        
            }
            return false;
        }
    });
kumar kundan
  • 2,027
  • 1
  • 27
  • 41
10

Here's what I've been doing:

  public void displayError(final String errorText) {
    Runnable doDisplayError = new Runnable() {
        public void run() {
            Toast.makeText(getApplicationContext(), errorText, Toast.LENGTH_LONG).show();
        }
    };
    messageHandler.post(doDisplayError);
}

That should allow the method to be called from either thread.

Where messageHandler is declared in the activity as ..

Handler messageHandler = new Handler();
Evan Langlois
  • 4,050
  • 2
  • 20
  • 18
6

From http://developer.android.com/guide/components/processes-and-threads.html :

Additionally, the Android UI toolkit is not thread-safe. So, you must not manipulate your UI from a worker thread—you must do all manipulation to your user interface from the UI thread. Thus, there are simply two rules to Android's single thread model:

  1. Do not block the UI thread
  2. Do not access the Android UI toolkit from outside the UI thread

You have to detect idleness in a worker thread and show a toast in the main thread.

Please post some code, if you want a more detailed answer.

After code publication :

In strings.xml

<string name="idleness_toast">"You are getting late do it fast"</string>

In YourWorkerThread.java

Toast.makeText(getApplicationContext(), getString(R.string.idleness_toast), 
    Toast.LENGTH_LONG).show();

Don't use AlertDialog, make a choice. AlertDialog and Toast are two different things.

pevik
  • 4,523
  • 3
  • 33
  • 44
  • idleTimer = new IdleTimer(5000, new IIdleCallback() { @Override public void inactivityDetected() { AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder( getApplicationContext()); Toast.makeText(null, "you are getting late do it fast...", Toast.LENGTH_LONG) .show(); } }); if(idleTimer.checkIsTimerRunning()){ idleTimer.stopIdleTimer(); } idleTimer.startIdleTimer(); – Amar Jun 29 '13 at 10:26
  • i just want that the thing that i want in my idleTimer thread to display over my main ui should come over that... – Amar Jun 29 '13 at 10:30
  • sorry i meshed up all the thing the code is something like this – Amar Jun 29 '13 at 10:57
  • idleTimer = new IdleTimer(5000, new IIdleCallback() { @Override public void inactivityDetected() { Toast.makeText(getApplicationContext(), "you are getting late do it fast...", Toast.LENGTH_LONG) .show(); } }); if(idleTimer.checkIsTimerRunning()){ idleTimer.stopIdleTimer(); } idleTimer.startIdleTimer(); – Amar Jun 29 '13 at 10:57
4
runOnUiThread(new Runnable(){
   public void run() {
     Toast.makeText(getApplicationContext(), "Status = " + message.getBody() , Toast.LENGTH_LONG).show();
   }
 });

this works for me

Ajeet Choudhary
  • 1,969
  • 1
  • 17
  • 41
umar farooq
  • 350
  • 1
  • 2
  • 8
1

You can simply use BeginInvokeOnMainThread(). It invokes an Action on the device main (UI) thread.

Device.BeginInvokeOnMainThread(() => { displayToast("text to display"); });

It is simple and works perfectly for me!

EDIT : Works if you're using C# Xamarin

Vozek
  • 21
  • 4
  • Isn't that a Xamarin specific API and not related to this question about Android Native (w/o Xamarin tags)? – Splaktar Oct 03 '16 at 22:32
  • Yes, you're right. Sorry for that, I haven't noticed. But people using Xamarin looking for a solution for the error are landing on this page. So I let the post here, just precising it is for Xamarin. – Vozek Oct 13 '16 at 11:24
  • From which assembly/namespace does 'Device' come from? – MJ33 Feb 27 '18 at 10:42
  • @MJ33 It's from Xamarin.Forms – Hagbard Apr 29 '19 at 14:22
0

I got this error in a JobService from the following code:

    BluetoothLeScanner bluetoothLeScanner = getBluetoothLeScanner();
    if (BluetoothAdapter.STATE_ON == getBluetoothAdapter().getState() && null != bluetoothLeScanner) {
        // ...
    } else {
        Logger.debug(TAG, "BluetoothAdapter isn't on so will attempting to turn on and will retry starting scanning in a few seconds");
        getBluetoothAdapter().enable();
        (new Handler()).postDelayed(new Runnable() {
            @Override
            public void run() {
                startScanningBluetooth();
            }
        }, 5000);
    }

The service crashed:

2019-11-21 11:49:45.550 729-763/? D/BluetoothManagerService: MESSAGE_ENABLE(0): mBluetooth = null

    --------- beginning of crash
2019-11-21 11:49:45.556 8629-8856/com.locuslabs.android.sdk E/AndroidRuntime: FATAL EXCEPTION: Timer-1
    Process: com.locuslabs.android.sdk, PID: 8629
    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:203)
        at android.os.Handler.<init>(Handler.java:117)
        at com.locuslabs.sdk.ibeacon.BeaconScannerJobService.startScanningBluetoothAndBroadcastAnyBeaconsFoundAndUpdatePersistentNotification(BeaconScannerJobService.java:120)
        at com.locuslabs.sdk.ibeacon.BeaconScannerJobService.access$500(BeaconScannerJobService.java:36)
        at com.locuslabs.sdk.ibeacon.BeaconScannerJobService$2$1.run(BeaconScannerJobService.java:96)
        at java.util.TimerThread.mainLoop(Timer.java:555)
        at java.util.TimerThread.run(Timer.java:505)

So I changed from Handler to Timer as follows:

   (new Timer()).schedule(new TimerTask() {
                @Override
                public void run() {
                    startScanningBluetooth();
                }
            }, 5000);

Now the code doesn't throw the RuntimeException anymore.

Michael Osofsky
  • 11,429
  • 16
  • 68
  • 113