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.
Related
1.I have a problem about creating a handler in a child thread
like
public class Main4Activity extends Activity {
private TextView mTextView;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.text_view);
new Thread() {
#Override
public void run() {
Looper.prepare();
#SuppressLint("HandlerLeak") Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
Toast.makeText(Main4Activity.this, "handler msg", Toast.LENGTH_SHORT).show();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
mTextView.setText("100");
}
};
handler.sendEmptyMessage(1);
Looper.loop();
}
}.start();
}
}
The above code will crash.
Process: com.example.hellokai.kotlindemo, PID: 27485
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6986)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1074)
at android.view.View.requestLayout(View.java:19889)
at android.view.View.requestLayout(View.java:19889)
at android.view.View.requestLayout(View.java:19889)
at android.view.View.requestLayout(View.java:19889)
at android.support.constraint.ConstraintLayout.requestLayout(ConstraintLayout.java:1959)
at android.view.View.requestLayout(View.java:19889)
at android.widget.TextView.checkForRelayout(TextView.java:7369)
at android.widget.TextView.setText(TextView.java:4480)
at android.widget.TextView.setText(TextView.java:4337)
at android.widget.TextView.setText(TextView.java:4312)
at com.example.hellokai.kotlindemo.Main4Activity$1$1.handleMessage(Main4Activity.java:40)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at com.example.hellokai.kotlindemo.Main4Activity$1.run(Main4Activity.java:45)
2.I know to update ui in the main thread,handler creation in the main thread to create, and then send a message in the child thread to the handler can update Ui.
3.My question is what is the role of the handler created in the child thread? When do we need to do that? What is the use of the scene?
Hope someone can solve my confusion!
I have a problem about creating a handler in a child thread.
Your are updating ui from the background thread.
For Example you could send a message from a thread and update ui like
private Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
Toast.makeText(Main4Activity.this.getApplicationContext(), "handler msg", Toast.LENGTH_SHORT).show();
mTextView.setText((String)msg.obj);
}
};
and then
new Thread() {
#Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg = Message.obtain(); // Creates an new Message instance
msg.obj = "Hello";
handler.sendMessage(msg);
}
}.start();
Or If you just want a delay there is not need for a thread, looper and sleep as already noted in comments by pskink https://developer.android.com/reference/android/os/Handler.html
I know to update ui in the main thread,handler creation in the main thread to create, and then send a message in the child thread to the
handler can update Ui.
Yes your are right you can create the handler on the ui thread you can send message from the thread and update your ui
My question is what is the role of the handler created in the child thread?
Handler is associated with a thread's looper. If you have a handler in ui thread its associated with it. In your case you have it inside a thread and hence handler is associated with that threads looper.
When do we need to do that? What is the use of the scene?
When you want to communicate from a backgroud thread to ui thread or from ui thread to background thread.
Your code
new Thread() {
#Override
public void run() {
Looper.prepare();
#SuppressLint("HandlerLeak") Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
Toast.makeText(Main4Activity.this, "handler msg", Toast.LENGTH_SHORT).show();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
mTextView.setText("100");
}
};
handler.sendEmptyMessage(1);
Looper.loop();
}
}.start();
Can be rewrote to use Android apis:
new Handler(getMainLooper()).postDelayed(
new Runnable() {
#Override
public void run() {
Toast.makeText(Main4Activity.this, "handler msg", Toast.LENGTH_SHORT).show();
mTextView.setText("100");
}
}
}, 200);
Note also that your main problem is that you create the Handler inside the Runnable that already is in a worker-thread, you can also create it early in the onCreate.
Start a thread in a service and try to update the UI in the thread by using handler. Can I just define a handler in the child thread using getMainLooper?
MainActivity.java:
public class MainActivity extends Activity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
btn.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
it=new Intent(getApplicationContext(), Myservice.class);
startService(it);
}
});
}
Handler mhandler= new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case 1:
edt.setText("Service");
break;
}
}
};
}
Myservice.java:
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
Thread thread = new Thread(new Runnable(){
#Override
public void run() {
// TODO Auto-generated method stub
Handler handler=new Handler(getMainLooper());
Message msg = Message.obtain(handler, 1);
handler.sendMessage(msg);
}
});
thread.start();
return super.onStartCommand(intent, flags, startId);
}
The Handler handler=new Handler(getMainLooper()); in the Service code is probably a different handler instance than the Activity handler( Handler mhandler= new Handler()). How about passing the handler from the Activity to the service.See my new Answer.
for e.g. Suppose there's a class called MYService :
public Myservice extends Service {
private Handler acitivityHandler;
public MyService(Handler handler) {
this.activityHandler = handler;
}
}
And from your activity when invoking the service pass-in the activity's handler instance
instead of relying on the main looper.
Handler mhandler= new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case 1:
edt.setText("Service");
break;
}
}
};
MyService myService = new MyService(mhandler);
That should work ideally.
Instead of accessing the UI elements from a (non-UI/main thread) access them from the Handler defined in your main activity.
From the (non-UI/main thread) just keep posting events to the handler to make UI updates.
//Inside the Thread's run method
public void run() {
if(uiNeedsToBeUpdated) {
handler.sendMessage(updateData);
}
}
In your MainActivity code (assuming you've a handler defined as a class member in your main activity), in the handler's handleMessage(), update the UI based on the progress made in the background task.
public void handleMessage (Message msg) {
switch(msg.getData().getInt("update")) {
//assuming you've bundled up int from the sending end
case 0 : { // background processing started, update UI
editText.setText("started");
break;
}
case 1 : { // background processing in progress, update UI
editText.setText("In progress");
break;
}
case 2: { //background processing finished , update UI
editText.setText("Done");
break;
}
}
}
Also you can use an Asyntask instead of a handler (http://examples.javacodegeeks.com/android/core/os/asynctask/android-asynctask-example/). Using an asynctask is easier as compared to a handler as it makes the programmer's life a little bit easier comparatively. Hope this info helps.
hi mate i have a thread that use looper. in the looper when he receive message , he call a blocking method (style read socket). How can can write method StopThread to stop this thread to terminate him ? have i to call quit on Handler or interrupt to thread ?
this is the code:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
try{
blocking call read
}catch(Interrupet Exception e) { }
}
};
Looper.loop();
}
public void stopThread(){ ??? }
}
Call
thread.interrupt()
to interrupt the thread.
Edit: You will have to post a runnable on the thread.
looperthread.handler.postrunnable(new Runnable(){
run(){
Looper.myLooper().quit();
}
});
hi mate in all tutorial or example the handler used in a looper is created inside the looper example:
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();
}
}
but if a create the handler before the thread, at example
public Handler mHandler=new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
is possible associate him at the thread in run() method in a looper or i must create him only in run method inside the looper ?
The constructor in Handler basically looks like this:
public Handler() {
....
mLooper = Looper.myLooper();
if (mLooper == null) {
// throws an exception
}
....
}
The field, mLooper is a package-private final field - and, of course, there's no setter available for you to change it later on.
In short, the Handler() constructor will associate itself with Looper.myLooper(), there are other constructors that allow you to pass the Looper - but these would also require you to create the Looper prior to creating your Handler.
I have an Activity, and in that I have a class.
text=new Dynamictext(...);
text.setText("txt");
in my DynamicText java I have this code:
public void setText(String text) {
this.text=text;
new asyncCreateText().execute();
//this.createText(text);
}
//private Handler handler = new Handler();
private class asyncCreateText extends AsyncTask<Void, Void, Void>
{
#Override
protected Void doInBackground(Void... unused) {
return null;
}
#Override
protected void onPostExecute(Void unused) {
}
}
I get:
ERROR/AndroidRuntime(5176): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
How can I handle this error?
ERROR/AndroidRuntime(5370): java.lang.ExceptionInInitializerError
ERROR/AndroidRuntime(5370): at com.l.start.DynamicText.setText(DynamicText.java:125)
ERROR/AndroidRuntime(5370): at com.l.start.OpenGLRenderer.initfonts(OpenGLRenderer.java:168)
ERROR/AndroidRuntime(5370): at com.l.start.OpenGLRenderer.init(OpenGLRenderer.java:119)
ERROR/AndroidRuntime(5370): at com.l.start.OpenGLRenderer.onSurfaceChanged(OpenGLRenderer.java:90)
ERROR/AndroidRuntime(5370): at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1120)
ERROR/AndroidRuntime(5370): at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:975)
ERROR/AndroidRuntime(5370): Caused by: java.lang.RuntimeException:
Can't create handler inside thread that has not called Looper.prepare()
ERROR/AndroidRuntime(5370): at android.os.Handler.<init>(Handler.java:121)
ERROR/AndroidRuntime(5370): at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
ERROR/AndroidRuntime(5370): at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
ERROR/AndroidRuntime(5370): at android.os.AsyncTask.<clinit>(AsyncTask.java:152)
ERROR/AndroidRuntime(5370): ... 6 more
The error is self-explanatory... doInBackground() runs on a background thread which, since it is not intended to loop, is not connected to a Looper.
You most likely don't want to directly instantiate a Handler at all... whatever data your doInBackground() implementation returns will be passed to onPostExecute() which runs on the UI thread.
mActivity = ThisActivity.this;
mActivity.runOnUiThread(new Runnable() {
public void run() {
new asyncCreateText().execute();
}
});
ADDED FOLLOWING THE STACKTRACE APPEARING IN QUESTION:
Looks like you're trying to start an AsyncTask from a GL rendering thread... don't do that cos they won't ever Looper.loop() either. AsyncTasks are really designed to be run from the UI thread only.
The least disruptive fix would probably be to call Activity.runOnUiThread() with a Runnable that kicks off your AsyncTask.
All the answers above are correct, but I think this is the easiest example possible:
public class ExampleActivity extends Activity {
private Handler handler;
private ProgressBar progress;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
progress = (ProgressBar) findViewById(R.id.progressBar1);
handler = new Handler();
}
public void clickAButton(View view) {
// Do something that takes a while
Runnable runnable = new Runnable() {
#Override
public void run() {
handler.post(new Runnable() { // This thread runs in the UI
#Override
public void run() {
progress.setProgress("anything"); // Update the UI
}
});
}
};
new Thread(runnable).start();
}
}
What this does is update a progress bar in the UI thread from a completely different thread passed through the post() method of the handler declared in the activity.
Hope it helps!
You create handler in background thread this way
private void createHandler() {
Thread thread = new Thread() {
public void run() {
Looper.prepare();
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Do Work
handler.removeCallbacks(this);
Looper.myLooper().quit();
}
}, 2000);
Looper.loop();
}
};
thread.start();
}
Activity.runOnUiThread() does not work for me. I worked around this issue by creating a regular thread this way:
public class PullTasksThread extends Thread {
public void run () {
Log.d(Prefs.TAG, "Thread run...");
}
}
and calling it from the GL update this way:
new PullTasksThread().start();
Try running you asyntask from the UI thread. I faced this issue when I wasn't doing the same!
Try this
Handler mHandler = new Handler(Looper.getMainLooper());
mHandler.post(new Runnable() {
#Override
public void run() {
//your code here that talks with the UI level widgets/ try to access the UI
//elements from this block because this piece of snippet will run in the UI/MainThread.
}
});
For all the Kotlin lovers, here is the code for you
Handler(Looper.getMainLooper()).post {
// any UI update here
}