Multiple Runnables on one handler, wrong repeat behaviour - android

In my android application I need to update different parts of the UI (actually a Fragment inside a ViewPager on a different time intervals. To do this I've created an Handler and 3 Runnables. With the postDelayed(Runnable, delay) method the Runnables should repeat themselves at scheduled time intervals.
Currently I have 1 runnable that must update a TextView every 100 ms, another that update a ProgressBar every 1000 ms and another one that checks some stuff every 2000 ms.
I saw the counter and the progress bar incrementing too fast and so I've put some code checking the delay between every call to the Run() method and I've discovered that the first runnable is executed every 58 ms and the progressbar's one running every 508 ms.
I've then tried to left only the first Runnable on the Handler (the counter one) and the Run() method get called every ~101 ms. So I've hypothized that other Runnables maybe interfer with the Message Queue schedule. I've then created one Handler for every Runnable but in that way the problem persist with 56~58 ms in the first Runnable and 508~510 ms on the second one.
Any suggestion on why this behaviour happen?
Here's part of my code:
private static final int TIME_STEP_COUNTER = 100;
private static final int TIME_STEP_PROGRESS = 1000;
private static final int TIME_STEP_DIRECTOR = 2000;
private Handler counterHandler, progressHandler, directorHandler;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
// some code here
directorThread = new DirectorThread(wwp,TIME_STEP_DIRECTOR);
if(directorHandler == null){
directorHandler = new Handler();
directorHandler.post(directorThread);
}
startCounting();
}
StartCounting() method
public void startCounting(){
stdCounterUpdater = new CounterUpdaterThread(textViewCounter,TIME_STEP_COUNTER, increment100ms);
progressUpdaterThread = new ProgressUpdaterThread(progressBar,TIME_STEP_PROGRESS,textViewPercentage);
fixerThread = new FixerThread(wwp,TIME_STEP_FIXER);
if(counterHandler == null){
counterHandler = new Handler();
}
counterHandler.post(stdCounterUpdater);
if(progressHandler == null){
progressHandler = new Handler();
}
progressHandler.postDelayed(progressBarInitThread, DELAY_INIT_PROGRESS);
progressHandler.postDelayed(progressUpdaterThread, DELAY_INIT_PROGRESS + INIT_PROGRESS_DURATION);
counterLayout.setVisibility(View.VISIBLE);
}
Threads
CounterUpdater
class CounterUpdaterThread implements Runnable{
public CounterUpdaterThread(TextView counterView, long deltaTime, float increment){
//Constructor with fields initialization
}
#Override
public void run(){
counter += increment;
changeCounterText();
if(counterHandler != null){
counterHandler.postDelayed(stdCounterUpdater,deltaTime);
}
}
}
ProgressUpdater
class ProgressUpdaterThread implements Runnable{
public ProgressUpdaterThread(ProgressBar pBar, long deltaTime, TextView percTW){
//Fields init
}
#Override
public void run(){
anim = new ProgressBarAnimation(progressBar, progress, progress + smoothScale);
progress += smoothScale;
anim.setDuration(PROGRESS_UPDATE_DURATION);
progressBar.startAnimation(anim);
percTW.setText(String.format(Locale.US,"%.1f", progressBar.getProgress()*100f/progressBar.getMax()) + "%");
if(progressHandler != null){
progressHandler.postDelayed(progressUpdaterThread,deltaTime);
}
}
}
DirectorThread
class DirectorThread implements Runnable{
public DirectorThread(WeeklyWorkPeriod wwp, long deltaTime){
//init
}
#Override
public void run(){
if(isStarted){
//....code.....
stopCounting();
isStarted = false;
//....code.....
}
}
else{
// ...code...
raiseMethod.requestProgressData(ProgressBarFragment.this,fragmentID);
startCounting();
isStarted = true;
}
}
if(directorHandler != null){
directorHandler.postDelayed(directorThread,deltaTime);
}
}
}
I've checked also these articles and some other question but with no results
Repeating Tasks
Handler Vs Timer
Handlers and Loopers

I just found the problem: wasn't about a single handler but about starting new callbacks in the DirectorThread. Just setting in the onCreateView() method isStarted to true solved the problem.
Among the other things I've replaced postDelayed() with postAtTime() as suggested by pskink cause it happens to be more accurate.

Related

Android view doesn't update when trying to set values over time

