I created a Looper thread class:
public class MyLooperThread extends Thread{
private Handler mHandler;
public void init(){
start(); //start the thread
synchronized (this) {
wait(5000); //wait for run()
}
Log.d("DEBUG","Init Done!");
//EXCEPTION: Can't create handler inside thread that has not called Looper.prepare()
MyObject obj = new MyObject(mHandler);
}
#Override
public void run() {
Looper.prepare();
mHandler = new Handler(){
#Override
public void handleMessage(Message msg){
//Check installed app package names, NOTHING RELATED WITH UI ...
}
};
synchronized (this) {
notify();
}
Looper.loop();
}//end of run()
}
In my Activity, I call above MyLooperThread 's init() method in onCreate(). Besides, I have a ToggleButton element, when ToggleButton is checked, I call MyLooperThread's init() method too.
public class MyActivity extends Activity implements OnCheckedChangeListener{
…
#Override
protected void onCreate(Bundle savedInstanceState){
…
myToggleButton.setOnCheckedChangeListener(this);
myToggleButton.setChecked(true);//checked by default
MyLooperThread myLooper = new MyLooperThread();
myLooper.init();
}
#Override
public void onCheckedChanged(CompoundButton button, boolean isChecked) {
if(isChecked){
MyLooperThread myLooper = new MyLooperThread();
myLooper.init();
}else{
...
}
}
}
When launch my app, it is fine. My toggle button is shown as checked by default. When I uncheck it & check it again, I got exception:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
which is pointed to the init() method's last line of code MyObject obj = new MyObject(mHandler);
Why I got this exception? I don't understand, my mHandler is created after I called Looper.prepare() in run().
Pretty sure the error is saying you're trying to do something UI related when not on the UI thread.
As you haven't posted anything that you're doing inside the handleMessage, I'm gonna assume that you're trying to change one of the UI elements. Always use the UI thread to update the UI. It should go something like this:
handleMessage(Message msg) {
...
getActivity().runOnUiThread(new Runnable {
...
// update UI here
...
});
...
}
just use a HandlerThread():
ht = new HandlerThread();
ht.start();
h = new Handler(ht.getLooper());
Related
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
}
});
}
I can't stop this thread when I exit my activity or application.
public class MyThread extends Thread {
public Handler handler;
#Override
public void try{
Looper.prepare();
handler = new Handler();
Looper.loop();
}
}
...
myThread = new MyThread();
myThread.start();
final Runnable runnable = new Runnable() {
#Override
public void run(){
doSomething();
myThread.handler.postDelayed(this,30*1000);
}
};
myThread.handler.post(runnable);
#Override
public void onStop(){
myThread.handler.removeCallbacksAndMessages(null);
myThread.handler.getLooper().quit();
myThread = null;
}
I can confirm that all the onStop() code is run, but the logcat still shows the thread running after I exit the application.
I think even if I remove the battery and smash the device with a sledgehammer it will still keep running, I've tried everything. :~) I must be missing something about handlers, loopers, and threads. Please help.
add a boolean flag in the Activity, say "shouldThreadRun", set to true in onResume(), set to false in onPause()
In run() of the Thread, check whether the Activity is still running
if(shouldThreadRun){
doSomething();
myThread.handler.postDelayed(this,30*1000);
}
I am learning how to use Looper and Handler class in android development
http://developer.android.com/reference/android/os/Looper.html
The example given in the android development is not clear to understand what is the usage and the how to use it.I dont know how to add Handler inside the Looper and how I can call the Looper to loop.
If it is available, can anyone give me a simple example to use it.
public class LooperTest extends Activity{
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
private class LooperTesting extends Thread
{
public Handler handler;
public void run()
{
Looper.prepare();
handler = new Handler()
{
public void handlerMessage(Message msg)
{
// do something
}
};
Looper.loop();
}
}
}
Hope this Links are helps to u
Link1
Link2
Link3
In your example you have only defined a Thread with a Looper. You need to start the Thread with the associated Looper before you can post any messages to it. I've added some code to your example to illustrate what has to be done:
public class LooperTest extends Activity{
LooperTesting mBgThread;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mBgThread = new mBgThread();
// Start the thread. When started, it will wait for incoming messages.
// Use the post* or send* methods of your handler-reference.
mBgThread.start();
}
public void onDestroy() {
// Don't forget to quit the Looper, so that the
// thread can finish.
mBgThread.handler.getLooper().quit();
}
private class LooperTesting extends Thread
{
public Handler handler;
public void run()
{
Looper.prepare();
handler = new Handler()
{
public void handlerMessage(Message msg)
{
// do something
}
};
Looper.loop();
}
}
}
I have the following:-
public class resApp extends MapActivity implements Runnable {
public void run() {
searchImage.setVisibility(View.GONE);
}
}
I also have a background thread that runs before this but that seems to run ok.
When i run the app the run() never gets called.
Can you help?
This code did work about 6 months ago but the device was 2.1.
Thanks
Chris
edit
I had already implemented
private Handler handler;
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (msg.toString().equalsIgnoreCase("1")) {
ad.dismiss();
} else {
pd.dismiss();
}
}
};
as an example and I already have an asynchronous task that runs in the back ground and in 2.1 I could have getters and setters in there. I have now had to pull these out and put them into the run() method as 2.2 doesn't like setting onclicklistener in an async task.
All I need to do is call the run() method on post execute but have tried everything:-
protected void onPostExecute(Object result) {
// Pass the result data back to the main activity
if (dialog != null) {
resApp.this.dialog.dismiss();
}
}
Could I just do:-
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
this.resApp.run();
}
};
You can call the run() method by using Handler.
Handler myHandler = new Handler();
resApp myObj;
And call it by using myHandler.post(myObj);
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
}