I want to be able to call the following method after a specified delay.
In objective c there was something like:
[self performSelector:#selector(DoSomething) withObject:nil afterDelay:5];
Is there an equivalent of this method in android with java?
For example I need to be able to call a method after 5 seconds.
public void DoSomething()
{
//do something here
}
Kotlin
Handler(Looper.getMainLooper()).postDelayed({
//Do something after 100ms
}, 100)
Java
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
}
}, 100);
The class to import is android.os.handler.
I couldn't use any of the other answers in my case.
I used the native java Timer instead.
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// this code will be executed after 2 seconds
}
}, 2000);
Note: This answer was given when the question didn't specify Android as the context. For an answer specific to the Android UI thread look here.
It looks like the Mac OS API lets the current thread continue, and schedules the task to run asynchronously. In the Java, the equivalent function is provided by the java.util.concurrent package. I'm not sure what limitations Android might impose.
private static final ScheduledExecutorService worker =
Executors.newSingleThreadScheduledExecutor();
void someMethod() {
⋮
Runnable task = new Runnable() {
public void run() {
/* Do something… */
}
};
worker.schedule(task, 5, TimeUnit.SECONDS);
⋮
}
For executing something in the UI Thread after 5 seconds:
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
//Do something here
}
}, 5000);
Kotlin & Java Many Ways
1. Using Handler
Handler().postDelayed({
TODO("Do something")
}, 2000)
2. Using TimerTask
Timer().schedule(object : TimerTask() {
override fun run() {
TODO("Do something")
}
}, 2000)
Or even shorter
Timer().schedule(timerTask {
TODO("Do something")
}, 2000)
Or shortest would be
Timer().schedule(2000) {
TODO("Do something")
}
3. Using Executors
Executors.newSingleThreadScheduledExecutor().schedule({
TODO("Do something")
}, 2, TimeUnit.SECONDS)
In Java
1. Using Handler
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Do something
}
}, 2000);
2. Using Timer
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// Do something
}
}, 2000);
3. Using ScheduledExecutorService
private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
Runnable runnable = new Runnable() {
public void run() {
// Do something
}
};
worker.schedule(runnable, 2, TimeUnit.SECONDS);
you can use Handler inside UIThread:
runOnUiThread(new Runnable() {
#Override
public void run() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//add your code here
}
}, 1000);
}
});
Thanks for all the great answers, I found a solution that best suits my needs.
Handler myHandler = new DoSomething();
Message m = new Message();
m.obj = c;//passing a parameter here
myHandler.sendMessageDelayed(m, 1000);
class DoSomething extends Handler {
#Override
public void handleMessage(Message msg) {
MyObject o = (MyObject) msg.obj;
//do something here
}
}
See this demo:
import java.util.Timer;
import java.util.TimerTask;
class Test {
public static void main( String [] args ) {
int delay = 5000;// in ms
Timer timer = new Timer();
timer.schedule( new TimerTask(){
public void run() {
System.out.println("Wait, what..:");
}
}, delay);
System.out.println("Would it run?");
}
}
More Safety - With Kotlin Coroutine
Most of the answers use Handler but I give a different solution to delay in activity, fragment, view model with Android Lifecycle ext. This way will auto cancel when the lifecycle begins destroyed - avoid leaking the memory or crashed app
In Activity or Fragment:
lifecycleScope.launch {
delay(DELAY_MS)
doSomething()
}
In ViewModel:
viewModelScope.lanch {
delay(DELAY_MS)
doSomething()
}
In suspend function: (Kotlin Coroutine)
suspend fun doSomethingAfter(){
delay(DELAY_MS)
doSomething()
}
If you get an error with the lifecycleScope not found! - import this dependency to the app gradle file:
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
If you have to use the Handler, but you are into another thread, you can use runonuithread to run the handler in UI thread. This will save you from Exceptions thrown asking to call Looper.Prepare()
runOnUiThread(new Runnable() {
#Override
public void run() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 1 second
}
}, 1000);
}
});
Looks quite messy, but this is one of the way.
I prefer to use View.postDelayed() method, simple code below:
mView.postDelayed(new Runnable() {
#Override
public void run() {
// Do something after 1000 ms
}
}, 1000);
Here is my shortest solution:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
}
}, 100);
If you are using Android Studio 3.0 and above you can use lambda expressions. The method callMyMethod() is called after 2 seconds:
new Handler().postDelayed(() -> callMyMethod(), 2000);
In case you need to cancel the delayed runnable use this:
Handler handler = new Handler();
handler.postDelayed(() -> callMyMethod(), 2000);
// When you need to cancel all your posted runnables just use:
handler.removeCallbacksAndMessages(null);
final Handler handler = new Handler();
Timer t = new Timer();
t.schedule(new TimerTask() {
public void run() {
handler.post(new Runnable() {
public void run() {
//DO SOME ACTIONS HERE , THIS ACTIONS WILL WILL EXECUTE AFTER 5 SECONDS...
}
});
}
}, 5000);
I suggest the Timer, it allows you to schedule a method to be called on a very specific interval. This will not block your UI, and keep your app resonsive while the method is being executed.
The other option, is the wait(); method, this will block the current thread for the specified length of time. This will cause your UI to stop responding if you do this on the UI thread.
So there are a few things to consider here as there are so many ways to skin this cat. Although answers have all already been given selected and chosen. I think it's important that this gets revisited with proper coding guidelines to avoid anyone going the wrong direction just because of "majority selected simple answer".
So first let's discuss the simple Post Delayed answer that is the winner selected answer overall in this thread.
A couple of things to consider. After the post delay, you can encounter memory leaks, dead objects, life cycles that have gone away, and more. So handling it properly is important as well. You can do this in a couple of ways.
For sake of modern development, I'll supply in KOTLIN
Here is a simple example of using the UI thread on a callback and confirming that your activity is still alive and well when you hit your callback.
Handler(Looper.getMainLooper()).postDelayed({
if(activity != null && activity?.isFinishing == false){
txtNewInfo.visibility = View.GONE
}
}, NEW_INFO_SHOW_TIMEOUT_MS)
However, this is still not perfect as there is no reason to hit your callback if the activity has gone away. so a better way would be to keep a reference to it and remove it's callbacks like this.
private fun showFacebookStylePlus1NewsFeedOnPushReceived(){
A35Log.v(TAG, "showFacebookStylePlus1NewsFeedOnPushReceived")
if(activity != null && activity?.isFinishing == false){
txtNewInfo.visibility = View.VISIBLE
mHandler.postDelayed({
if(activity != null && activity?.isFinishing == false){
txtNewInfo.visibility = View.GONE
}
}, NEW_INFO_SHOW_TIMEOUT_MS)
}
}
and of course handle cleanup on the onPause so it doesn't hit the callback.
override fun onPause() {
super.onPause()
mHandler.removeCallbacks(null)
}
Now that we have talked through the obvious, let's talk about a cleaner option with modern day coroutines and kotlin :). If you aren't using these yet, you are really missing out.
fun doActionAfterDelay()
launch(UI) {
delay(MS_TO_DELAY)
actionToTake()
}
}
or if you want to always do a UI launch on that method you can simply do:
fun doActionAfterDelay() = launch(UI){
delay(MS_TO_DELAY)
actionToTake()
}
Of course just like the PostDelayed you have to make sure you handle canceling so you can either do the activity checks after the delay call or you can cancel it in the onPause just like the other route.
var mDelayedJob: Job? = null
fun doActionAfterDelay()
mDelayedJob = launch(UI) {
try {
delay(MS_TO_DELAY)
actionToTake()
}catch(ex: JobCancellationException){
showFancyToast("Delayed Job canceled", true, FancyToast.ERROR, "Delayed Job canceled: ${ex.message}")
}
}
}
}
//handle cleanup
override fun onPause() {
super.onPause()
if(mDelayedJob != null && mDelayedJob!!.isActive) {
A35Log.v(mClassTag, "canceling delayed job")
mDelayedJob?.cancel() //this should throw CancelationException in coroutine, you can catch and handle appropriately
}
}
If you put the launch(UI) into the method signature the job can be assigned in the calling line of code.
so moral of the story is to be safe with your delayed actions, make sure you remove your callbacks, or cancel your jobs and of course confirm you have the right life cycle to touch items on your delay callback complete. The Coroutines also offers cancelable actions.
Also worth noting that you should typically handle the various exceptions that can come with coroutines. For example, a cancelation, an exception, a timeout, whatever you decide to use. Here is a more advanced example if you decide to really start utilizing coroutines.
mLoadJob = launch(UI){
try {
//Applies timeout
withTimeout(4000) {
//Moves to background thread
withContext(DefaultDispatcher) {
mDeviceModelList.addArrayList(SSDBHelper.getAllDevices())
}
}
//Continues after async with context above
showFancyToast("Loading complete", true, FancyToast.SUCCESS)
}catch(ex: JobCancellationException){
showFancyToast("Save canceled", true, FancyToast.ERROR, "Save canceled: ${ex.message}")
}catch (ex: TimeoutCancellationException) {
showFancyToast("Timed out saving, please try again or press back", true, FancyToast.ERROR, "Timed out saving to database: ${ex.message}")
}catch(ex: Exception){
showFancyToast("Error saving to database, please try again or press back", true, FancyToast.ERROR, "Error saving to database: ${ex.message}")
}
}
For a Simple line Handle Post delay, you can do as following :
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Do someting
}
}, 3000);
I hope this helps
You can use this for Simplest Solution:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Write your code here
}
}, 5000); //Timer is in ms here.
Else, Below can be another clean useful solution:
new Handler().postDelayed(() ->
{/*Do something here*/},
5000); //time in ms
You can make it much cleaner by using the newly introduced lambda expressions:
new Handler().postDelayed(() -> {/*your code here*/}, time);
Using Kotlin, we can achieve by doing the following
Handler().postDelayed({
// do something after 1000ms
}, 1000)
If you use RxAndroid then thread and error handling becomes much easier. Following code executes after a delay
Observable.timer(delay, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> {
// Execute code here
}, Throwable::printStackTrace);
I created simpler method to call this.
public static void CallWithDelay(long miliseconds, final Activity activity, final String methodName)
{
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
try {
Method method = activity.getClass().getMethod(methodName);
method.invoke(activity);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}, miliseconds);
}
To use it, just call : .CallWithDelay(5000, this, "DoSomething");
Below one works when you get,
java.lang.RuntimeException: Can't create handler inside thread that
has not called Looper.prepare()
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
}
}, 100);
It's very easy using the CountDownTimer.
For more details https://developer.android.com/reference/android/os/CountDownTimer.html
import android.os.CountDownTimer;
// calls onTick every second, finishes after 3 seconds
new CountDownTimer(3000, 1000) {
public void onTick(long millisUntilFinished) {
Log.d("log", millisUntilFinished / 1000);
}
public void onFinish() {
// called after count down is finished
}
}.start();
I like things cleaner:
Here is my implementation, inline code to use inside your method
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
}
}, 100);
everybody seems to forget to clean the Handler before posting a new runnable or message on it. Otherway they could potentially accumulate and cause bad behaviour.
handler.removeMessages(int what);
// Remove any pending posts of messages with code 'what' that are in the message queue.
handler.removeCallbacks(Runnable r)
// Remove any pending posts of Runnable r that are in the message queue.
Here is another tricky way: it won't throw exception when the runnable change UI elements.
public class SimpleDelayAnimation extends Animation implements Animation.AnimationListener {
Runnable callBack;
public SimpleDelayAnimation(Runnable runnable, int delayTimeMilli) {
setDuration(delayTimeMilli);
callBack = runnable;
setAnimationListener(this);
}
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
callBack.run();
}
#Override
public void onAnimationRepeat(Animation animation) {
}
}
You can call the animation like this:
view.startAnimation(new SimpleDelayAnimation(delayRunnable, 500));
Animation can attach to any view.
Here is the answer in Kotlin you lazy, lazy people:
Handler().postDelayed({
//doSomethingHere()
}, 1000)
Kotlin
runOnUiThread from a Fragment
Timer
example:
Timer().schedule(500) {
activity?.runOnUiThread {
// code
}
}
A suitable solution in android:
private static long SLEEP_TIME = 2 // for 2 second
.
.
MyLauncher launcher = new MyLauncher();
launcher.start();
.
.
private class MyLauncher extends Thread {
#Override
/**
* Sleep for 2 seconds as you can also change SLEEP_TIME 2 to any.
*/
public void run() {
try {
// Sleeping
Thread.sleep(SLEEP_TIME * 1000);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
//do something you want to do
//And your code will be executed after 2 second
}
}
Related
I've built a simple music player in Android. The view for each song contains a SeekBar, implemented like this:
public class Song extends Activity implements OnClickListener,Runnable {
private SeekBar progress;
private MediaPlayer mp;
// ...
private ServiceConnection onService = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder rawBinder) {
appService = ((MPService.LocalBinder)rawBinder).getService(); // service that handles the MediaPlayer
progress.setVisibility(SeekBar.VISIBLE);
progress.setProgress(0);
mp = appService.getMP();
appService.playSong(title);
progress.setMax(mp.getDuration());
new Thread(Song.this).start();
}
public void onServiceDisconnected(ComponentName classname) {
appService = null;
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song);
// ...
progress = (SeekBar) findViewById(R.id.progress);
// ...
}
public void run() {
int pos = 0;
int total = mp.getDuration();
while (mp != null && pos<total) {
try {
Thread.sleep(1000);
pos = appService.getSongPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
progress.setProgress(pos);
}
}
This works fine. Now I want a timer counting the seconds/minutes of the progress of the song. So I put a TextView in the layout, get it with findViewById() in onCreate(), and put this in run() after progress.setProgress(pos):
String time = String.format("%d:%d",
TimeUnit.MILLISECONDS.toMinutes(pos),
TimeUnit.MILLISECONDS.toSeconds(pos),
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(
pos))
);
currentTime.setText(time); // currentTime = (TextView) findViewById(R.id.current_time);
But that last line gives me the exception:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Yet I'm doing basically the same thing here as I'm doing with the SeekBar - creating the view in onCreate, then touching it in run() - and it doesn't give me this complaint.
You have to move the portion of the background task that updates the UI onto the main thread. There is a simple piece of code for this:
runOnUiThread(new Runnable() {
#Override
public void run() {
// Stuff that updates the UI
}
});
Documentation for Activity.runOnUiThread.
Just nest this inside the method that is running in the background, and then copy paste the code that implements any updates in the middle of the block. Include only the smallest amount of code possible, otherwise you start to defeat the purpose of the background thread.
I solved this by putting runOnUiThread( new Runnable(){ .. inside run():
thread = new Thread(){
#Override
public void run() {
try {
synchronized (this) {
wait(5000);
runOnUiThread(new Runnable() {
#Override
public void run() {
dbloadingInfo.setVisibility(View.VISIBLE);
bar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Intent mainActivity = new Intent(getApplicationContext(),MainActivity.class);
startActivity(mainActivity);
};
};
thread.start();
My solution to this:
private void setText(final TextView text,final String value){
runOnUiThread(new Runnable() {
#Override
public void run() {
text.setText(value);
}
});
}
Call this method on a background thread.
Kotlin coroutines can make your code more concise and readable like this:
MainScope().launch {
withContext(Dispatchers.Default) {
//TODO("Background processing...")
}
TODO("Update UI here!")
}
Or vice versa:
GlobalScope.launch {
//TODO("Background processing...")
withContext(Dispatchers.Main) {
// TODO("Update UI here!")
}
TODO("Continue background processing...")
}
Usually, any action involving the user interface must be done in the main or UI thread, that is the one in which onCreate() and event handling are executed. One way to be sure of that is using runOnUiThread(), another is using Handlers.
ProgressBar.setProgress() has a mechanism for which it will always execute on the main thread, so that's why it worked.
See Painless Threading.
You can use Handler to Delete View without disturbing the main UI Thread.
Here is example code
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
//do stuff like remove view etc
adapter.remove(selecteditem);
}
});
Kotlin Answer
We have to use UI Thread for the job with true way. We can use UI Thread in Kotlin, such as:
runOnUiThread(Runnable {
//TODO: Your job is here..!
})
I've been in this situation, but I found a solution with the Handler Object.
In my case, I want to update a ProgressDialog with the observer pattern.
My view implements observer and overrides the update method.
So, my main thread create the view and another thread call the update method that update the ProgressDialop and....:
Only the original thread that created a view hierarchy can touch its
views.
It's possible to solve the problem with the Handler Object.
Below, different parts of my code:
public class ViewExecution extends Activity implements Observer{
static final int PROGRESS_DIALOG = 0;
ProgressDialog progressDialog;
int currentNumber;
public void onCreate(Bundle savedInstanceState) {
currentNumber = 0;
final Button launchPolicyButton = ((Button) this.findViewById(R.id.launchButton));
launchPolicyButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
showDialog(PROGRESS_DIALOG);
}
});
}
#Override
protected Dialog onCreateDialog(int id) {
switch(id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Loading");
progressDialog.setCancelable(true);
return progressDialog;
default:
return null;
}
}
#Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch(id) {
case PROGRESS_DIALOG:
progressDialog.setProgress(0);
}
}
// Define the Handler that receives messages from the thread and update the progress
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
int current = msg.arg1;
progressDialog.setProgress(current);
if (current >= 100){
removeDialog (PROGRESS_DIALOG);
}
}
};
// The method called by the observer (the second thread)
#Override
public void update(Observable obs, Object arg1) {
Message msg = handler.obtainMessage();
msg.arg1 = ++currentPluginNumber;
handler.sendMessage(msg);
}
}
This explanation can be found on this page, and you must read the "Example ProgressDialog with a second thread".
I see that you have accepted #providence's answer. Just in case, you can also use the handler too! First, do the int fields.
private static final int SHOW_LOG = 1;
private static final int HIDE_LOG = 0;
Next, make a handler instance as a field.
//TODO __________[ Handler ]__________
#SuppressLint("HandlerLeak")
protected Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
// Put code here...
// Set a switch statement to toggle it on or off.
switch(msg.what)
{
case SHOW_LOG:
{
ads.setVisibility(View.VISIBLE);
break;
}
case HIDE_LOG:
{
ads.setVisibility(View.GONE);
break;
}
}
}
};
Make a method.
//TODO __________[ Callbacks ]__________
#Override
public void showHandler(boolean show)
{
handler.sendEmptyMessage(show ? SHOW_LOG : HIDE_LOG);
}
Finally, put this at onCreate() method.
showHandler(true);
Use this code, and no need to runOnUiThread function:
private Handler handler;
private Runnable handlerTask;
void StartTimer(){
handler = new Handler();
handlerTask = new Runnable()
{
#Override
public void run() {
// do something
textView.setText("some text");
handler.postDelayed(handlerTask, 1000);
}
};
handlerTask.run();
}
I had a similar issue, and my solution is ugly, but it works:
void showCode() {
hideRegisterMessage(); // Hides view
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
showRegisterMessage(); // Shows view
}
}, 3000); // After 3 seconds
}
I was facing a similar problem and none of the methods mentioned above worked for me. In the end, this did the trick for me:
Device.BeginInvokeOnMainThread(() =>
{
myMethod();
});
I found this gem here.
I use Handler with Looper.getMainLooper(). It worked fine for me.
Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
// Any UI task, example
textView.setText("your text");
}
};
handler.sendEmptyMessage(1);
This is explicitly throwing an error. It says whichever thread created a view, only that can touch its views. It is because the created view is inside that thread's space. The view creation (GUI) happens in the UI (main) thread. So, you always use the UI thread to access those methods.
In the above picture, the progress variable is inside the space of the UI thread. So, only the UI thread can access this variable. Here, you're accessing progress via new Thread(), and that's why you got an error.
For a one-liner version of the runOnUiThread() approach, you can use a lambda function, i.e.:
runOnUiThread(() -> doStuff(Object, myValue));
where doStuff() can represents some method used to modify the value of some UI Object (setting text, changing colors, etc.).
I find this to be much neater when trying to update several UI objects without the need for a 6 line Runnable definition at each as mentioned in the most upvoted answer, which is by no means incorrect, it just takes up a lot more space and I find to be less readable.
So this:
runOnUiThread(new Runnable() {
#Override
public void run() {
doStuff(myTextView, "myNewText");
}
});
can become this:
runOnUiThread(() -> doStuff(myTextView, "myNewText"));
where the definition of doStuff lies elsewhere.
Or if you don't need to be so generalizable, and just need to set the text of a TextView object:
runOnUiThread(() -> myTextView.setText("myNewText"));
For anyone using fragment:
(context as Activity).runOnUiThread {
//TODO
}
This happened to my when I called for an UI change from a doInBackground from Asynctask instead of using onPostExecute.
Dealing with the UI in onPostExecute solved my problem.
I was working with a class that did not contain a reference to the context. So it was not possible for me to use runOnUIThread(); I used view.post(); and it was solved.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
final int currentPosition = mediaPlayer.getCurrentPosition();
audioMessage.seekBar.setProgress(currentPosition / 1000);
audioMessage.tvPlayDuration.post(new Runnable() {
#Override
public void run() {
audioMessage.tvPlayDuration.setText(ChatDateTimeFormatter.getDuration(currentPosition));
}
});
}
}, 0, 1000);
When using AsyncTask Update the UI in onPostExecute method
#Override
protected void onPostExecute(String s) {
// Update UI here
}
This is the stack trace of mentioned exception
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6149)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:843)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
at android.view.View.requestLayout(View.java:16474)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
at android.view.View.setFlags(View.java:8938)
at android.view.View.setVisibility(View.java:6066)
So if you go and dig then you come to know
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
Where mThread is initialize in constructor like below
mThread = Thread.currentThread();
All I mean to say that when we created particular view we created it on UI Thread and later try to modifying in a Worker Thread.
We can verify it via below code snippet
Thread.currentThread().getName()
when we inflate layout and later where you are getting exception.
If you do not want to use runOnUiThread API, you can in fact implement AsynTask for the operations that takes some seconds to complete. But in that case, also after processing your work in doinBackground(), you need to return the finished view in onPostExecute(). The Android implementation allows only main UI thread to interact with views.
If you simply want to invalidate (call repaint/redraw function) from your non UI Thread, use postInvalidate()
myView.postInvalidate();
This will post an invalidate request on the UI-thread.
For more information : what-does-postinvalidate-do
Well, You can do it like this.
https://developer.android.com/reference/android/view/View#post(java.lang.Runnable)
A simple approach
currentTime.post(new Runnable(){
#Override
public void run() {
currentTime.setText(time);
}
}
it also provides delay
https://developer.android.com/reference/android/view/View#postDelayed(java.lang.Runnable,%20long)
For me the issue was that I was calling onProgressUpdate() explicitly from my code. This shouldn't be done. I called publishProgress() instead and that resolved the error.
In my case,
I have EditText in Adaptor, and it's already in the UI thread. However, when this Activity loads, it's crashes with this error.
My solution is I need to remove <requestFocus /> out from EditText in XML.
For the people struggling in Kotlin, it works like this:
lateinit var runnable: Runnable //global variable
runOnUiThread { //Lambda
runnable = Runnable {
//do something here
runDelayedHandler(5000)
}
}
runnable.run()
//you need to keep the handler outside the runnable body to work in kotlin
fun runDelayedHandler(timeToWait: Long) {
//Keep it running
val handler = Handler()
handler.postDelayed(runnable, timeToWait)
}
If you couldn't find a UIThread you can use this way .
yourcurrentcontext mean, you need to parse Current Context
new Thread(new Runnable() {
public void run() {
while (true) {
(Activity) yourcurrentcontext).runOnUiThread(new Runnable() {
public void run() {
Log.d("Thread Log","I am from UI Thread");
}
});
try {
Thread.sleep(1000);
} catch (Exception ex) {
}
}
}
}).start();
In Kotlin simply put your code in runOnUiThread activity method
runOnUiThread{
// write your code here, for example
val task = Runnable {
Handler().postDelayed({
var smzHtcList = mDb?.smzHtcReferralDao()?.getAll()
tv_showSmzHtcList.text = smzHtcList.toString()
}, 10)
}
mDbWorkerThread.postTask(task)
}
If you are within a fragment, then you also need to get the activity object as runOnUIThread is a method on the activity.
An example in Kotlin with some surrounding context to make it clearer - this example is navigating from a camera fragment to a gallery fragment:
// Setup image capture listener which is triggered after photo has been taken
imageCapture.takePicture(
outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
Log.d(TAG, "Photo capture succeeded: $savedUri")
//Do whatever work you do when image is saved
//Now ask navigator to move to new tab - as this
//updates UI do on the UI thread
activity?.runOnUiThread( {
Navigation.findNavController(
requireActivity(), R.id.fragment_container
).navigate(CameraFragmentDirections
.actionCameraToGallery(outputDirectory.absolutePath))
})
Solved : Just put this method in doInBackround Class... and pass the message
public void setProgressText(final String progressText){
Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
// Any UI task, example
progressDialog.setMessage(progressText);
}
};
handler.sendEmptyMessage(1);
}
I want to be able to call the following method after a specified delay.
In objective c there was something like:
[self performSelector:#selector(DoSomething) withObject:nil afterDelay:5];
Is there an equivalent of this method in android with java?
For example I need to be able to call a method after 5 seconds.
public void DoSomething()
{
//do something here
}
Kotlin
Handler(Looper.getMainLooper()).postDelayed({
//Do something after 100ms
}, 100)
Java
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
}
}, 100);
The class to import is android.os.handler.
I couldn't use any of the other answers in my case.
I used the native java Timer instead.
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// this code will be executed after 2 seconds
}
}, 2000);
Note: This answer was given when the question didn't specify Android as the context. For an answer specific to the Android UI thread look here.
It looks like the Mac OS API lets the current thread continue, and schedules the task to run asynchronously. In the Java, the equivalent function is provided by the java.util.concurrent package. I'm not sure what limitations Android might impose.
private static final ScheduledExecutorService worker =
Executors.newSingleThreadScheduledExecutor();
void someMethod() {
⋮
Runnable task = new Runnable() {
public void run() {
/* Do something… */
}
};
worker.schedule(task, 5, TimeUnit.SECONDS);
⋮
}
For executing something in the UI Thread after 5 seconds:
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
//Do something here
}
}, 5000);
Kotlin & Java Many Ways
1. Using Handler
Handler().postDelayed({
TODO("Do something")
}, 2000)
2. Using TimerTask
Timer().schedule(object : TimerTask() {
override fun run() {
TODO("Do something")
}
}, 2000)
Or even shorter
Timer().schedule(timerTask {
TODO("Do something")
}, 2000)
Or shortest would be
Timer().schedule(2000) {
TODO("Do something")
}
3. Using Executors
Executors.newSingleThreadScheduledExecutor().schedule({
TODO("Do something")
}, 2, TimeUnit.SECONDS)
In Java
1. Using Handler
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Do something
}
}, 2000);
2. Using Timer
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// Do something
}
}, 2000);
3. Using ScheduledExecutorService
private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
Runnable runnable = new Runnable() {
public void run() {
// Do something
}
};
worker.schedule(runnable, 2, TimeUnit.SECONDS);
you can use Handler inside UIThread:
runOnUiThread(new Runnable() {
#Override
public void run() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//add your code here
}
}, 1000);
}
});
Thanks for all the great answers, I found a solution that best suits my needs.
Handler myHandler = new DoSomething();
Message m = new Message();
m.obj = c;//passing a parameter here
myHandler.sendMessageDelayed(m, 1000);
class DoSomething extends Handler {
#Override
public void handleMessage(Message msg) {
MyObject o = (MyObject) msg.obj;
//do something here
}
}
See this demo:
import java.util.Timer;
import java.util.TimerTask;
class Test {
public static void main( String [] args ) {
int delay = 5000;// in ms
Timer timer = new Timer();
timer.schedule( new TimerTask(){
public void run() {
System.out.println("Wait, what..:");
}
}, delay);
System.out.println("Would it run?");
}
}
More Safety - With Kotlin Coroutine
Most of the answers use Handler but I give a different solution to delay in activity, fragment, view model with Android Lifecycle ext. This way will auto cancel when the lifecycle begins destroyed - avoid leaking the memory or crashed app
In Activity or Fragment:
lifecycleScope.launch {
delay(DELAY_MS)
doSomething()
}
In ViewModel:
viewModelScope.lanch {
delay(DELAY_MS)
doSomething()
}
In suspend function: (Kotlin Coroutine)
suspend fun doSomethingAfter(){
delay(DELAY_MS)
doSomething()
}
If you get an error with the lifecycleScope not found! - import this dependency to the app gradle file:
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
If you have to use the Handler, but you are into another thread, you can use runonuithread to run the handler in UI thread. This will save you from Exceptions thrown asking to call Looper.Prepare()
runOnUiThread(new Runnable() {
#Override
public void run() {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 1 second
}
}, 1000);
}
});
Looks quite messy, but this is one of the way.
I prefer to use View.postDelayed() method, simple code below:
mView.postDelayed(new Runnable() {
#Override
public void run() {
// Do something after 1000 ms
}
}, 1000);
Here is my shortest solution:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
}
}, 100);
If you are using Android Studio 3.0 and above you can use lambda expressions. The method callMyMethod() is called after 2 seconds:
new Handler().postDelayed(() -> callMyMethod(), 2000);
In case you need to cancel the delayed runnable use this:
Handler handler = new Handler();
handler.postDelayed(() -> callMyMethod(), 2000);
// When you need to cancel all your posted runnables just use:
handler.removeCallbacksAndMessages(null);
final Handler handler = new Handler();
Timer t = new Timer();
t.schedule(new TimerTask() {
public void run() {
handler.post(new Runnable() {
public void run() {
//DO SOME ACTIONS HERE , THIS ACTIONS WILL WILL EXECUTE AFTER 5 SECONDS...
}
});
}
}, 5000);
I suggest the Timer, it allows you to schedule a method to be called on a very specific interval. This will not block your UI, and keep your app resonsive while the method is being executed.
The other option, is the wait(); method, this will block the current thread for the specified length of time. This will cause your UI to stop responding if you do this on the UI thread.
So there are a few things to consider here as there are so many ways to skin this cat. Although answers have all already been given selected and chosen. I think it's important that this gets revisited with proper coding guidelines to avoid anyone going the wrong direction just because of "majority selected simple answer".
So first let's discuss the simple Post Delayed answer that is the winner selected answer overall in this thread.
A couple of things to consider. After the post delay, you can encounter memory leaks, dead objects, life cycles that have gone away, and more. So handling it properly is important as well. You can do this in a couple of ways.
For sake of modern development, I'll supply in KOTLIN
Here is a simple example of using the UI thread on a callback and confirming that your activity is still alive and well when you hit your callback.
Handler(Looper.getMainLooper()).postDelayed({
if(activity != null && activity?.isFinishing == false){
txtNewInfo.visibility = View.GONE
}
}, NEW_INFO_SHOW_TIMEOUT_MS)
However, this is still not perfect as there is no reason to hit your callback if the activity has gone away. so a better way would be to keep a reference to it and remove it's callbacks like this.
private fun showFacebookStylePlus1NewsFeedOnPushReceived(){
A35Log.v(TAG, "showFacebookStylePlus1NewsFeedOnPushReceived")
if(activity != null && activity?.isFinishing == false){
txtNewInfo.visibility = View.VISIBLE
mHandler.postDelayed({
if(activity != null && activity?.isFinishing == false){
txtNewInfo.visibility = View.GONE
}
}, NEW_INFO_SHOW_TIMEOUT_MS)
}
}
and of course handle cleanup on the onPause so it doesn't hit the callback.
override fun onPause() {
super.onPause()
mHandler.removeCallbacks(null)
}
Now that we have talked through the obvious, let's talk about a cleaner option with modern day coroutines and kotlin :). If you aren't using these yet, you are really missing out.
fun doActionAfterDelay()
launch(UI) {
delay(MS_TO_DELAY)
actionToTake()
}
}
or if you want to always do a UI launch on that method you can simply do:
fun doActionAfterDelay() = launch(UI){
delay(MS_TO_DELAY)
actionToTake()
}
Of course just like the PostDelayed you have to make sure you handle canceling so you can either do the activity checks after the delay call or you can cancel it in the onPause just like the other route.
var mDelayedJob: Job? = null
fun doActionAfterDelay()
mDelayedJob = launch(UI) {
try {
delay(MS_TO_DELAY)
actionToTake()
}catch(ex: JobCancellationException){
showFancyToast("Delayed Job canceled", true, FancyToast.ERROR, "Delayed Job canceled: ${ex.message}")
}
}
}
}
//handle cleanup
override fun onPause() {
super.onPause()
if(mDelayedJob != null && mDelayedJob!!.isActive) {
A35Log.v(mClassTag, "canceling delayed job")
mDelayedJob?.cancel() //this should throw CancelationException in coroutine, you can catch and handle appropriately
}
}
If you put the launch(UI) into the method signature the job can be assigned in the calling line of code.
so moral of the story is to be safe with your delayed actions, make sure you remove your callbacks, or cancel your jobs and of course confirm you have the right life cycle to touch items on your delay callback complete. The Coroutines also offers cancelable actions.
Also worth noting that you should typically handle the various exceptions that can come with coroutines. For example, a cancelation, an exception, a timeout, whatever you decide to use. Here is a more advanced example if you decide to really start utilizing coroutines.
mLoadJob = launch(UI){
try {
//Applies timeout
withTimeout(4000) {
//Moves to background thread
withContext(DefaultDispatcher) {
mDeviceModelList.addArrayList(SSDBHelper.getAllDevices())
}
}
//Continues after async with context above
showFancyToast("Loading complete", true, FancyToast.SUCCESS)
}catch(ex: JobCancellationException){
showFancyToast("Save canceled", true, FancyToast.ERROR, "Save canceled: ${ex.message}")
}catch (ex: TimeoutCancellationException) {
showFancyToast("Timed out saving, please try again or press back", true, FancyToast.ERROR, "Timed out saving to database: ${ex.message}")
}catch(ex: Exception){
showFancyToast("Error saving to database, please try again or press back", true, FancyToast.ERROR, "Error saving to database: ${ex.message}")
}
}
For a Simple line Handle Post delay, you can do as following :
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Do someting
}
}, 3000);
I hope this helps
You can use this for Simplest Solution:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Write your code here
}
}, 5000); //Timer is in ms here.
Else, Below can be another clean useful solution:
new Handler().postDelayed(() ->
{/*Do something here*/},
5000); //time in ms
You can make it much cleaner by using the newly introduced lambda expressions:
new Handler().postDelayed(() -> {/*your code here*/}, time);
Using Kotlin, we can achieve by doing the following
Handler().postDelayed({
// do something after 1000ms
}, 1000)
If you use RxAndroid then thread and error handling becomes much easier. Following code executes after a delay
Observable.timer(delay, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> {
// Execute code here
}, Throwable::printStackTrace);
I created simpler method to call this.
public static void CallWithDelay(long miliseconds, final Activity activity, final String methodName)
{
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
try {
Method method = activity.getClass().getMethod(methodName);
method.invoke(activity);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}, miliseconds);
}
To use it, just call : .CallWithDelay(5000, this, "DoSomething");
Below one works when you get,
java.lang.RuntimeException: Can't create handler inside thread that
has not called Looper.prepare()
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
}
}, 100);
It's very easy using the CountDownTimer.
For more details https://developer.android.com/reference/android/os/CountDownTimer.html
import android.os.CountDownTimer;
// calls onTick every second, finishes after 3 seconds
new CountDownTimer(3000, 1000) {
public void onTick(long millisUntilFinished) {
Log.d("log", millisUntilFinished / 1000);
}
public void onFinish() {
// called after count down is finished
}
}.start();
I like things cleaner:
Here is my implementation, inline code to use inside your method
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
}
}, 100);
everybody seems to forget to clean the Handler before posting a new runnable or message on it. Otherway they could potentially accumulate and cause bad behaviour.
handler.removeMessages(int what);
// Remove any pending posts of messages with code 'what' that are in the message queue.
handler.removeCallbacks(Runnable r)
// Remove any pending posts of Runnable r that are in the message queue.
Here is another tricky way: it won't throw exception when the runnable change UI elements.
public class SimpleDelayAnimation extends Animation implements Animation.AnimationListener {
Runnable callBack;
public SimpleDelayAnimation(Runnable runnable, int delayTimeMilli) {
setDuration(delayTimeMilli);
callBack = runnable;
setAnimationListener(this);
}
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
callBack.run();
}
#Override
public void onAnimationRepeat(Animation animation) {
}
}
You can call the animation like this:
view.startAnimation(new SimpleDelayAnimation(delayRunnable, 500));
Animation can attach to any view.
Here is the answer in Kotlin you lazy, lazy people:
Handler().postDelayed({
//doSomethingHere()
}, 1000)
Kotlin
runOnUiThread from a Fragment
Timer
example:
Timer().schedule(500) {
activity?.runOnUiThread {
// code
}
}
A suitable solution in android:
private static long SLEEP_TIME = 2 // for 2 second
.
.
MyLauncher launcher = new MyLauncher();
launcher.start();
.
.
private class MyLauncher extends Thread {
#Override
/**
* Sleep for 2 seconds as you can also change SLEEP_TIME 2 to any.
*/
public void run() {
try {
// Sleeping
Thread.sleep(SLEEP_TIME * 1000);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
//do something you want to do
//And your code will be executed after 2 second
}
}
I've built a simple music player in Android. The view for each song contains a SeekBar, implemented like this:
public class Song extends Activity implements OnClickListener,Runnable {
private SeekBar progress;
private MediaPlayer mp;
// ...
private ServiceConnection onService = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder rawBinder) {
appService = ((MPService.LocalBinder)rawBinder).getService(); // service that handles the MediaPlayer
progress.setVisibility(SeekBar.VISIBLE);
progress.setProgress(0);
mp = appService.getMP();
appService.playSong(title);
progress.setMax(mp.getDuration());
new Thread(Song.this).start();
}
public void onServiceDisconnected(ComponentName classname) {
appService = null;
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song);
// ...
progress = (SeekBar) findViewById(R.id.progress);
// ...
}
public void run() {
int pos = 0;
int total = mp.getDuration();
while (mp != null && pos<total) {
try {
Thread.sleep(1000);
pos = appService.getSongPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
progress.setProgress(pos);
}
}
This works fine. Now I want a timer counting the seconds/minutes of the progress of the song. So I put a TextView in the layout, get it with findViewById() in onCreate(), and put this in run() after progress.setProgress(pos):
String time = String.format("%d:%d",
TimeUnit.MILLISECONDS.toMinutes(pos),
TimeUnit.MILLISECONDS.toSeconds(pos),
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(
pos))
);
currentTime.setText(time); // currentTime = (TextView) findViewById(R.id.current_time);
But that last line gives me the exception:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Yet I'm doing basically the same thing here as I'm doing with the SeekBar - creating the view in onCreate, then touching it in run() - and it doesn't give me this complaint.
You have to move the portion of the background task that updates the UI onto the main thread. There is a simple piece of code for this:
runOnUiThread(new Runnable() {
#Override
public void run() {
// Stuff that updates the UI
}
});
Documentation for Activity.runOnUiThread.
Just nest this inside the method that is running in the background, and then copy paste the code that implements any updates in the middle of the block. Include only the smallest amount of code possible, otherwise you start to defeat the purpose of the background thread.
I solved this by putting runOnUiThread( new Runnable(){ .. inside run():
thread = new Thread(){
#Override
public void run() {
try {
synchronized (this) {
wait(5000);
runOnUiThread(new Runnable() {
#Override
public void run() {
dbloadingInfo.setVisibility(View.VISIBLE);
bar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Intent mainActivity = new Intent(getApplicationContext(),MainActivity.class);
startActivity(mainActivity);
};
};
thread.start();
My solution to this:
private void setText(final TextView text,final String value){
runOnUiThread(new Runnable() {
#Override
public void run() {
text.setText(value);
}
});
}
Call this method on a background thread.
Kotlin coroutines can make your code more concise and readable like this:
MainScope().launch {
withContext(Dispatchers.Default) {
//TODO("Background processing...")
}
TODO("Update UI here!")
}
Or vice versa:
GlobalScope.launch {
//TODO("Background processing...")
withContext(Dispatchers.Main) {
// TODO("Update UI here!")
}
TODO("Continue background processing...")
}
Usually, any action involving the user interface must be done in the main or UI thread, that is the one in which onCreate() and event handling are executed. One way to be sure of that is using runOnUiThread(), another is using Handlers.
ProgressBar.setProgress() has a mechanism for which it will always execute on the main thread, so that's why it worked.
See Painless Threading.
You can use Handler to Delete View without disturbing the main UI Thread.
Here is example code
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
//do stuff like remove view etc
adapter.remove(selecteditem);
}
});
Kotlin Answer
We have to use UI Thread for the job with true way. We can use UI Thread in Kotlin, such as:
runOnUiThread(Runnable {
//TODO: Your job is here..!
})
I've been in this situation, but I found a solution with the Handler Object.
In my case, I want to update a ProgressDialog with the observer pattern.
My view implements observer and overrides the update method.
So, my main thread create the view and another thread call the update method that update the ProgressDialop and....:
Only the original thread that created a view hierarchy can touch its
views.
It's possible to solve the problem with the Handler Object.
Below, different parts of my code:
public class ViewExecution extends Activity implements Observer{
static final int PROGRESS_DIALOG = 0;
ProgressDialog progressDialog;
int currentNumber;
public void onCreate(Bundle savedInstanceState) {
currentNumber = 0;
final Button launchPolicyButton = ((Button) this.findViewById(R.id.launchButton));
launchPolicyButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
showDialog(PROGRESS_DIALOG);
}
});
}
#Override
protected Dialog onCreateDialog(int id) {
switch(id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Loading");
progressDialog.setCancelable(true);
return progressDialog;
default:
return null;
}
}
#Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch(id) {
case PROGRESS_DIALOG:
progressDialog.setProgress(0);
}
}
// Define the Handler that receives messages from the thread and update the progress
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
int current = msg.arg1;
progressDialog.setProgress(current);
if (current >= 100){
removeDialog (PROGRESS_DIALOG);
}
}
};
// The method called by the observer (the second thread)
#Override
public void update(Observable obs, Object arg1) {
Message msg = handler.obtainMessage();
msg.arg1 = ++currentPluginNumber;
handler.sendMessage(msg);
}
}
This explanation can be found on this page, and you must read the "Example ProgressDialog with a second thread".
I see that you have accepted #providence's answer. Just in case, you can also use the handler too! First, do the int fields.
private static final int SHOW_LOG = 1;
private static final int HIDE_LOG = 0;
Next, make a handler instance as a field.
//TODO __________[ Handler ]__________
#SuppressLint("HandlerLeak")
protected Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
// Put code here...
// Set a switch statement to toggle it on or off.
switch(msg.what)
{
case SHOW_LOG:
{
ads.setVisibility(View.VISIBLE);
break;
}
case HIDE_LOG:
{
ads.setVisibility(View.GONE);
break;
}
}
}
};
Make a method.
//TODO __________[ Callbacks ]__________
#Override
public void showHandler(boolean show)
{
handler.sendEmptyMessage(show ? SHOW_LOG : HIDE_LOG);
}
Finally, put this at onCreate() method.
showHandler(true);
Use this code, and no need to runOnUiThread function:
private Handler handler;
private Runnable handlerTask;
void StartTimer(){
handler = new Handler();
handlerTask = new Runnable()
{
#Override
public void run() {
// do something
textView.setText("some text");
handler.postDelayed(handlerTask, 1000);
}
};
handlerTask.run();
}
I had a similar issue, and my solution is ugly, but it works:
void showCode() {
hideRegisterMessage(); // Hides view
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
showRegisterMessage(); // Shows view
}
}, 3000); // After 3 seconds
}
I was facing a similar problem and none of the methods mentioned above worked for me. In the end, this did the trick for me:
Device.BeginInvokeOnMainThread(() =>
{
myMethod();
});
I found this gem here.
I use Handler with Looper.getMainLooper(). It worked fine for me.
Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
// Any UI task, example
textView.setText("your text");
}
};
handler.sendEmptyMessage(1);
This is explicitly throwing an error. It says whichever thread created a view, only that can touch its views. It is because the created view is inside that thread's space. The view creation (GUI) happens in the UI (main) thread. So, you always use the UI thread to access those methods.
In the above picture, the progress variable is inside the space of the UI thread. So, only the UI thread can access this variable. Here, you're accessing progress via new Thread(), and that's why you got an error.
For a one-liner version of the runOnUiThread() approach, you can use a lambda function, i.e.:
runOnUiThread(() -> doStuff(Object, myValue));
where doStuff() can represents some method used to modify the value of some UI Object (setting text, changing colors, etc.).
I find this to be much neater when trying to update several UI objects without the need for a 6 line Runnable definition at each as mentioned in the most upvoted answer, which is by no means incorrect, it just takes up a lot more space and I find to be less readable.
So this:
runOnUiThread(new Runnable() {
#Override
public void run() {
doStuff(myTextView, "myNewText");
}
});
can become this:
runOnUiThread(() -> doStuff(myTextView, "myNewText"));
where the definition of doStuff lies elsewhere.
Or if you don't need to be so generalizable, and just need to set the text of a TextView object:
runOnUiThread(() -> myTextView.setText("myNewText"));
For anyone using fragment:
(context as Activity).runOnUiThread {
//TODO
}
This happened to my when I called for an UI change from a doInBackground from Asynctask instead of using onPostExecute.
Dealing with the UI in onPostExecute solved my problem.
I was working with a class that did not contain a reference to the context. So it was not possible for me to use runOnUIThread(); I used view.post(); and it was solved.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
final int currentPosition = mediaPlayer.getCurrentPosition();
audioMessage.seekBar.setProgress(currentPosition / 1000);
audioMessage.tvPlayDuration.post(new Runnable() {
#Override
public void run() {
audioMessage.tvPlayDuration.setText(ChatDateTimeFormatter.getDuration(currentPosition));
}
});
}
}, 0, 1000);
When using AsyncTask Update the UI in onPostExecute method
#Override
protected void onPostExecute(String s) {
// Update UI here
}
This is the stack trace of mentioned exception
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6149)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:843)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
at android.view.View.requestLayout(View.java:16474)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
at android.view.View.setFlags(View.java:8938)
at android.view.View.setVisibility(View.java:6066)
So if you go and dig then you come to know
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
Where mThread is initialize in constructor like below
mThread = Thread.currentThread();
All I mean to say that when we created particular view we created it on UI Thread and later try to modifying in a Worker Thread.
We can verify it via below code snippet
Thread.currentThread().getName()
when we inflate layout and later where you are getting exception.
If you do not want to use runOnUiThread API, you can in fact implement AsynTask for the operations that takes some seconds to complete. But in that case, also after processing your work in doinBackground(), you need to return the finished view in onPostExecute(). The Android implementation allows only main UI thread to interact with views.
If you simply want to invalidate (call repaint/redraw function) from your non UI Thread, use postInvalidate()
myView.postInvalidate();
This will post an invalidate request on the UI-thread.
For more information : what-does-postinvalidate-do
Well, You can do it like this.
https://developer.android.com/reference/android/view/View#post(java.lang.Runnable)
A simple approach
currentTime.post(new Runnable(){
#Override
public void run() {
currentTime.setText(time);
}
}
it also provides delay
https://developer.android.com/reference/android/view/View#postDelayed(java.lang.Runnable,%20long)
For me the issue was that I was calling onProgressUpdate() explicitly from my code. This shouldn't be done. I called publishProgress() instead and that resolved the error.
In my case,
I have EditText in Adaptor, and it's already in the UI thread. However, when this Activity loads, it's crashes with this error.
My solution is I need to remove <requestFocus /> out from EditText in XML.
For the people struggling in Kotlin, it works like this:
lateinit var runnable: Runnable //global variable
runOnUiThread { //Lambda
runnable = Runnable {
//do something here
runDelayedHandler(5000)
}
}
runnable.run()
//you need to keep the handler outside the runnable body to work in kotlin
fun runDelayedHandler(timeToWait: Long) {
//Keep it running
val handler = Handler()
handler.postDelayed(runnable, timeToWait)
}
If you couldn't find a UIThread you can use this way .
yourcurrentcontext mean, you need to parse Current Context
new Thread(new Runnable() {
public void run() {
while (true) {
(Activity) yourcurrentcontext).runOnUiThread(new Runnable() {
public void run() {
Log.d("Thread Log","I am from UI Thread");
}
});
try {
Thread.sleep(1000);
} catch (Exception ex) {
}
}
}
}).start();
In Kotlin simply put your code in runOnUiThread activity method
runOnUiThread{
// write your code here, for example
val task = Runnable {
Handler().postDelayed({
var smzHtcList = mDb?.smzHtcReferralDao()?.getAll()
tv_showSmzHtcList.text = smzHtcList.toString()
}, 10)
}
mDbWorkerThread.postTask(task)
}
If you are within a fragment, then you also need to get the activity object as runOnUIThread is a method on the activity.
An example in Kotlin with some surrounding context to make it clearer - this example is navigating from a camera fragment to a gallery fragment:
// Setup image capture listener which is triggered after photo has been taken
imageCapture.takePicture(
outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
Log.d(TAG, "Photo capture succeeded: $savedUri")
//Do whatever work you do when image is saved
//Now ask navigator to move to new tab - as this
//updates UI do on the UI thread
activity?.runOnUiThread( {
Navigation.findNavController(
requireActivity(), R.id.fragment_container
).navigate(CameraFragmentDirections
.actionCameraToGallery(outputDirectory.absolutePath))
})
Solved : Just put this method in doInBackround Class... and pass the message
public void setProgressText(final String progressText){
Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
// Any UI task, example
progressDialog.setMessage(progressText);
}
};
handler.sendEmptyMessage(1);
}
I've built a simple music player in Android. The view for each song contains a SeekBar, implemented like this:
public class Song extends Activity implements OnClickListener,Runnable {
private SeekBar progress;
private MediaPlayer mp;
// ...
private ServiceConnection onService = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder rawBinder) {
appService = ((MPService.LocalBinder)rawBinder).getService(); // service that handles the MediaPlayer
progress.setVisibility(SeekBar.VISIBLE);
progress.setProgress(0);
mp = appService.getMP();
appService.playSong(title);
progress.setMax(mp.getDuration());
new Thread(Song.this).start();
}
public void onServiceDisconnected(ComponentName classname) {
appService = null;
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.song);
// ...
progress = (SeekBar) findViewById(R.id.progress);
// ...
}
public void run() {
int pos = 0;
int total = mp.getDuration();
while (mp != null && pos<total) {
try {
Thread.sleep(1000);
pos = appService.getSongPosition();
} catch (InterruptedException e) {
return;
} catch (Exception e) {
return;
}
progress.setProgress(pos);
}
}
This works fine. Now I want a timer counting the seconds/minutes of the progress of the song. So I put a TextView in the layout, get it with findViewById() in onCreate(), and put this in run() after progress.setProgress(pos):
String time = String.format("%d:%d",
TimeUnit.MILLISECONDS.toMinutes(pos),
TimeUnit.MILLISECONDS.toSeconds(pos),
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(
pos))
);
currentTime.setText(time); // currentTime = (TextView) findViewById(R.id.current_time);
But that last line gives me the exception:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Yet I'm doing basically the same thing here as I'm doing with the SeekBar - creating the view in onCreate, then touching it in run() - and it doesn't give me this complaint.
You have to move the portion of the background task that updates the UI onto the main thread. There is a simple piece of code for this:
runOnUiThread(new Runnable() {
#Override
public void run() {
// Stuff that updates the UI
}
});
Documentation for Activity.runOnUiThread.
Just nest this inside the method that is running in the background, and then copy paste the code that implements any updates in the middle of the block. Include only the smallest amount of code possible, otherwise you start to defeat the purpose of the background thread.
I solved this by putting runOnUiThread( new Runnable(){ .. inside run():
thread = new Thread(){
#Override
public void run() {
try {
synchronized (this) {
wait(5000);
runOnUiThread(new Runnable() {
#Override
public void run() {
dbloadingInfo.setVisibility(View.VISIBLE);
bar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
}
Intent mainActivity = new Intent(getApplicationContext(),MainActivity.class);
startActivity(mainActivity);
};
};
thread.start();
My solution to this:
private void setText(final TextView text,final String value){
runOnUiThread(new Runnable() {
#Override
public void run() {
text.setText(value);
}
});
}
Call this method on a background thread.
Kotlin coroutines can make your code more concise and readable like this:
MainScope().launch {
withContext(Dispatchers.Default) {
//TODO("Background processing...")
}
TODO("Update UI here!")
}
Or vice versa:
GlobalScope.launch {
//TODO("Background processing...")
withContext(Dispatchers.Main) {
// TODO("Update UI here!")
}
TODO("Continue background processing...")
}
Usually, any action involving the user interface must be done in the main or UI thread, that is the one in which onCreate() and event handling are executed. One way to be sure of that is using runOnUiThread(), another is using Handlers.
ProgressBar.setProgress() has a mechanism for which it will always execute on the main thread, so that's why it worked.
See Painless Threading.
You can use Handler to Delete View without disturbing the main UI Thread.
Here is example code
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
//do stuff like remove view etc
adapter.remove(selecteditem);
}
});
Kotlin Answer
We have to use UI Thread for the job with true way. We can use UI Thread in Kotlin, such as:
runOnUiThread(Runnable {
//TODO: Your job is here..!
})
I've been in this situation, but I found a solution with the Handler Object.
In my case, I want to update a ProgressDialog with the observer pattern.
My view implements observer and overrides the update method.
So, my main thread create the view and another thread call the update method that update the ProgressDialop and....:
Only the original thread that created a view hierarchy can touch its
views.
It's possible to solve the problem with the Handler Object.
Below, different parts of my code:
public class ViewExecution extends Activity implements Observer{
static final int PROGRESS_DIALOG = 0;
ProgressDialog progressDialog;
int currentNumber;
public void onCreate(Bundle savedInstanceState) {
currentNumber = 0;
final Button launchPolicyButton = ((Button) this.findViewById(R.id.launchButton));
launchPolicyButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
showDialog(PROGRESS_DIALOG);
}
});
}
#Override
protected Dialog onCreateDialog(int id) {
switch(id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage("Loading");
progressDialog.setCancelable(true);
return progressDialog;
default:
return null;
}
}
#Override
protected void onPrepareDialog(int id, Dialog dialog) {
switch(id) {
case PROGRESS_DIALOG:
progressDialog.setProgress(0);
}
}
// Define the Handler that receives messages from the thread and update the progress
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
int current = msg.arg1;
progressDialog.setProgress(current);
if (current >= 100){
removeDialog (PROGRESS_DIALOG);
}
}
};
// The method called by the observer (the second thread)
#Override
public void update(Observable obs, Object arg1) {
Message msg = handler.obtainMessage();
msg.arg1 = ++currentPluginNumber;
handler.sendMessage(msg);
}
}
This explanation can be found on this page, and you must read the "Example ProgressDialog with a second thread".
I see that you have accepted #providence's answer. Just in case, you can also use the handler too! First, do the int fields.
private static final int SHOW_LOG = 1;
private static final int HIDE_LOG = 0;
Next, make a handler instance as a field.
//TODO __________[ Handler ]__________
#SuppressLint("HandlerLeak")
protected Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
// Put code here...
// Set a switch statement to toggle it on or off.
switch(msg.what)
{
case SHOW_LOG:
{
ads.setVisibility(View.VISIBLE);
break;
}
case HIDE_LOG:
{
ads.setVisibility(View.GONE);
break;
}
}
}
};
Make a method.
//TODO __________[ Callbacks ]__________
#Override
public void showHandler(boolean show)
{
handler.sendEmptyMessage(show ? SHOW_LOG : HIDE_LOG);
}
Finally, put this at onCreate() method.
showHandler(true);
Use this code, and no need to runOnUiThread function:
private Handler handler;
private Runnable handlerTask;
void StartTimer(){
handler = new Handler();
handlerTask = new Runnable()
{
#Override
public void run() {
// do something
textView.setText("some text");
handler.postDelayed(handlerTask, 1000);
}
};
handlerTask.run();
}
I had a similar issue, and my solution is ugly, but it works:
void showCode() {
hideRegisterMessage(); // Hides view
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
showRegisterMessage(); // Shows view
}
}, 3000); // After 3 seconds
}
I was facing a similar problem and none of the methods mentioned above worked for me. In the end, this did the trick for me:
Device.BeginInvokeOnMainThread(() =>
{
myMethod();
});
I found this gem here.
I use Handler with Looper.getMainLooper(). It worked fine for me.
Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
// Any UI task, example
textView.setText("your text");
}
};
handler.sendEmptyMessage(1);
This is explicitly throwing an error. It says whichever thread created a view, only that can touch its views. It is because the created view is inside that thread's space. The view creation (GUI) happens in the UI (main) thread. So, you always use the UI thread to access those methods.
In the above picture, the progress variable is inside the space of the UI thread. So, only the UI thread can access this variable. Here, you're accessing progress via new Thread(), and that's why you got an error.
For a one-liner version of the runOnUiThread() approach, you can use a lambda function, i.e.:
runOnUiThread(() -> doStuff(Object, myValue));
where doStuff() can represents some method used to modify the value of some UI Object (setting text, changing colors, etc.).
I find this to be much neater when trying to update several UI objects without the need for a 6 line Runnable definition at each as mentioned in the most upvoted answer, which is by no means incorrect, it just takes up a lot more space and I find to be less readable.
So this:
runOnUiThread(new Runnable() {
#Override
public void run() {
doStuff(myTextView, "myNewText");
}
});
can become this:
runOnUiThread(() -> doStuff(myTextView, "myNewText"));
where the definition of doStuff lies elsewhere.
Or if you don't need to be so generalizable, and just need to set the text of a TextView object:
runOnUiThread(() -> myTextView.setText("myNewText"));
For anyone using fragment:
(context as Activity).runOnUiThread {
//TODO
}
This happened to my when I called for an UI change from a doInBackground from Asynctask instead of using onPostExecute.
Dealing with the UI in onPostExecute solved my problem.
I was working with a class that did not contain a reference to the context. So it was not possible for me to use runOnUIThread(); I used view.post(); and it was solved.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
final int currentPosition = mediaPlayer.getCurrentPosition();
audioMessage.seekBar.setProgress(currentPosition / 1000);
audioMessage.tvPlayDuration.post(new Runnable() {
#Override
public void run() {
audioMessage.tvPlayDuration.setText(ChatDateTimeFormatter.getDuration(currentPosition));
}
});
}
}, 0, 1000);
When using AsyncTask Update the UI in onPostExecute method
#Override
protected void onPostExecute(String s) {
// Update UI here
}
This is the stack trace of mentioned exception
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6149)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:843)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.view.View.requestLayout(View.java:16474)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
at android.view.View.requestLayout(View.java:16474)
at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352)
at android.view.View.setFlags(View.java:8938)
at android.view.View.setVisibility(View.java:6066)
So if you go and dig then you come to know
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
Where mThread is initialize in constructor like below
mThread = Thread.currentThread();
All I mean to say that when we created particular view we created it on UI Thread and later try to modifying in a Worker Thread.
We can verify it via below code snippet
Thread.currentThread().getName()
when we inflate layout and later where you are getting exception.
If you do not want to use runOnUiThread API, you can in fact implement AsynTask for the operations that takes some seconds to complete. But in that case, also after processing your work in doinBackground(), you need to return the finished view in onPostExecute(). The Android implementation allows only main UI thread to interact with views.
If you simply want to invalidate (call repaint/redraw function) from your non UI Thread, use postInvalidate()
myView.postInvalidate();
This will post an invalidate request on the UI-thread.
For more information : what-does-postinvalidate-do
Well, You can do it like this.
https://developer.android.com/reference/android/view/View#post(java.lang.Runnable)
A simple approach
currentTime.post(new Runnable(){
#Override
public void run() {
currentTime.setText(time);
}
}
it also provides delay
https://developer.android.com/reference/android/view/View#postDelayed(java.lang.Runnable,%20long)
For me the issue was that I was calling onProgressUpdate() explicitly from my code. This shouldn't be done. I called publishProgress() instead and that resolved the error.
In my case,
I have EditText in Adaptor, and it's already in the UI thread. However, when this Activity loads, it's crashes with this error.
My solution is I need to remove <requestFocus /> out from EditText in XML.
For the people struggling in Kotlin, it works like this:
lateinit var runnable: Runnable //global variable
runOnUiThread { //Lambda
runnable = Runnable {
//do something here
runDelayedHandler(5000)
}
}
runnable.run()
//you need to keep the handler outside the runnable body to work in kotlin
fun runDelayedHandler(timeToWait: Long) {
//Keep it running
val handler = Handler()
handler.postDelayed(runnable, timeToWait)
}
If you couldn't find a UIThread you can use this way .
yourcurrentcontext mean, you need to parse Current Context
new Thread(new Runnable() {
public void run() {
while (true) {
(Activity) yourcurrentcontext).runOnUiThread(new Runnable() {
public void run() {
Log.d("Thread Log","I am from UI Thread");
}
});
try {
Thread.sleep(1000);
} catch (Exception ex) {
}
}
}
}).start();
In Kotlin simply put your code in runOnUiThread activity method
runOnUiThread{
// write your code here, for example
val task = Runnable {
Handler().postDelayed({
var smzHtcList = mDb?.smzHtcReferralDao()?.getAll()
tv_showSmzHtcList.text = smzHtcList.toString()
}, 10)
}
mDbWorkerThread.postTask(task)
}
If you are within a fragment, then you also need to get the activity object as runOnUIThread is a method on the activity.
An example in Kotlin with some surrounding context to make it clearer - this example is navigating from a camera fragment to a gallery fragment:
// Setup image capture listener which is triggered after photo has been taken
imageCapture.takePicture(
outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
Log.d(TAG, "Photo capture succeeded: $savedUri")
//Do whatever work you do when image is saved
//Now ask navigator to move to new tab - as this
//updates UI do on the UI thread
activity?.runOnUiThread( {
Navigation.findNavController(
requireActivity(), R.id.fragment_container
).navigate(CameraFragmentDirections
.actionCameraToGallery(outputDirectory.absolutePath))
})
Solved : Just put this method in doInBackround Class... and pass the message
public void setProgressText(final String progressText){
Handler handler = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
// Any UI task, example
progressDialog.setMessage(progressText);
}
};
handler.sendEmptyMessage(1);
}
i want to repeat task while the activity is open.
For example repeat foo() every minute while the activity is open.
I tought about Timer, handler and runable.
I tought about this code:
Maybe there is some better way?
public void setRefreshRate()
{
newTimer = true
while(true)
{
if(newTimer)
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if(isNetworkAvailable() && movedToAnotherActivity== false)
new GetWorkouts().execute();
newTimer = true;
}
}, Integer.getInteger(data.getWallRefresh()));
newTimer = false;
}
}
There may be a better way but I like AsyncTask so I would probably use that and call sleep() in doInBackground() then you can call cancel() on your task object and set it to null when the Activity finishes.
public void doInBackground(Void...params)
{
boolean flag = false;
while (!flag)
{
// do some work
Thread.sleep(6000);
}
}
then overide and set flag to true in onBackPressed() and finish(). You can then use any of the other AsyncTask methods to update the UI if necessary.
AsyncTask
Thanks to codeMagic's answer for starting me down the right path, but AsyncTask isn't really designed for this. From the docs:
AsyncTasks should ideally be used for short operations (a few seconds at the most.)
The problem is that AsyncTasks, at least by default, run sequentially on the same worker thread, so if you try to launch another AsyncTask, it won't be able to run, since the timer loop never finishes.
To work around this, I just used a raw Thread and it's working fine.
apiUpdateTimerThread = new Thread(new Runnable() {
#Override
public void run() {
while(true) {
try {
Log.i(TAG, "UPDATE FROM THE API!!!!");
doSomeStuff();
Thread.sleep(5 * 1000);
} catch(InterruptedException e) {
Log.e(TAG, "API Update AsyncTask Interrupted", e);
}
}
}
});
To stop it, just call
apiUpdateTimerThread.interrupt();