I've got a Handler that runs a runnable task every 2 seconds. It runs a fairly intensive task (executes a shell command). Every time the I run handler.postDelayed(update, 2000); the user interface lags (for 2 seconds). How do I stop this lag?
I know there's lag because I have a dynamic interface, so I can move around a view and when the handler is run the interface becomes unresponsive for the 2 seconds.
Is there a way to go around this?
You are doing this work on the main UI thread, which is not acceptable for your user to have a good experience, as you have already identified.
You can instead create a new background thread for your handler to run any posted runnables on, which will take the least amount of code change:
mHandlerThread = new HandlerThread("YourHandlerThread");
mHandlerThread.start();
handler = new Handler(mHandlerThread.getLooper());
// Now post your runnable, as before
handler.postDelayed(update, 2000);
Just keep in mind that you cannot touch any UI elements from this thread, as that is not allowed by Android.
when the handler is run the interface becomes unresponsive for the 2 seconds
That means that you are doing two seconds' worth of work on the main application thread. Doing 2 milliseconds' worth of work is more appropriate.
The Runnable that is passed to postDelayed() has its run() method called on the main application thread. If this work will take more than a millisecond or two, you should be using something other than Handler and postDelayed() for your every-two-seconds work, such as a ScheduledExecutorService.
Handler is a way to run code on the UI Thread in Android.
If you don't need your code run on the UI Thread, you may want to consider just making your own Thread or using an Executor.
I've fixed it. Thanks to everyone notifying me that the Handler runs on the UI thread...
I've now run a separate thread to update the variable used in the handler task:
double p = 0;
public void z(){
Thread t = new Thread(){
#Override
public void run(){
p = a.b();
}
};
t.start();
}
Runnable y = new Runnable() {
#Override
public void run() {
z();
c.setText(String.valueOf(p));
d.setProgress(Float.valueOf(String.valueOf(p / 100)));
handler.postDelayed(this, 2000);
}
};`
Related
I'm running some webcam captures in a thread like so:
class Capture implements Runnable {
#Override
public void run() {
while(true){
//capture images
//sleep 5 seconds
}
}
//To actually start the capture
new Capture().run();
I'm doing this constantly, so I expect to still be able to perform UI functions like clicking on buttons while this is going on, but that's not the case. The x button on my JFrame is unresponsive, and same with other UI components.
Do I need to do something other than just using a separate thread? Doesn't seem to be working for me. Thanks
You have just implemented Runnable. You haven't started a Thread to do the job. Try this:
new Thread(new Capture()).start();
Also consider Timer class for such job.
This is happening because you are calling the run method but not starting the Thread when you do new Capture().run(); you are not even creating an instance of the Thread
Do I need to do something other than just using a separate thread?
yes, create and start the thread doing instead
new Thread(new Capture()).start();
I am trying to call GLFW.glfwPollEvents() in an asynchronous task that runs every tick (1/30th of a second in this case). This ticking timer effectively controls when every action in the app takes place.
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
#Override
public void run(){
//... more code ...
GLFW.glfwPollEvents();
//... more code ...
}
}, 33, 33);
But, this does not work because
This function may only be called from the main thread.
(from the documentation)
How can I call this on the main thread? When it isn't run on the main thread, the application crashes. I'm looking for something like
GLFW.dispatchMainThread(new GLFWRunnable(){
public void run(){
//...
}
});
This is possible to do in swing and awt by using
EventQueue.invokeLater(new Runnable(){
public void run(){
//...
}
});
But the same code doesn't work for GLFW.
How can I run a task on GLFW's main thread using LWJGL without using a while(true) loop on the main thread?
Since GLFW's main thread must be the same as the application's main thread (a limitation in GLFW; that's where it puts the OS message queue that is handled differently by swing/awt it seems - see my answer here) I would do things the other way around.
My main thread would have the forever loop (probably using glfwWaitEvents so as not to eat CPU time needlessly). I would then post any events that were app-specific to another thread that does the processing. Your message processing is now decoupled from your message receiving.
This way I don't have to wait for 1/30th of a second to get OS messages (it always frustrates me when an app lags opening a menu, registering a click, or arrowing through a list).
Your update thread can even sleep for 1/30th of a second (or less, sleeping again if not enough time has passed or not if a second high-priority queue needs work) and only wake up to check for queued events as an extremely simple scheduling method.
The answer I linked to above has a link to an LWJGL Multithreaded Demo that does something very similar. Their renderLoop would be your updateLoop and would draw from a queue and do processing before going to sleep again instead of updating and presenting graphics.
This is weird: When I start a schedule using timer.scheduleAtFixedRate, it takes a long time and then throws a OutOfMemoryError: Java heap space, but if I just called the timerTask.run() method in the main thread, it works just fine.
The method I called is not open-source, so I cannot just paste it out. But could anyone help me analyse some possibilities on this scenario. What is the difference between running it in a timerTask thread and running it in the main thread?
Thanks a lot!
The code snippet is just as simple as follows:
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
AModule.load(tmpFilePath); //not open-source
}
}
timer.scheduleAtFixedRate(task, new Date(), 1*60*1000); //OOME
//If I change the upper code to:
task.run() //it just works fine
The AWT timer runs in its own thread. If by "main thread" you mean the UI thread, or event queue, then:
Perhaps your closed source code is accessing Swing some how. Swing components should only be accessed from the event thread. You might have trouble when running in the timer yet always run fine in your main thread.
If that's the case you're lucky. Calling Swing from another thread usually works. I never have problems till I'm demoing or the client tries to use it. And the problems are often not repeatable.
I have a question regarding calling methods after a certain amount of delay.
I want to call a Java method exampleFunction() after a delay of about 10 seconds. I have looked for solutions online and have come across the ScheduledThreadPoolExecutor(). So I have used this, but the thing is, once the function runs after 10 seconds, it doesn't exit from the thread. Is there any way I can exit from the thread? Or can I run the ScheduledThreadPoolExecutor() on the current thread instead of creating a new thread?
class Test {
...
exampleFunction();
...
public void exampleFunction() {
ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
exec.schedule(new Runnable() {
public void run() {
...do something here...
}
}, 10, TimeUnit.SECONDS);
}
}
So is there any way I can exit this thread after exampleFunction runs after a delay of 10 seconds? Or can I have the ScheduledThreadPoolExecutor use the current thread instead of creating a new one?
Or is there another way I can approach this problem? I want to be able to run exampleFunction() after 10 seconds on the current thread, instead of creating a new thread.
Edit: I think it may not be a thread issue. I'm still trying to figure out the problem is. Thanks everyone for your suggestions and advice.
EDIT: Can I pass an argument to exampleFunction() and then use it inside public void run()?
I believe your problem may be that you are not shutting down the executor after your submit the job to it.
exec.schedule(...);
exec.shutdown();
The jobs that have been submitted will continue to run but you have to shutdown the service after you've submitted the last job to it.
Based on all the comments and confusion, any answer is just a guess.
What I think you want:
The UI thread to invoke exampleFunction
'exampleFunction` to schedule a task for 10 seconds later and return immediately
In 10 seconds time, to have the run method be invoked on the UI thread
In Swing, this is done by using SwingUtilities.invokeLater.
ExampleFunction would look like this:
public void exampleFunction() {
new Thread() {
public void run() {
TimeUnit.SECONDS.sleep(10); //Will need a try/catch
SwingUtilities.invokeLater(new Runnable() {
public void run() {
...do something here...
}
});
}
}.start();
}
Note: SwingUtilities.invokeAndWait could also be used.
Note 2: Although not usually advised, a simple Thread here is simpler than making a new Thread pool.
Thread.sleep can be used if merely wishing to making the current thread block. If you do use a pooled executor, make sure to use it as a pooled executor - not one per (new) thread. To "exit" from a thread, just let execution run out of run. If using Swing, use the EDT.
I have read that the main UI thread in android should not call sleep.
However, my application needs to :
call thread1 from main UI thread
call thread2 from main UI thread.
Use the output (2 images) of the 2 thread, add them and then display them.
I am using Thread.sleep() so that the main thread waits thread1 and thread2 till they are done. However mImageview.setbitmap is not working after i call it in the main thread (after sleep).
can u pls advise me how i should do this?
Multithreading in Android should be done Asynchronously. For this purpose, you should use the AsyncTask-class.
For your case, you would for example create a taks to load (or process) those two images. While the process is running (in another thread, off the UI-thread), you could show a ProgressBar that shows to the user that your application is currently busy (this is then done on the UI-thread).
When the task has finished, you get the results (your two images) from the task, hide the progress-bar and show everything to the user.
Despite the fact that a non-reacting GUI always has the feeling that your application frooze, if a UI-Thread is blocked for more then 5 seconds (which is a looong time), your application will be Force-closed as it is not "reacting" (and an ANR will be raised).
It's not just Thread.Sleep(). In the GUI thread, do whatever you need to to start the two threads/tasks/whatever and then exit the event handler.
Do not wait in GUI event-handlers! Not in Java, C++, C, Delphi, anything. Use an async task, or a Handler, and signal to the GUI thread. Thread 1 signals that it is done, thread 2 signals that it is done. In either case check to see if the data has been returned by the other thread. If it has, you have both sets of returned data and so you can add them and display them.
Do not wait in GUI event-handlers.
You can simply use Threads And Handlers for this purpose.
Here is a small demo for this,
Create a Handler in your onCreate like this,
Drawable d=null;
Handler handler=new Handler()
{
public void handleMesaage(Message msg)
{
if(msg.what==0)
{
imageView.setBackgroundDrawable(d);
}
}
};
And now call Your Thread like this,
Thread t=new Thread(new Runnable()
{
#Override
public void run() {
InputStream is = (InputStream) new URL(url).getContent();
d = Drawable.createFromStream(is, "src name");
handler.sendEmptyMessage(0);
}
});t.start();
I would suggest to use the ExecutorService. Here is how
Create the two image loading activities as Runnable tasks.
Execute them with the ExecutorService.
Use ExecutorService.awaitTermination(); for the main thread to wait for Runnable tasks to complete. Its documentation reads
Blocks until all tasks have completed execution after a shutdown
request, or the timeout occurs, or the current thread is interrupted,
whichever happens first.
This is the Asynch way of doing it and I guess should be preferred.