I got a fragment which got a control called RingProgress which is simply a ring that fills itself according to a percentage value given. For example, if I do:
ringProgress.setProgress(20);
It means that 20% of the ring will now be filled.
What I'm trying to do is to animate the ring being filled over a few seconds. So what I've tried to do is this:
#Override
public void onResume()
{
super.onResume();
HandlerThread handlerThread = new HandlerThread("countdown");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
handler.post(new Runnable()
{
#Override
public void run()
{
final Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask()
{
int totalSeconds = secondsToStart + minutesToStart * 60;
int secondsPassed = 0;
#Override
public void run()
{
if(secondsPassed == totalSeconds)
{
timer.cancel();
}
final int currentProgress = (secondsPassed / totalSeconds) * 100;
secondsPassed++;
getActivity().runOnUiThread(new Runnable()
{
#Override
public void run()
{
mRingProgressBar.setProgress(currentProgress);
}
});
}
}, 0, 1000);
}
});
}
The problem is that the update of the ring is not shown until the time is up. For example, if I set it for 5 seconds then when the fragment loads the ring is set to 0, then nothing happens for 5 seconds and then the ring is full with 100% all at once..
How can I start this animation properly?
I guess the problem is with
final int currentProgress = (secondsPassed / totalSeconds) * 100;
secondsPassed / totalSeconds return int value so it will be 0 or 1 only. And you multiply it to 100.
You have to use float or double instead
something like
final int currentProgress = Math.round(((float) secondsPassed)/((float) totalSeconds)*100f);
On this line:
Handler handler = new Handler(handlerThread.getLooper());
You are trying to get the looper from a handlerThread. But how sure you are the looper has already been initialized?
From the documentation of getLooper()
This method returns the Looper associated with this thread. If this thread not been started or for any reason is isAlive() returns false, this method will return null. If this thread has been started, this method will block until the looper has been initialized.
onLooperPrepared() is the callback, where you can be sure, that the Looper has been initialized, and therefore you can construct logics on that.
Thus, what you have to do, is to subclass HandlerThread and create appropriate logics from onLooperPrepared() callback.
Here's a nice post which will help you out. See implementation of MyWorkerThread class there.
Instead of using a handler, you could use a property animator as follows:
ObjectAnimator.ofInt(mRingProgressBar, "progress", 0, 100)
.setDuration(totalSeconds * 1000) //time is in miliseconds
.start();
This will find a method setProgress() in your mRingProgressBarand set the value according to the limits given. In the example above, 0 to 100.
You can read more about it here
Since you want to run on a different thread, you can use this handler in the top of the class:
private int progress = 0;
Handler timerHandler = new Handler();
Runnable timerRunnable = new Runnable() {
#Override
public void run() {
ringProgress.setProgress(progress);
progress += 20;
if (progress == 100) { //clear??
}
timerHandler.postDelayed(this, 1000);
}
};
In inCreate set the max:
ringProgress.setMax(100);
This will complete the animation within 5 seconds, then you can clear the animation. If you want smaller increments, change the line below (update every tenth of a second), and change the steps
timerHandler.postDelayed(this, 100);

Timer not working in Android when calling invalidate()

I want to create a flashing effect by drawing a path with color grey, white (matching to the background), and then grey again. I want to flash 3 times, showing gray for 1 sec, white for 1 sec gray again for 1 sec, etc.
When I created a Handler for postDelayed(), the program skipped over the run() and did not execute it in the timing set, and no flashing occurred:
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
invalidate(); //calls onDraw()
Log.d(TAG, "Flashing now now");
}
}, 1000);
How would I implement such a flashing functionality with a timer and flash it 3 times?
Thanks!
You can try something like 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() {
System.out.println("done}
}, delay, period);
Otherwise you have plenty other examples to follow like this Example 1, Example 2, Example 3 etc. Let me know if you still need help.
This is a working code example of how we flash a globe from blue to red and back again. You can modify the code on the inside to limit how many times and what time delay you want.
protected MyGlobeFlasherHandler handlerFlashGlobe = new MyGlobeFlasherHandler(this);
#Override
protected onCreate(Bundle bundle) {
handlerFlashGlobe.sendEmptyMessageDelayed(0, 700);
}
/**
* private static handler so there are no leaked activities.
*/
protected static class MyGlobeFlasherHandler extends Handler {
private final WeakReference<HomeBase> activity;
public MyGlobeFlasherHandler(HomeBase activity) {
this.activity = new WeakReference<HomeBase>(activity);
}
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (activity.get() != null) {
if (activity.get().shadedGlobe) {
activity.get().imgData.setImageDrawable(activity.get().getResources().getDrawable(R.drawable.globe_blue));
} else {
activity.get().imgData.setImageDrawable(activity.get().getResources().getDrawable(R.drawable.globe_red));
}
activity.get().shadedGlobe = !activity.get().shadedGlobe;
sendEmptyMessageDelayed(0, 700);
}
}
}

Current Download/Upload Speed in Android

