Im programming a Music Player with a Seekbar.To manage it im using an Handler with a Runnable which does it update. Somehow it is lagging my UI . How can I stop this lag ?
OnCreate:
mHandler = new Handler();
When Im playing a Song:
public static void updateProgressBar() {
mHandler.postDelayed(mUpdateTimeTask, 100);
}
My Runnable:
private static Runnable mUpdateTimeTask = new Runnable() {
public void run() {
try {
long totalDuration = songService.getTotalDuration();
int currentDuration = songService.getCurrentDuration();
// Displaying Total Duration time
player_time_length.setText(""+utils.milliSecondsToTimer(totalDuration));
// Displaying time completed playing
player_time_current.setText(""+utils.milliSecondsToTimer(currentDuration));
// Updating progress bar
int progress = (int)(utils.getProgressPercentage(currentDuration, totalDuration));
SB_song.setProgress(currentDuration);
// Running this thread after 100 milliseconds
mHandler.postDelayed(this, 100);
}catch(Exception e){}
}
};
How can I prevent this lagg in my App ?
The lag is because the Runnable is executing in the UI Thread. To reduce or remove the lag, you have to reduce the amount of work you do inside the Runnable.
One thing that you can do is to remove long totalDuration = songService.getTotalDuration(); from the Runnable and instead, put it outside, like I do in my music player.
I could add more to this answer if you included the "utils" methods that you're using to convert milliseconds to human readable time.
I would say its because its running on the UI Thread which laggs. But you have to use UI elements so another Thread is not possible .. Im right ?
Make sure the text view is not set to wrap_content. That will trigger a layout pass each time you call setText.
Related
I'm building app for making tests. I have a scene made in SceneBuilder. There is a ImageView under image Label with Question and 3 buttons "A", "B", "C", Texts to buttons and question's Label are taken from DataBase, if you click answer new Image, question and asnwers are loading, everything works, but i want to add a Timer in the corner. When image and question show on the screen there will be "Time to read the question" and countdown from 10 to 0, and then "Time to answer" and again countdown from 10 to 0. If timer ends and there is no answer question and image will change automaticly. But the problem is that, i can do the timer, its counting down, and after this time it change the question but I dont know how to put it into Label. If inside Timer I do something like seconds-- label.setText(seconds) there is no error but when I start app there is a lot of exceptions. Can you help me how I can put this variable which is decrementing in timer after each second to Label ?
public void setTimer() {
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
if(interval > 0)
{
timeElapsed.setText("Time to read the question: "+interval);
System.out.println(interval);
interval--;
}
else
timer.cancel();
}
}, 1000,1000);
}
Now i have something like this, in console everything is working there is a countdown from 10 to 0 but no effect in scene.
And errors:
Exception in thread "Timer-0" java.lang.IllegalStateException: Not on FX application thread; currentThread = Timer-0
at javafx.graphics/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:291)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:493)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
at javafx.controls/javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:271)
at javafx.controls/javafx.scene.control.skin.LabeledSkinBase.lambda$new$11(LabeledSkinBase.java:219)
at javafx.controls/com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler.lambda$new$1(LambdaMultiplePropertyChangeListenerHandler.java:49)
at javafx.base/javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
at javafx.base/com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)
at javafx.base/com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
at javafx.base/javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:104)
at javafx.base/javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:111)
at javafx.base/javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:145)
at javafx.base/javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:50)
at javafx.base/javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
at javafx.controls/javafx.scene.control.Labeled.setText(Labeled.java:147)
at gui.controller.StudentTestYesNoController$1.run(StudentTestYesNoController.java:40)
at java.base/java.util.TimerThread.mainLoop(Timer.java:556)
at java.base/java.util.TimerThread.run(Timer.java:506)
The problem is that you are trying to change the UI from a thread other than the application.
This should solve the problems with your current implementation
public void setTimer() {
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
if(interval > 0)
{
Platform.runLater(() -> timeElapsed.setText("Time to read the question: "+interval));
System.out.println(interval);
interval--;
}
else
timer.cancel();
}
}, 1000,1000);
}
Also, you can take a look at something specific about the javafx - Timeline
JavaFX periodic background task
Adding on to what mr mcwolf said above, i think you need to set
Platform.setImplicitExit(false);
before Platform.runLater(); so that it will run every time the task is called.
I'm building a simple timer that will display the milliseconds as well. Something like
Hours:Minutes:Seconds:Milliseconds
I'm updating the TextView that displays the timer every millisecond, inside a Handler that calls itself every 1 millisecond:
Handler timerHandler = new Handler();
Runnable timerRunnable = new Runnable() {
#Override
public void run() {
//calculate new time
...
//update TextView
...
timerHandler.postDelayed(this, 1);
}
};
My question is, will calling a Handler every 1 millisecond significantly affect the battery usage of the app?
Is there any better approach (performance-wise) other than using a Handler?
I've checked multiple posts of timers and stopwatches implementations but they will either not display the milliseconds or they won't discuss about performance issues.
I am trying to write a simple little thing that stops an audio file from being played when the user "interrupts" it by talking to the phone. I'm using the SoundMeter class that this person wrote to get the maxAmplitude of the microphone. I've set a threshold to 800 and want to test the return of the microphone amplitude against it constantly (every 50ms).
For some reason, it's hit or miss when I get the "interrupt" to show up and when the audio stops. I'd like for the interrupt to be showing the entire time the amplitude is above 800.
handler = new Handler();
final Runnable r = new Runnable() {
#Override
public void run() {
mic_amplitude.setText(""+mSoundMeter.getAmplitude());
if(mSoundMeter.getAmplitude() > threshold) {
Log.d("INTERRUPT", "INTERRUPT");
interrupt.setText("Interrupt");
mBackgroundAudio.pause(mp);
audioButton.setText("Resume Audio");
} else {
interrupt.setText("");
}
handler.postDelayed(this, 50);
}
};
handler.postDelayed(r, 50);
While viewing this, I'm able to see the amplitude at a steady 40-50 while there is no background noise and peaks of 1200-1500 when talking quietly. I'd like the interrupt to show anytime above 800, but it's currently only showing up intermittently.
Ok, I figured it out. I tested what my amplitude was by logging the amp along with the interrupt and I saw I was getting 0. I realized I had been testing on a new amplitude (other than the amp I was showing), so I assigned the amplitude to a variable and used the variable everywhere else.
here's my outcome:
handler = new Handler();
final Runnable r = new Runnable() {
#Override
public void run() {
mAmplitude = mSoundMeter.getAmplitude();
mic_amplitude.setText(""+mAmplitude);
if(mAmplitude > threshold) {
Log.d("INTERRUPT", "INTERRUPT " + mAmplitude);
interrupt.setText("Interrupt");
mBackgroundAudio.pause(mp);
audioButton.setText("Resume Audio");
} else {
interrupt.setText("");
}
handler.postDelayed(this, 100);
}
};
handler.postDelayed(r, 100);
This code is inside of a method and that method runs onStart for an android application. what this does is that it presses a button for 3 seconds which changes its color from blue to red and then goes back to its orginal state; then the next button does the same thing. This process is repeated for three different buttons. The code below works perfectly fine but is there a way to put all this code into one runabble object? I tried but a have problems with the .postDelayed because it would just press all the buttons at the same time instead of each button turn off and on at the given time.
The reason why I want this is because I created a random generator class that builds random patterns for Buttons class on android and I put that in a 2d array called list which is of type Button. however when I put something like list[x][y] and int x and int y are outside of r or r1 which are Runnable I get an error that says that only static variables can go inside of this new object. Is there any way I can use these variables so I can make a for loop or while loop to make something like list[x][y] instead of hardcore list[0][0] like how it is right now. Please help!!!
handler = new Handler();
r = new Runnable(){
public void run(){
list[0][0].setPressed(false);
}
};
handler.postDelayed(r, 3000);
r1 = new Runnable(){
public void run(){
list[0][1].setPressed(true);
}
};
handler.postDelayed(r1, 3000);
r2 = new Runnable(){
public void run(){
list[0][1].setPressed(false);
list[0][2].setPressed(true);
}
};
handler.postDelayed(r2, 6000);
r3 = new Runnable(){
public void run(){
list[0][2].setPressed(false);
}
};
handler.postDelayed(r3, 9000);
You can make correction with run method of runnable and make it generic method for all the buttons.
You can replace logic for the code
list[0][0].setPressed(false);
and use something like, In place of doing list[0][0] just take the common view (button) variable and initialize it with clicked button so your runnable will take action as per the view click and in this case you don't need to have 3 different runnable different for each button.
I'm trying to set the background color of an ImageView over a specified amount of time (pauses in between each switch of color) based on a certain criteria (such as string = "a" or "b"). My problem is I can't seem to make the entire application wait for one change until it moves on to the next, so to the user it just looks like the color starts then is immediately the last color.
I've tried CountDownTimers (which just continue to execute while the other timer is running), handlers (postDelayed(null, 5000)), Thread.Sleeps etc.
Here's an example of what I'm trying to do:
Set color on ImageView to red
sleep for 500ms
for(int i = 0; i < stringArray.length; i++){
if(stringCompare = "a") {
Set color on ImageView to blue
sleep for 500ms }
else if(stringCompare = "b") {
Set color on ImageView to blue
sleep for 1000ms
}
Set color on ImageView to red
sleep for 500ms
}
I know that's kind of crazy, but I've tried all the above methods I could think of with no success on actually making the program wait, but not completely stopping it.
Thanks.
For the past two decades, most GUIs have been based on event-driven models. Some, such as Android, use a single-threaded event-driven model. Your request to change a color does not take immediate effect; rather, it puts a message on a work queue, to be processed by the main application thread. That main application thread is also what is running your code, unless you specifically move that code to a background thread.
As a result, never sleep on the main application thread. Otherwise, that thread is tied up and cannot process any sort of GUI events, let alone your request to change the color.
postDelayed() (on View or Handler) will allow you to schedule code to be run in the future on the main application thread, after a delay, without tying up the main application thread during that delay.
Put this in your activity (make it an inner class):
private class ColorChanger extends Thread
{
final ImageView imageView;
public ColorChanger(ImageView imageView)
{
this.imageView = imageView;
}
private void changeColor(final int color)
{
YourActivity.this.runOnUiThread(new Runnable() {
public void run() {
// Set imageView to color here
}
});
}
#Override
public void run() {
try {
Thread.sleep(500);
changeColor(0xffff0000);
Thread.sleep(500);
changeColor(0xff0000ff);
// Etc.
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Then you can just do:
new ColorChanger(/*Your imageview*/).start();
Anywhere in your activity.
Create a separate thread that does your pause and run. From that thread post messages to the UI thread to change the color of its imageview.