I have this code. I don't know why postDelay make UI frozen in this case. I want the Runnable will run after 100 miliseconds deley and run in 4000 miliseconds.
package com.delaythread;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
public class MyNeedActivity extends Activity implements OnClickListener {
private ProgressBar progressBar;
private final Handler handler = new Handler() {
#Override
public void handleMessage(final Message msg) {
super.handleMessage(msg);
progressBar.setVisibility(ProgressBar.INVISIBLE);
}
};
#Override
public void onClick(final View v) {
if(v.getId() == R.id.button1) {
/* This call doesn't make ProgressBar frozen.
final Thread t = new Thread(new MyRunnable());
t.start();
progressBar.setVisibility(ProgressBar.VISIBLE);
*/
// This makes ProgressBar frozen in 4000 miliseconds.
final boolean b = handler.postDelayed(new MyRunnable(), 100);
if(b) {
progressBar.setVisibility(ProgressBar.VISIBLE);
}
}
}
/** Called when the activity is first created. */
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
((Button)findViewById(R.id.button1)).setOnClickListener(this);
progressBar = (ProgressBar)findViewById(R.id.progressBar1);
}
private class MyRunnable implements Runnable {
#Override
public void run() {
sleep();
}
private void sleep() {
try {
Thread.sleep(4000);
handler.sendEmptyMessage(0);
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}
}
Update: Actually what I want is AsyncTask executes after a delay time, so I do as this answer Java/android how to start an AsyncTask after 3 seconds of delay?. He said I should use Handler and Runnable.
The following should suit your need according to the post
private final Handler handler = new Handler() {
#Override
public void handleMessage(final Message msg) {
super.handleMessage(msg);
//start Asyntask here. progress show/hide should be done in asynctaswk itself.
}
};
#Override
public void onClick(final View v) {
if(v.getId() == R.id.button1) {
final boolean b = handler.postDelayed(new MyRunnable() , 1000);
}
}
private class MyRunnable implements Runnable {
#Override
public void run() {
handler.sendmessage(0);
}
}
}
You probably want to run your MyRunnable on other thread than main UI one, so you need to start a regular thread for this, like
new Thread(new MyRunnable()).start();
instead of using Handler for this, which queues your runnable to be executed on main UI thread.
BTW, for this purpose Timer with TimerTask would suit better.
The Android Reference for the class Handler points out:
"[...] When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it [...]"
So your Handler, created on instantiation of the Activity, should be running on the UI thread causing it to block when your Runnable is executed.
Try creating a new Thread class in which your Handler is instantiated. Then pass the Runnable to it from your onClick() method. To pass messages back (such as updating the progress bar) you can use another Handler that is running on the UI thread.
You could also save yourself a lot of pain by taking a look at the AsyncTask class.
PS:
Delaying the execution could be done in the AsyncTaskdoInBackground() via a Thread.sleep(100) call. To delay execution on UI thread level you could do the same in AsyncTask.onPreExecute().
As far as I understand it you ask your MyRunnable to run on the GUI thread (of which there is only one); but the only this it does is sleep, effectively causing the GUI thread to freeze waiting for it.
You shouldn't do complicated calcultaions (or, sleep) in the GUI thread.
You may want to read the documentation on threads and the UI thread for a more elaborate description.
Your progress bar isn't updating because you aren't updating it! Try using an AsyncTask, (it runs on a different thread but allows you to update UI elements) and setting the state of the progress bar from within the onProgress method in the Async task.
OR
Just follow this example on the Android Progress Bar page
Try this:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Code to be executed after desired seconds
}
}, seconds*1000);
This would freeze the UI for given number of seconds and the execute the code inside the run()
Related
I'm using code that looks like this :
_thread = new Thread(){
#Override
public void run() {
try {
while (true) {
operate();
Thread.sleep(DELAY);
}
} catch (InterruptedException e) {
// Doesn't matters...
}
}
};
operate function looks like this :
// does things....
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
// adds an ImageView to the screen
}
});
// does other things...
At the bottom line, what i wanted to achieve is an operation that happens once in a while, without interrupting the main thread and the UI, something like a game-loop.
In the first 2 times that operate() runs, it adds the ImageView and everything is alright, but after 2 or 3 times it stops adding the ImageViews, but the UI is still running as usual. When i debugged the problem, i found out that after 3 times the run() method of the Runnable isn't called anymore, even thought the operate function was called.
The wired thing (for me) was that when i removed the Thread.sleep, everything worked fine (much faster of course...). I tried to replace it with a very long for loop (just for checking) and it worked, but of course it is not an appropriate solution to the problem.
I read about the problem, most of the people that asked this question did a thread.sleep or an infinite loop on the main thread, but, as i see it, i didn't do such thing. Many people wrote that you should replace the Thread.sleep with Handler.postDelayed. I tried to do it but it didn't work, maybe I did it wrong. I even tried replacing the runOnUiThread with other options I found on the internet, but all of them gave me the same exact results. I tried to replace the method that I'm adding the view to the activity, but all of them, again, gave the same result.
The waiting is crucial for this application. I got to find a way to wait sometime and then execute a function on the UI thread, cause this pattern returns at least a couple of times in my application.
It sounds like you want a post delay so that you can do the code on the UI thread after some delay. Handler Post Delay.
private static final int DELAY = 500;
private Handler mHandler;
private Runnable mRunnable;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start();
}
private void start()
{
mHandler = new Handler();
mRunnable = new MyRunnable(this);
mHandler.postDelayed(mRunnable, DELAY);
}
private void stop()
{
mHandler.removeCallbacks(mRunnable);
}
private void doSomething()
{
// Do your stuff here.
// Reschedule.
mHandler.postDelayed(mRunnable, DELAY);
}
Recommended way of creating a Runnable.
private static class MyRunnable implements Runnable
{
private WeakReference<MainActivity> mRef;
// In here you can pass any object that you need.
MyRunnable(MainActivity activity)
{
mRef = new WeakReference<MainActivity>(activity);
}
#Override
public void run()
{
// Safety check to avoid leaking.
MainActivity activity = mRef.get();
if(activity == null)
{
return;
}
// Do something here.
activity.doSomething();
}
}
There could be several reasons why the UI Runnable isn't being executed. Probably the activity variable has something messed up with it or it's referencing the context incorrectly, or as you said the Thread.sleep() could be causing an issue. At this point more parts of the code needs to viewed to better solve the problem.
A better way of implementing your logic is to use a scheduled Timer instead of using an infinite loop with a Thread.sleep() in it. It will execute the code within a background thread. And then use a Handler to update the UI instead of activity.runOnUiThread(). Here's an example:
// Global variable within the activity
private Handler handler;
// Activity's onCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
handler = new Handler(getMainLooper());
Timer timer = new Timer("ScheduledTask");
// Timer must be started on the Main UI thread as such.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
operate();
}
}, 0L, DELAY);
}
private void operate() {
// does things in background....
handler.post(new Runnable() {
#Override
public void run() {
// adds an ImageView to the screen from within the Main UI thread
}
});
// does other things in the background...
}
I would like to be able to run my specific method in background through a looper in an on click event, is this the right way to do this?
myThread = new LooperThread();
myThread.start();
upload.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
}
});
stop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myThread.handler.post(new Runnable() {
#Override
public void run() {
//my methods
}
});
}
});
And my Looper Class:
class LooperThread extends Thread {
Handler handler;
public void run() {
Looper.prepare();
handler = new Handler();
Looper.loop();
}
}
If it is,
the problem is that in this way, i don't understand why the system don't recognize "handler" while i typing: "myThread.handler.post.." and run the methods.
Otherwise, can you help me on making this?
I am sorry if i made mistakes while making the question, but it's my first time here :)``
Welcome to Stack Overflow.
I would like to be able to run my specific method in background through a looper in an on click event, is this the right way to do this?
Your code works, but I couldn't say it's the right way to do it. Like #tynn mentioned, a HandlerThread might be a better option.
If it is, the problem is that in this way, i don't understand why the system don't recognize "handler" while i typing: "myThread.handler.post.." and run the methods.
Otherwise, can you help me on making this?
If I understood your problem, it's an access issue. Your handler seems unaccessible since it's declared as package-private. You could make your handler visible this way:
// Should this class be public or package-private?
public class LooperThread extends Thread {
private Handler handler;
public Handler getHandler() {
return handler;
}
// ...
}
And you will be able to reference the handler like this:
myThread.getHandler().post(...);
UPDATE
You can delay a Runnable execution this way:
// Delay execution of a Runnable task by 5000 milliseconds.
myThread.getHandler().postDelayed(myDelayedRunnable, 5000);
The scenario is
I have two threads and a UI thread. The UI thread when clicked on login button creates a ClientThread which creates a socket and runs until the socket is connected, whenever a message is received i use a handler to post message to another thread called ProcessDataThread, now on receiving some messages from server i need to update UI related stuff from ProcessDataThread, I searched around alot and i found these two ways runonUiThread function which i guess can only be run from the Activity Class which is useless and the Asynctask method which i am not sure how to pass the activity context to...
Here is the code
The code executed when clicked on Login Button in the MainActivity
public void onLoginClick(View view)
{
global_constants.clientObject = new ClientThread();
global_constants.clientThread = new Thread(global_constants.clientObject);
global_constants.clientThread.start();
}
The code in ClientThread run method
public class ClientThread implements Runnable {
.......
#Override
public void run() {
......
while(!Thread.currentThread().isInterrupted() && (!CloseThread))
{
byte[] buff;
....
global_constants.updateConversationHandler.post(new ProcessDataThread(buff));
}
}
}
The method code in ProcessDataThread after parsing out the incoming data and stuff
public class ProcessDataThread implements Runnable {
.........
void ProcessLoginFailedPacket(byte[] buff)
{
// how to call the UI thread from here for updating some UI elements??
}
}
[EDIT]
i stored the activity context in a global variable and then did it this way, but i dont know whether it will be safer or not
((Activity)global_constants.MainContext).runOnUiThread(new Runnable(){
public void run()
{
TextView txtErr = (TextView) ((Activity)global_constants.MainContext).findViewById(R.id.errMsg);
txtErr.setVisibility(0);
txtErr.setText(reason);
}
});
You can post a runnable which does the UI operation to main thread as follows,
public class Utils {
public static void runOnUiThread(Runnable runnable){
final Handler UIHandler = new Handler(Looper.getMainLooper());
UIHandler .post(runnable);
}
}
Utils.runOnUiThread(new Runnable() {
#Override
public void run() {
// UI updation related code.
}
});
I am calling from a method:
myHandler.postDelayed(mMyRunnableHide, 6000);
which calls:
public Runnable mMyRunnableHide = new Runnable()
{
public void run()
{
mTextDisplay.setText("");
DisplayX();
}
};
if a button on screen is clicked I want to stop the runnable:
Button next = (Button) findViewById(R.id.Breaction);
next.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
myHandler.removeCallbacks(mMyRunnableHide);
mTextDisplay.setText("");
DisplayX();
}
});
}
the removecallbacks is not stopping the runnable. What am I doing wrong? Am I using the correct method? I just want the runnable to "Not Run" when the user clicks the button.
Thanks for any help.
It appears to me that removeCallbacks(..) only stops pending messages (Runnables). If your runnable has already started, then there's no stopping it (at least not this way).
Alternatively, you can extend the Runnable class and give it some kind of kill switch like this:
public class MyRunnable implements Runnable
{
private boolean killMe = false;
private void run()
{
if(killMe)
return;
/* do your work */
}
private void killRunnable()
{
killMe = true;
}
}
This will only prevent it from starting, but you could occasionally check killMe and bail out. If you are looping the runnable (like some kind of background thread) you can say:
while(!killMe) {
/* do work */
}
Hope this helps
EDIT I just wanted to post an update on this. Since this original post, Google has come up with a great class called AsyncTask that handles all of this stuff for you. Anyone reading this really should look into it because it is the correct way of doing things.
You can read about it here
Handler.removeCallback is synchronous and will work nicely provided:
You call postDelayed always in the main thread.
You call removeCallback always in the main thread
You don't call postDelayed again after having removed callbacks.
So in your case removeCallbacks is called from a button handler, which runs in the main thread. But you didn't show in your code the point from where you call postDelayed. If you call it from a background thread thats where your problem is.
If you are sure you don't call any of these methods from background threads, and the order of the calls is correct, then you might be leaving uncancelled tasks unadvertedly alive due to activity recreation on config changes (screen rotation, etc). Always make sure to call removeCallbacks again in the onDestroy method to prevent this kind of problems.
Here is another way to accomplish what mtmurdock is describing. This class will allow editing of instance variables in any class that your Runnable is defined as an anonymous inner class.
package support;
/**
* Runnable that can be stopped from executing
*/
public abstract class KillableRunnable implements Runnable{
private boolean isKilled=false;
/**
* Instead of Overriding run(), override this method to perform a Runnable operation.
* This will allow editing instance variables in the class that this Runnable is defined
*/
public abstract void doWork();
//The handler that posts this Runnable will call this method.
//By default, check if it has been killed. doWork() will now be the method
//override to implement this Runnable
#Override
final public void run(){
if(!isKilled){
doWork();
}
}
final public void kill(){
isKilled=true;
}
}
I don't think that removeCallbacks(..) only stops pending messages (Runnables) ,I think removeCallbacks(..) not working have other cause,but i don‘t know. because postDelayed(..) and removeCallbacks(..) is in the same thread
the following has worked for me. Place it in onResume.
mService= null;
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "OnServiceConnected");
ContadorFG.LocalBinder binder = (ContadorFG.LocalBinder) service;
mService = binder.getService();
connected = true;
synchronized (lock){
lock.notifyAll();
}
}
public void onResume() {
super.onResume();
loopDelayed();
}
private void loopDelayed(){
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (mService != null) {
----
----
----
return;
}else{
//auto call
loopDelayed();
}
}
}, 10);
}
Basically, I am trying to run a seconds counter and a levels counter. For every 10 seconds I want to ++level.
But that's not implemented as yet, so far I am just trying to get the seconds to display but I am getting runtime exceptions and a crash.
Googling I see that its because I am trying to update the UI from my thread and thats not allowed.
So I guess I am going to need asyncTask, but I have no idea how to do that with my simple little program. Please help or give me some alternatives...
package com.ryan1;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
public class main extends Activity {
int level = 1;
int seconds_running=0;
TextView the_seconds;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
the_seconds = (TextView) findViewById(R.id.textview_seconds);
Thread thread1 = new Thread(){
public void run(){
try {
sleep(1000); Log.d("RYAN", " RYAN ");
updated_secs();
} catch (Exception e) {
e.printStackTrace();
Log.d("RYAN", " "+e);
}
}
};
thread1.start();
}
public void updated_secs(){
seconds_running++;
the_seconds.setText(" "+seconds_running);
}
}
Create a Handler in your UI thread, then in the worker thread send a message to the handler (Handler.sendMessage(...)).
The message will be processed on your UI thread, so you can update the text widget correctly. Like this:
private Handler myUIHandler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
if (msg.what == some_id_you_created)
{
//Update UI here...
}
}
};
Then in your thread, to send a message to the handler you do this:
Message theMessage = myHandler.obtainMessage(some_id_you_created);
myUIHandler.sendMessage(theMessage);//Sends the message to the UI handler.
For this kind of thing, it is a waste to use another thread; it is just a waste and makes it so you have to dael with multithreading issues. Instead, just use Handler.sendDelayedMessage():
static final int MSG_DO_IT = 1;
static final long TIME_BETWEEN_MESSAGES = 10 * 1000; // 10 seconds
Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_IT: {
// Update your UI here...
Message newMsg = obtainMessage(MSG_DO_IT);
sendMessageDelayed(newMsg, TIME_BETWEEN_MESSAGES);
} break;
}
}
}
#Override
void onResume() {
super.onResume();
// Start the timer, executing first event immediately.
Message newMsg = mHandler.obtainMessage(MSG_DO_IT);
mHandler.sendMessage(newMsg);
}
#Override
void onPause() {
super.onPause();
// Stop the timer.
mHandler.removeMessages(MSG_DO_IT);
}
Note that this implementation will have some drift -- the actual time between messages is TIME_BETWEEN_MESSAGES + (time to dispatch message). If you need something that won't drift, you can do the timing yourself by using Hander.sendMessageAtTime() and incrementing the new time with a constant value each time, starting with an initial time you get with SystemClock.uptimeMillis() in onResume().
There is a great example of a Timer built using AsyncTask and a Handler with the postDelayed method.
You are correct that updating the UI from a background is a no-no.