I have been trying to figure out how to make a custom chronometer that is precise down to a hundredth of a second. I have looked at a lot of the other similar questions and decided to try it using a thread and handler.
This is my custom handleMessage method:
public void handleMessage(Message msg) {
String timeText = msg.getData().getString("time");
watch.setText(timeText);
}
The watch object is just a TextView that is initialized in onCreate().
And this is the main chunk of my run method:
while(true) {
long timeElapsed = System.currentTimeMillis() - startTime;
int hundreths = (int)((timeElapsed % 1000) / 10);
int seconds = (int)((timeElapsed % 60000) / 1000);
int minutes = (int)(timeElapsed / 60000);
Bundle bundle = new Bundle();
bundle.putString("time", String.format("d:%02d.%02d", minutes, seconds, hundreths));
Message msg = handler.obtainMessage();
msg.setData(bundle);
handler.handleMessage(msg);
}
I realize that the whole idea behind using a handler is that only the UI thread can update elements on the screen, but I am still getting a CalledFromWrongThreadException with the message that only the original thread that started created the View hierarchy can call methods on it. I am confused as to what exactly I am doing wrong.
I think your error is because you are using handleMessage instead of sendMessage.
A simpler approach might be to use runOnUiThread method and give up the handler.
http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)
Related
I want to create a monitoring application and their updated UI every second. for example I have 10 textView for display timing and 10 Progress bar to set some progress to display and 6 timers for display time like a stopwatch. all things in the same activity and its run also at the same time.
But When I used ScheduledExecutorService UI stuck and the application going to not respond. how to Implement all things perfectly without ANR?
Here is My code update textView Timer in the thread
private void getLiveUpdate() {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
MyTimerTask myTimerTask = new MyTimerTask(() -> {
runOnUiThread(() -> {
getSetData();
getTime(binding.tvCurrentDate);
setRv_channels(switchGroup1, Utills.switchModels1, binding.rvChannelsMain);
setRv_channels(switchGroup2, Utills.switchModels2, binding.rvChannelsMain1);
setRv_channels(switchGroup3, Utills.switchModels3, binding.rvChannelsMai2);
if (isOtOn) {
binding.tvOtOffTime.setText(timee(otStartCounter));
otStartCounter++;
}
if (isPatientIn) {
binding.tvPOutTime.setText(timee(patientInCounter));
patientInCounter++;
}
if (isSurgIn) {
binding.tvSugOutTime.setText(timee(surgeonTimeCounter));
surgeonTimeCounter++;
}
if (isAnaeIn) {
binding.tvAnafTime.setText(timee(anaeTimeCounter));
anaeTimeCounter++;
}
if (isSurgeryStart) {
binding.tvSurgeryTime.setText(timee(surgeryTimeConunter));
surgeryTimeConunter++;
}
if (isAnaeStart) {
binding.tvAneTime.setText(timee(anaeStartTimeConunter));
anaeStartTimeConunter++;
}
});
});
scheduler.scheduleAtFixedRate(myTimerTask, 0, 1, TimeUnit.SECONDS);
}
private String timee(int seconds) {
int hours = seconds / 3600;
int minutes = (seconds % 3600) / 60;
int secs = seconds % 60;
return String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, secs);
}
UI thread is one and only, there is no such thing as "multiple UI thread"
if you are performing some actions in separated thread and want to show (partial) results in GUI you have to run GUI-drawing-related code (e.g. textView.setText(...)) in this one and only UI thread. easiest way is to use Handler with MainLooper - its called "main", because UI thread is one and only mandatory working thread, you may not thread your app/code at all. so you can get access to it by some static refrerences, thus below may be pasted literally anywhere in any thread
Handler(Looper.getMainLooper()).post {
// UI related code
}
still if you want to change text in some TextView, which is created/referenced in UI thread only you have to pass reference to it to this "another thread" and use this reference inside Runnable posted for Handler with main Looper
Basically, I am trying to do something more complicated than that, but this is my problem:
When using handler.postDelayed inside a for loop, there's delay only on the first time, and I wait the delay to kick in every time the for loop repeats:
For example, in this case:
for(int z=0; z<4; z++) {
final int finalZ = z;
handler.postDelayed(new Runnable() {
#Override
public void run() {
Toast.makeText(play.this, "z:" + finalZ, Toast.LENGTH_SHORT).show();
}
}, 5000);
}
I will get:
Waiting 5 seconds.
z:0
z:1
z:2
z:3
What I want to get:
Waiting 5 seconds.
z:0
Waiting 5 seconds.
z:1
Waiting 5 seconds.
z:2
Waiting 5 seconds.
z:3
I was told not to use Thread.sleep() because it can cause various issues (I didn't quite understand them as I am new to android studio). I am using this in a certain activity (not my Main_Activity).
I am basically trying to change an image's color ever 0.5 seconds or so (more complications go into that, but that's the main idea). Will Thread.sleep() be better?
EDIT: Okay, so thanks to #tynn & #pskink I got this to work (see their answers). But now, another problem came up following this.
If, for example, I will run the exact same code after that, they will both run at the same time, and not one after another, how can i make the second "for" start only after the first "for" has ended?
Here another aspect
private int z=0;
Runnable r = new Runnable() {
#Override
public void run() {
Toast.makeText(play.this, "z:" + z, Toast.LENGTH_SHORT).show();
if(z<4){
z++;
handler.postDelayed(r, 5000);
}
}
}
handler.post(r);
The handler runs your code on another thread, independent of your for loop. The delay is relative to the point where you posted the runnable. Not relative to the previous post. Also it doesn't block the for loop at all. That's the purpose of handlers.
You can assume that any post in this scenario happens at the same time, so you could just do handler.postDelayed(runnable, (z + 1) * 5000). Or maybe define the execution time as absolute with handler.postAtTime(runnable, firstRun + z * 5000)
But since you're trying to do something more complicated than that, you should better consider using a Timer or similar. Maybe RxJava.
Your current code works like this:
hey handler, do it after 5s,
oh and do it after 5s too,
and do it after 5s too,
and this plz do after 5s.
Then we wait 5s and handler is doing those actions in one moment.
What you can do is change it to:
hey handler, do it after 5s,
oh and do it after 10s,
and this after 15s
...
Or you can do it like this:
hey handler do it after 5s, where 'it' action is 'hey handler do it after 5s'.
Then handler will wait 5s and call action 'hey handler do it after 5s'. So handler will have to wait again 5s and do some action after that time.
Here is code for my aproach:
final int finalZ = z;
delayedAction(handler, 0, z);
. . .
void delayedAction(final Handler handler, final int i, final int max) {
if(i<max) {
Toast.makeText(play.this, "z:" + finalZ, Toast.LENGTH_SHORT).show();
handler.postDelayed(new Runnable() {
#Override
public void run() {
delayedAction(handler, i+1, max);
}
});
}
}
I'm using a runnable in my Android app to update a countdown timer, as shown in the code below. It appears to work but I noticed my timer takes a few seconds longer than expected. For example, if it's supposed to count down 3 minutes, it takes 3 minutes and 5 seconds. I tried using a timer in a service to manage the countdown display in the main activity. The timer/service worked as expected.
Why doesn't runnable/postDelayed() run for the correct amount of time? Is postDelayed() timing reliable? The runnable decrements a variable then uses it to update an EditText with setText(). Does setText() take too long (a small fraction of a second), so the runnable really runs every 1.x seconds?
Handler handler = new Handler();
Runnable r = new Runnable() {
public void run() {
// decrement the time remaining and update the display
handler.postDelayed(this, 1000);
}
};
...
// start the runnable
handler.postDelayed(r, 1000);
Your code is kinda sorta designed to be inaccurate because you are not accounting for the time taken by the guts of the runnable. You might get improved results by doing something like
public void run(){
startTime = System.currentTimeMillis();
// compare expectedTime to startTime and compensate
// <guts of runnable goes here>
// now wrap it up...
delay = 1000 - (System.currentTimeMillis() - startTime);
if (delay < 0)
delay = 0;
expectedTime = System.currentTimeMillies() + delay;
handler.postDelayed(this, delay);
}
What about using CountDownTimer? I used this for same tasks several times and haven’t met this kind of problem.
I've made a simple Android music player. I want to have a TextView that shows the current time in the song in minutes:seconds format. So the first thing I tried was to make the activity Runnable and put this in run():
int position = 0;
while (MPService.getMP() != null && position<MPService.duration) {
try {
Thread.sleep(1000);
position = MPService.getSongPosition();
} catch (InterruptedException e) {
return;
}
// ... convert position to formatted minutes:seconds string ...
currentTime.setText(time); // currentTime = (TextView) findViewById(R.id.current_time);
But that fails because I can only touch a TextView in the thread where it was created. So then I tried using runOnUiThread(), but that doesn't work because then Thread.sleep(1000) is called repeatedly on the main thread, so the activity just hangs at a blank screen. So any ideas how I can solve this?
new code:
private int startTime = 0;
private Handler timeHandler = new Handler();
private Runnable updateTime = new Runnable() {
public void run() {
final int start = startTime;
int millis = appService.getSongPosition() - start;
int seconds = (int) ((millis / 1000) % 60);
int minutes = (int) ((millis / 1000) / 60);
Log.d("seconds",Integer.toString(seconds)); // no problem here
if (seconds < 10) {
// this is hit, yet the text never changes from the original value of 0:00
currentTime.setText(String.format("%d:0%d",minutes,seconds));
} else {
currentTime.setText(String.format("%d:%d",minutes,seconds));
}
timeHandler.postAtTime(this,(((minutes*60)+seconds+1)*1000));
}
};
private ServiceConnection onService = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder rawBinder) {
appService = ((MPService.LocalBinder)rawBinder).getService();
// start playing the song, etc.
if (startTime == 0) {
startTime = appService.getSongPosition();
timeHandler.removeCallbacks(updateTime);
timeHandler.postDelayed(updateTime,1000);
}
}
what about this:
int delay = 5000; // delay for 5 sec.
int period = 1000; // repeat every sec.
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask()
{
public void run()
{
//your code
}
}, delay, period);
Use a Timer for this (instead of a while loop with a Thread.Sleep in it). See this article for an example of how to use a timer to update a UI element periodically:
Updating the UI from a timer
Edit: updated way-back link, thanks to Arialdo: http://web.archive.org/web/20100126090836/http://developer.android.com/intl/zh-TW/resources/articles/timed-ui-updates.html
Edit 2: non way-back link, thanks to gatoatigrado: http://android-developers.blogspot.com/2007/11/stitch-in-time.html
You have to use a handler to handle the interaction with the GUI. Specifically a thread cannot touch ANYTHING on the main thread. You do something in a thread and if you NEED something to be changed in your main thread, then you call a handler and do it there.
Specifically it would look something like this:
Thread t = new Thread(new Runnable(){
... do stuff here
Handler.postMessage();
}
Then somewhere else in your code, you do
Handler h = new Handler(){
something something...
modify ui element here
}
Idea its like this, thread does something, notifies the handler, the handler then takes this message and does something like update a textview on the UI thread.
This is one more Timer example and I'm using this code in my project.
https://stackoverflow.com/a/18028882/1265456
I think the below blog article clearly gives a very nice solution. Especially, if you are a background service and want to regularly update your UI from this service using a timer-like functionality.
It really helped me, much more than the 2007 blog link posted by MusiGenesis above.
https://www.websmithing.com/2011/02/01/how-to-update-the-ui-in-an-android-activity-using-data-from-a-background-service/
The code that follows comes from p.58-61 of the book "Android Developer's Cookbook". The book introduces the code in the context of messages being a way to pass information between threads. It describes the code by saying: "The timer is run in a background thread so it does not block the UI thread, but it needs to update the UI whenever the time changes."
I'm confused because I don't see two threads. To me it seems that the main UI thread posts a runnable message to its own message queue (and that message then re-posts itself with a time-delay). Am I missing something?
package com.cookbook.background_timer;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class BackgroundTimer extends Activity {
//keep track of button presses, a main thread task
private int buttonPress=0;
TextView mButtonLabel;
//counter of time since app started, a background task
private long mStartTime = 0L;
private TextView mTimeLabel;
//Handler to handle the message to the timer task
private Handler mHandler = new Handler();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (mStartTime == 0L) {
mStartTime = SystemClock.uptimeMillis();
mHandler.removeCallbacks(mUpdateTimeTask);
mHandler.postDelayed(mUpdateTimeTask, 100);
}
mTimeLabel = (TextView) findViewById(R.id.text);
mButtonLabel = (TextView) findViewById(R.id.trigger);
Button startButton = (Button) findViewById(R.id.trigger);
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view){
mButtonLabel.setText("Pressed " + ++buttonPress + " times");
}
});
}
private Runnable mUpdateTimeTask = new Runnable() {
public void run() {
final long start = mStartTime;
long millis = SystemClock.uptimeMillis() - start;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
mTimeLabel.setText("" + minutes + ":" + String.format("%02d",seconds));
mHandler.postDelayed(this, 200);
}
};
#Override
protected void onPause() {
mHandler.removeCallbacks(mUpdateTimeTask);
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
mHandler.postDelayed(mUpdateTimeTask, 100);
}
}
The second thread is kind of hidden. It's when you call postDelayed(mUpdateTImeTask,100) in onCreate(). The handler has a thread in it that counts down the delay time (100 milliseconds in this case) and then runs mUpdateTImeTask. Note that at the end of the run() method of mUpdateTimeTask, it puts itself back in the handler's timer thread by calling postDelayed() again.
The Android api has lots of classes like Handler and AsyncTask that make it easier to do multithreading. These classes hide a lot of the nuts and bolts of threading (which is what makes them nice to use). Unfortunately, that makes it hard to learn what's going on--you sort of have to know what's going on in order to learn it. :)
The Runnable class is essentially a class used in threading. The run() method will be invoked by the interface that calls it (the Handler) and - in this implementation - the application sets up the Handler to run mUpdateTimeTask 100ms after that line is executed. Which will then run everything in the run() method in your Runnable.
When onCreate() is called, your application gets the mTimeLabel object from the view and it is updated with the setText() method in your Runnable. That will update the time on your UI thread and then register itself to go off in another 200 milliseconds.
There is no second thread here! You can test easily by putting some expensive code in the runnable, which will block the UI thread. You have to make a new Thread(Runnable) and go from there.
This is something you need almost in every project. I had to add a Timer class in my open-source Aniqroid library which is get triggered in the UI thread and utilizes the Handler.postDelayed() feature without having to write all the boiler-plate code.
http://aniqroid.sileria.com/doc/api/ (Look for downloads at the bottom or use google code project to see more download options: http://code.google.com/p/aniqroid/downloads/list)
The class documentation is here: http://aniqroid.sileria.com/doc/api/com/sileria/android/Timer.html