I'm trying to understand how Handler works in a pair with Looper, but i have some problem. I need to do some long operation in a back thread and then to send some result in a textView.
I get the following error after pressing a button:
Only the original thread that created a view hierarchy can touch its views.
public class MainActivity extends AppCompatActivity {
Button mButton;
TextView mTextView;
ConsumeThread mConsumeThread;
class ConsumeThread extends Thread{
public Handler mHandler;
#Override
public void run() {
Looper.prepare();
mHandler = new Handler(){
#Override
public void handleMessage(Message msg){
int arg = msg.what;
someLongOperation(arg);
}
};
Looper.loop();
}
private void someLongOperation(int arg){
// do some long operation
arg += 1000;
mTextView.setText("Operation's code is " +arg); // fatal exception
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.txt_view);
mButton = (Button) findViewById(R.id.button);
mConsumeThread = new ConsumeThread();
mConsumeThread.start();
mButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
if(mConsumeThread.mHandler != null){
Message msg = mConsumeThread.mHandler.obtainMessage(10);
mConsumeThread.mHandler.sendMessage(msg);
}
}
});
}
To get Main Thread Handler You have get Handler as follows .
Because:-
Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it
So you need to get Handler which is associated with MainThread. For that you can use one of the following:-
With Context
Handler mainHandler = new Handler(getMainLooper()){
#Override
public void handleMessage(Message msg) {
}
};
Or Directly with Looper even when do not have Context
Handler mainHandler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
}
};
I assume that you are doing some long running task . So its better if you go with AsyncTask.
private void someLongOperation(int arg){
// do some long operation
arg =+ 1000;
mTextView.setText("Operation's code is " +arg); // fatal exception
}
//see here, you are in worker thread, so you can't excess UI toolkit or else exception, so if you want to do something ui related task in worker thread, use runOnUi, see here
runOnUiThread(new Runnable() {
#Override
public void run() {
textview.setText("");
}
})
You can't update the UI from another thread. You have to move the code that updates the UI to the UIThread.
Try Using:
runOnUiThread(new Runnable() {
#Override
public void run() {
//TextView Update Code
}
});
Tip: Try to reduce the number of lines of code you put inside this, as then there would be no purpose of using another thread.
Only the original thread that created a view hierarchy can touch its views
You have to do ui related work on the main thread...
So you can do it like this...
private void someLongOperation(int arg){
// do some long operation
arg =+ 1000;
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
mTextView.setText("Operation's code is " +arg); // fatal exception
}
});
}
Quoting from this
Inside the Handler, override the handleMessage() method. The Android system invokes this method when it receives a new message for a thread it's managing; all of the Handler objects for a particular thread receive the same message.
My doubt is here : all of the Handler objects for a particular thread receive the same message
I have created a activity, which have below code
#Override
protected void onPostCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onPostCreate(savedInstanceState);
self = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message msg) {
Log.e(TAG,"HAndle MainActivity: "+msg.what);
};
};
sen = new Sender(new Handler(Looper.getMainLooper()), Thread.NORM_PRIORITY);
sen.start();
sen.repeatMsg();
}
Where sender is a thread, which creates a Handler(Looper.getMainLooper())
public class Sender extends Thread{
protected static final String TAG = "Sender";
private Looper mLooper;
private Handler uiHandler;
private int mPriority;
public Sender(Handler handler,int priority) {
// TODO Auto-generated constructor stub
mPriority = priority;
uiHandler = handler;
}
#Override
public void run() {
// TODO Auto-generated method stub
Log.e(TAG,"Inside run");
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
// setPriority(mPriority);
Process.setThreadPriority(mPriority);
Looper.loop();
Handler msgHandler = new Handler(mLooper){
#Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
Log.e(TAG,"Inside handleMessage "+msg.what);
}
};
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been
// created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
public void repeatMsg() {
Log.e(TAG,"Inside repeatMsg");
Timer t = new Timer();
TimerTask tt = new TimerTask() {
#Override
public void run() {
Handler h = new Handler(Looper.getMainLooper());
Message m = Message.obtain();
m.what = 1000;
h.dispatchMessage(m);//sendMessage(m);
repeatMsg();
}
};
t.schedule(tt, 2000);
}
}
What i expect is , the dispatched message should be received by the self handler, because both handlers bound to same thread, and is what the quoted statement states. But self's handleMessage is never called.
Why this mess?? What i mis understood??
What i should do to get my expected behaviour?
Also, Consider again a SeconMainActivity, which is similar to MainActivity, On some click event, say, the SeconMainActivity is launched, with same onPostExecute of MainActivity. Meanwhile, the Sender thread is still repeating the message. Now, who will receive the handleMessge call??
MainActivity ?
SecondMainActivity ?
Handler inside Sender Thread ?
None !
Update : Year 2018 , the doc still reads the same
This is a class which extends Thread and implements the run() function:
public class TestThread extends Thread{
public Handler handler;
public TestThread(){
handler = new Handler(){
#Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
}
};
}
public Looper getLooper(){
return Looper.myLooper();
}
#Override
public void run() {
// TODO Auto-generated method stub
Looper.prepare();
Looper.loop();
}
}
Now in a button in the main activity I have this code:
TestThread t=new TestThread();
t.start();
Handler h=new Handler(t.getLooper());
h.post(new Runnable(){
#Override
public void run() {
// TODO Auto-generated method stub
while (true);
}
});
As far as I know this is supposed to put the runnable in the target Thread's message queue and the thread (not UI thread) will run it when possible.
But this code blocks the UI. why does this happen? As you see I sent the target thread's looper to the Handler constructor and the handler should use that looper not the main thread's looper.
Looper.myLooper() returns the current thread looper which is the calling UI thread's looper for you. Then you make a handler with it and post a blocking runnable there.
To make this "work", move the myLooper() call under the thread run() method.
I am having troubles with Threading ;)
I have done an script about my class as follow:
class Test{
TextToSpeech mTts = new TextToSpeech();
boolean volatile mVarGlobal = false;
class T1 extends Thread{
public void run(){
doSomething(){
mVarGlobal = false;
// network working...Take a while
mTts.speak(...) //Speech information received from Network
mVarGlobal = true;
}
}
T1 t = new T1();
t.start();
class CheckVarGlobal extends Thread{
public void run(){
if(!mVarGlobal) {
text2Speech.speak("something");
}
postAtTime(this, 3000);
}
}
CheckVarGlobal c = new CheckVarGlobal ();
c.start();
}
As you can see I have 2 Threads running, one which is getting network information and a second one which is cheking if the information from network has been received. The boolean variable mVarGlobal will be true, and then the Thread which is checking "CheckVarGlobal" will stop as the condition (!mVarGlobal) is false.
The problem is that once i get the information in the 1st Thread from network and speech the information, 2nd Thread still is running and speeching "something". I guess the 2nd Thread has not realize that CheckVarGlobal is true... I have typed the variable as volatile as it is used from 2 threads.
Any idea why is it happening, and how to solve it?
Thanks a lot,
Best.
David.
Dayerman... Here is some EXPERIMENTAL code with two handlers.
Edit. I have edited the code to comply with best practices, using a switch in a single handler:
private Handler myHandler= new Handler(){
#Override
public void handleMessage(Message msg){
switch(msg.what){
case 0:
if (!isDoneThinking){
editTextConfusedText.setText("Still Thinking "+new Integer(thinkSeconds).toString());
thinkSeconds++;
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0),1000);
}
else {
thinkSeconds= 0; // reset timer
}
break;
case 1:
isDoneThinking= true;
onThreadedMessage(msg);
break;
default:
super.handleMessage(msg);
break;
}
}
};
public void onThreadMessage(Message msg){
Bundle b= msg.getData();
String encryptedText="";
if (b != null){
encryptedText= b.getString("encryptedText");
}
editTextConfusedText.setText(encryptedText);
Log.d(TAG,encryptedText);
}
Usage:
buttonConfuseText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
final String inString= editTextPlainText.getText().toString();
isDoneThinking= false;
myHandler.sendEmptyMessage(0); // <=== START TIMER
Thread thread= new Thread( new Runnable() { // <== START THREADED RUNNABLE
public void run() {
String outString= encrypt(password,inString);
Message msg= myHandler.obtainMessage(1);
Bundle b= new Bundle();
b.putString("encryptedText",outString);
msg.setData(b);
myHandler.sendMessage(msg);
Log.d(TAG,outString);
}
});
thread.setDaemon(true);
thread.start();
}
};
Your are checking the variable in if condition rather than while condition.That means whenever it comes to thread 2 that time only it is checking.Better to use while condition in thread2.
How can I display Toast messages from a thread?
You can do it by calling an Activity's runOnUiThread method from your thread:
activity.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
}
});
I like to have a method in my activity called showToast which I can call from anywhere...
public void showToast(final String toast)
{
runOnUiThread(() -> Toast.makeText(MyActivity.this, toast, Toast.LENGTH_SHORT).show());
}
I then most frequently call it from within MyActivity on any thread like this...
showToast(getString(R.string.MyMessage));
This is similar to other answers, however updated for new available apis and much cleaner. Also, does not assume you're in an Activity Context.
public class MyService extends AnyContextSubclass {
public void postToastMessage(final String message) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
}
});
}
}
One approach that works from pretty much anywhere, including from places where you don't have an Activity or View, is to grab a Handler to the main thread and show the toast:
public void toast(final Context context, final String text) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
Toast.makeText(context, text, Toast.LENGTH_LONG).show();
}
});
}
The advantage of this approach is that it works with any Context, including Service and Application.
Like this or this, with a Runnable that shows the Toast.
Namely,
Activity activity = // reference to an Activity
// or
View view = // reference to a View
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
showToast(activity);
}
});
// or
view.post(new Runnable() {
#Override
public void run() {
showToast(view.getContext());
}
});
private void showToast(Context ctx) {
Toast.makeText(ctx, "Hi!", Toast.LENGTH_SHORT).show();
}
Sometimes, you have to send message from another Thread to UI thread. This type of scenario occurs when you can't execute Network/IO operations on UI thread.
Below example handles that scenario.
You have UI Thread
You have to start IO operation and hence you can't run Runnable on UI thread. So post your Runnable to handler on HandlerThread
Get the result from Runnable and send it back to UI thread and show a Toast message.
Solution:
Create a HandlerThread and start it
Create a Handler with Looper from HandlerThread:requestHandler
Create a Handler with Looper from Main Thread: responseHandler and override handleMessage method
post a Runnable task on requestHandler
Inside Runnable task, call sendMessage on responseHandler
This sendMessage result invocation of handleMessage in responseHandler.
Get attributes from the Message and process it, update UI
Sample code:
/* Handler thread */
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());
final Handler responseHandler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
//txtView.setText((String) msg.obj);
Toast.makeText(MainActivity.this,
"Runnable on HandlerThread is completed and got result:"+(String)msg.obj,
Toast.LENGTH_LONG)
.show();
}
};
for ( int i=0; i<5; i++) {
Runnable myRunnable = new Runnable() {
#Override
public void run() {
try {
/* Add your business logic here and construct the
Messgae which should be handled in UI thread. For
example sake, just sending a simple Text here*/
String text = "" + (++rId);
Message msg = new Message();
msg.obj = text.toString();
responseHandler.sendMessage(msg);
System.out.println(text.toString());
} catch (Exception err) {
err.printStackTrace();
}
}
};
requestHandler.post(myRunnable);
}
Useful articles:
handlerthreads-and-why-you-should-be-using-them-in-your-android-apps
android-looper-handler-handlerthread-i
Get UI Thread Handler instance and use handler.sendMessage();
Call post() method handler.post();
runOnUiThread()
view.post()
You can use Looper to send Toast message. Go through this link for more details.
public void showToastInThread(final Context context,final String str){
Looper.prepare();
MessageQueue queue = Looper.myQueue();
queue.addIdleHandler(new IdleHandler() {
int mReqCount = 0;
#Override
public boolean queueIdle() {
if (++mReqCount == 2) {
Looper.myLooper().quit();
return false;
} else
return true;
}
});
Toast.makeText(context, str,Toast.LENGTH_LONG).show();
Looper.loop();
}
and it is called in your thread. Context may be Activity.getContext() getting from the Activity you have to show the toast.
I made this approach based on mjaggard answer:
public static void toastAnywhere(final String text) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
Toast.makeText(SuperApplication.getInstance().getApplicationContext(), text,
Toast.LENGTH_LONG).show();
}
});
}
Worked well for me.
Kotlin Code with runOnUiThread
runOnUiThread(
object : Runnable {
override fun run() {
Toast.makeText(applicationContext, "Calling from runOnUiThread()", Toast.LENGTH_SHORT)
}
}
)
I encountered the same problem:
E/AndroidRuntime: FATAL EXCEPTION: Thread-4
Process: com.example.languoguang.welcomeapp, PID: 4724
java.lang.RuntimeException: Can't toast on a thread that has not called Looper.prepare()
at android.widget.Toast$TN.<init>(Toast.java:393)
at android.widget.Toast.<init>(Toast.java:117)
at android.widget.Toast.makeText(Toast.java:280)
at android.widget.Toast.makeText(Toast.java:270)
at com.example.languoguang.welcomeapp.MainActivity$1.run(MainActivity.java:51)
at java.lang.Thread.run(Thread.java:764)
I/Process: Sending signal. PID: 4724 SIG: 9
Application terminated.
Before: onCreate function
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Toast.makeText(getBaseContext(), "Thread", Toast.LENGTH_LONG).show();
}
});
thread.start();
After: onCreate function
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getBaseContext(), "Thread", Toast.LENGTH_LONG).show();
}
});
it worked.
java 11:
var handler = new Handler(Looper.getMainLooper);
handler.post(() -> Toast.makeText(your_context, "Hi!", Toast.LENGTH_SHORT).show());
Lambdas are available in java 8 though. var is introduced in java 11.
Contrary to almost every answer here, Toast#makeText and Toast#show do NOT have to run on the UI thread. The only requirement is that it runs on a thread that has called Looper#prepare.
The reasons for this is because toasts are handled and rendered by the OS, not the application. Internally, Toast#show makes a call to a system service to enqueue the toast.
This means the following code is valid
private static class MyThread extends Thread {
public Handler handler;
#Override
public void run() {
Looper.prepare();
handler = new Handler(Looper.myLooper()) {
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Looper.loop()
}
}
final private MyThread t = new MyThread();
// start and wait for t to start looping
private void onClick() {
t.handler.post(() -> Toast.makeText(this, "this works", Toast.LENGTH_SHORT).show());
}
Method in onCreate :
private void toastPublic(final String message){
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
Toast.makeText(getBaseContext(),""+message,
4 /*Toast.LENGTH_SHORT*/).show();
}});
}
Next : use in inside Thread