All,
I am newbie in android, just developing app to display the current Download/Upload Speed. I have got some tips from this Example. It uses TrafficStats.getTotalRxBytes() function that returns the total Bytes received after boot and increases monotonically.
So i used the below code, to display only the Current Available Speed. But i am not getting the result. it displays the Total Tx/Rx values only.
Is there any Alternative to achieve this? Any Code snippets would be much appreciated as i am a complete beginner.
Also, i cant understand below code. Can some one explain this?
private final Runnable mRunnable = new Runnable()
i need to refresh the data displayed in textView for every one second. I Guess
mHandler.postDelayed(mRunnable, 1000) ( );
is used to do that. is there any alternative? if not, how does would be the control flow when using PostDelayed function? (I am bit confused)
public class MainActivity extends Activity {
private Handler mHandler = new Handler();
private long mStartRX =0;
private long mStartTX = 0;
private static final int RESULT_SETTINGS = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(mRunnable, 1000);
}
private final Runnable mRunnable = new Runnable()
{
public void run()
{
long TxResult=0;
long RxResult=0;
long or1=0,or2=0;
long rxBytes = TrafficStats.getTotalRxBytes()- mStartRX-or1;
if (rxBytes<1024)
{
RxResult=0;
}
else if (rxBytes>1024)
{
// RxResult=0;
RxResult=RxResult+(rxBytes/1024);
}
TextView RX = (TextView)findViewById(R.id.rxOut);
//mStartRX=rxBytes;
TextView TX = (TextView)findViewById(R.id.txOut);
RX.setText(Long.toString(RxResult));
long txBytes = TrafficStats.getTotalTxBytes()- mStartTX-or2;
if (txBytes<1024)
{
TxResult=0;
}
else if (txBytes>1024)
{
TxResult=0;
TxResult=TxResult+(txBytes/1024);
}
TX.setText(Long.toString(TxResult));
or2=or2+txBytes;
or1=or1+rxBytes;
mHandler.postDelayed(mRunnable, 100);
}
};

Updating UI on button click after a certain time

I have a TextView. I want to update its text (append a "1") after 1 second of a button click.
public class HaikuDisplay extends Activity {
Method m;
Timer t;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
t = new Timer();
m = HaikuDisplay.class.getMethod("change");
}
//Event handler of the button
public void onRefresh(View view)
{
//To have the reference of this inside the TimerTask
final HaikuDisplay hd = this;
TimerTask task1 = new TimerTask(){
public void run(){
/*
* I tried to update the text here but since this is not the UI thread, it does not allow to do so.
*/
//Calls change() method
m.invoke(hd, (Object[])null);
}
};
t.schedule(task1, 1000);
}
public void change()
{
//Appends a "1" to the TextView
TextView t = (TextView)findViewById(R.id.textView1);
t.setText(t.getText() + "1");
}
//Event handler of another button which updates the text directly by appending "2".
//This works fine unless I click the first button.
public void onRefresh1(View view)
{
TextView t = (TextView)findViewById(R.id.textView1);
t.setText(t.getText() + "2");
}
}
Consider all Exceptions be handled.
On first click, m.invoke gives InvocationTargetException. But it calls the method change() on successive invokes without any Exceptions(verified by logging). But it does not update the text. Where am I wrong?
Also, I see in the debugger that it creates a new Thread every time I click the button. That is fine. But why isn't it removing the previous Threads though their execution has been completed?
Do something like this
public void onRefresh1(View v) {
// You can have this in a field not to find it every time
final EditText t = (EditText) findViewById(R.id.textView1);
t.postDelayed(new Runnable() {
#Override
public void run() {
t.append("1");
}
}, 1000);
}
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
// Update UI
}
}, 1000);
implement this on button click
UPDATE:
There are some other answers. dtmilano suggested another solution which is almost same to mine except he is calling the postDelayed method of View class and In my answer I used postDelayed method of handler class.
from the api reference of android the postDelayed method of Handler says
The runnable will be run on the thread to which this handler is
attached.
and the postDelayed method of View says
The runnable will be run on the user interface thread.
This is the only difference between these two solution. in my answer instead of creating new Handler every time you can use any other handler instance. Then the runnable will be run on that thread where that specific handler is declared. And if the postDelayed of EditText is used the the runnable method will be run on the user Interface Thread.
Now the performance issue, both has the same performance (If anybody can prove me wrong with reference I will be happy)
That's looking awful convoluted - have you considered using CountDownTimer instead?
new CountDownTimer(1000, 1000) {
public void onTick(long millisUntilFinished) {
// no-op
}
public void onFinish() {
change();
}
}.start();
This should call change (and hence change the text) on the UI thread, avoiding reflection and threading errors.
Hi Use the following code for that. Hope this will help you .
new java.util.Timer().schedule(
new java.util.TimerTask() {
#Override
public void run() {
// your code here
}
},
1000
);
Have a look of this question also.
display data after every 10 seconds in Android
You can try with this also.
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
public void run() {
doStuff();
/*
* Now register it for running next time
*/
handler.postDelayed(this, 1000);
}
};
**EDIT 3**
Try with this once you are need to enable once (i mean if you put your code in yourmethod()== this will get automatically call 1 seconds once.
private Timer timer;
TimerTask refresher;
// Initialization code in onCreate or similar:
timer = new Timer();
refresher = new TimerTask() {
public void run() {
yourmethod();
};
};
// first event immediately, following after 1 seconds each
timer.scheduleAtFixedRate(refresher, 0,100);

How to change a TextView every second in Android

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/

Categories

Resources