I have some code which sets a timer, but if a user sets the timer while it is in use I need to remove the runnable which runs the timer and start it again. But when no handler callback runnable exists and this code is called it crashes my application. So I need to check if a handler is running, if so then end it and restart it, but looking through the documentation and other Stackoverflow questions, I cannot see if this is possible.
Here is my code, I have commented around the code which should only be executed if a handler runnable exists:
submitTimer.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
String s = timer.getText().toString();
if(!s.equals(""))
{
//I NEED TO CHECK A RUNNABLE HANDLER EXISTS AND IF SO THEN RUN THIS CODE, IF NOT IGNORE THIS CODE
Map.handler.removeCallbacks(Map.getRunnable());
Map.runnable.run();
//I NEED TO CHECK A RUNNABLE HANDLER EXISTS AND IF SO THEN RUN THIS CODE, IF NOT IGNORE THIS CODE
int l = Integer.parseInt(s);
Map.timerinmins = l;
timer.setHint("Current Timer is "+Map.timerinmins);
timer.setText("");
Toast.makeText(Preferences.this, "Timer is set!", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(Preferences.this, "No value was entered", Toast.LENGTH_SHORT).show();
}
}
});
Can anyone help me figure out a way of checking the handlers current state?
Referring to Jay Snayder's reply:
Since there are cases in which Map.handler.hasMessages(CALLBACK_PRESENT_INTEGER) returns false even though Map.handler.sendEmptyMessage(CALLBACK_PRESENT_INTEGER) has been called (happened to me, too), it may be safer to use a boolean to determine whether the handler has callbacks or not.
So, the code logic is something very simple like this:
...
boolean callbackPresent = false;
...
if(!callbackPresent) {
// do what you have to do in case the handler doesn't have callbacks
// right before adding a callback to the handler, call:
callbackPresent = true;
} else {
// do what you have to do in case the handler has callbacks
// right before removing the callbacks from the handler, call:
callbackPresent = false;
}
...
I'm using this solution in an app and it's working perfectly.
If you want you could send an empty message when you first put a callback in and then check for that message in the handler. This empty message could represent that there is a callback present. Removing that message later could then be used similarly to see if the callback is still there. Don't have a relevant situation such as this to go from, but thought that I would at least try and share a possibility.
...
Map.handler.sendEmptyMessage(CALLBACK_PRESENT_INTEGER);
...
if(Map.handler.hasMessages(CALLBACK_PRESENT_INTEGER)
...
Map.handler.removeMessage(CALLBACK_PRESENT_INTEGER);
...
This is probably not ideal, but could be a potential solution if you have access to your handler from the point where your callback is used. Not sure if there is a direct way to find out.
Handler class has a method
boolean hasCallbacks(#NonNull Runnable runnable)
to check if there are any pending posts of messages with callback runnable in the message queue.
It exists since SDK 16 but it's hidden prior to SDK 29.
HandlerCompat class has the compatible version of this method for SDKs 16+:
boolean hasCallbacks(handler, runnable)
Related
I have an activity which is using a postDelayed call:
public class SplashActivity extends Activity {
private Handler handler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(...);
handler.postDelayed(new Runnable() {
public void run() { finish(); }
}, 3000L);
}
}
This runs at app startup, and i need to navigate it and my login screen. However, the UIController's loopMainThreadUntilIdle doesn't seem to take the underlying MessageQueue in the handler into account. As such, this action finishes immediately while there is still messages in the queue.
onView(withId(R.id.splash_screen)).perform(new ViewAction() {
#Override
public Matcher<View> getConstraints() {
return isAssignableFrom(View.class);
}
#Override
public String getDescription() {
return "";
}
#Override
public void perform(final UiController uiController, final View view) {
uiController.loopMainThreadUntilIdle();
}
});
I've been unable to figure out how to block until the queue is drained. Android itself is preventing me from doing a lot of things i would have tried (like extending Handler and overriding the postDelayed method, etc...)
Anyone have any suggestions on how to handle postDelayed?
I'd rather avoid uiController.loopMainThreadForAtLeast, which seems hacky (like a Thread.sleep would)
When Espresso waits, it actually does take in account MessageQueue, but in a different way from what you think. To be idle, the queue must either be empty, or have tasks to be run in more than 15 milliseconds from now.
You can check the code yourself, especially the method loopUntil() in UiControllerImpl.java and the file QueueInterrogator.java. In the latter file you will also find the logic of how Espresso checks the MessageQueue (method determineQueueState()).
Now, how to solve your problem? There are many ways:
Use AsyncTask instead of Handler, sleeping on the background thread and executing actions onPostExecute(). This does the trick because Espresso will wait for AsyncTask to finish, but you might not like the overhead of another thread.
Sleep in your test code, but you don't like that approach already.
Write your custom IdlingResource: this is a general mechanism to let Espresso know when something is idle so that it can run actions and assertions. For this approach you could:
Use the class CountingIdlingResource that comes with Espresso
Call increment() when you post your runnable and decrement() inside the runnable after your logic has run
Register your IdlingResource in the test setup and unregister it in the tear down
See also: docs and sample, another sample
As far as I know there is no wait for activity to finish method in espresso. You could implement your own version of waitForCondition, something robotium has. That way you'll only wait for as long as is needed and you can detect issues with your activity not finishing.
You'd basically poll your condition every x ms, something like.
while (!conditionIsMet() && currentTime < timeOut){
sleep(100);
}
boolean conditionIsMet() {
return "espresso check for if your splash view exists";
}
I've been writing android apps for some months now, and I'm at the point where I'm building an actual needed app.
As I want that to work nice and fast, I made a Workerthread to do all kinds of tasks in the background while the UI can...build up and work and stuff.
It's based on the Android Studio Drawer app blueprint.
In Main.onCreate I got my operator=new Operator(), which extends Thread.
Now, when loading a new Fragment, it sometimes calls MainActivity.operator.someMethod() (I made operator static so I can use it from anywhere), and after some time I realized, the only tasks actually running in background are those in the operators run() method and an Asynctask my login Fragment runs. Everything else the UI waits for to complete and therefore gets executed by the UI thread.
So I thought: no problem! My operator gets a handler which is built in run(), and I change those tasks:
public void run() {
Looper.prepare(); //Android crashed and said I had to call this
OpHandler = new Handler();
LoadLoginData();
[...Load up some Arrays with hardcoded stuff and compute for later use...]
}
public void LoadLoginData() {
OpHandler.post(LoadLoginDataRunnable);
}
private Runnable LoadLoginDataRunnable = new Runnable() {
#Override
public void run() {
if(sharedPreferences==null)
sharedPreferences= PreferenceManager.getDefaultSharedPreferences(context);
sessionID=sharedPreferences.getString("sessionID", null);
if(sessionID!=null) {
postenID = sharedPreferences.getString("postenID", PID_STANDARD);
postenName = sharedPreferences.getString("postenName", PID_STANDARD);
context.QuickToast(sessionID, postenName, postenID);
}
}
};
context is my MainActivity, I gave the operator a reference so I could send Toasts for Debugging.
But now, the Runnables seem to not run or complete, any Log.e or Log.d stuff doesn't arrive in the console.
After some googeling and stackoverflowing, everyone is just always explaining what the difference is between Handlers, Asynctask, and Threads. And the multitask examples always only show something like new Thread(new Runnable{run(task1)}).start times 3 with different tasks.
And so became my big question:
How to correctly, over a longer time (~lifecycle of the MainActivity), with different tasks, use a background thread?
Edit: to clarify, I would also like a direct solution to my special problem.
Edit 2: after reading nikis comment (thank you), the simple answer seems to be "use HandlerThread instead of thread". Will try that as soon as I get home.
Trying a HandlerThread now. It seems my OpHandler, initialized in run(), gets destroyed or something after run() has finished, not sure whats up here (this is btw another mystery of the kind I hoped would get answered here). I get a NullpointerException as soon as I try to use it after run() has finished.
Make your worker thread own a queue of tasks. In the run() method, just pop a task from the queue and execute it. If the queue is empty, wait for it to fill.
class Operator extends Thread
{
private Deque<Runnable> tasks;
private boolean hasToStop=false;
void run()
{
boolean stop=false;
while(!stop)
{
sychronized(this)
{
stop=hasToStop;
}
Runnable task=null;
synchronized(tasks)
{
if(!tasks.isEmpty())
task=tasks.poll();
}
if(task!=null)
task.run();
}
}
void addTask(Runnable task)
{
synchronized(tasks)
{
tasks.add(task);
}
}
public synchronized void stop()
{
hasToStop=true;
}
}
What if I have handler.postDelayed thread already under execution and I need to cancel it?
I do this to cancel postDelays, per the Android: removeCallbacks removes any pending posts of Runnable r that are in the message queue.
handler.removeCallbacks(runnableRunner);
or use to remove all messages and callbacks
handler.removeCallbacksAndMessages(null);
If you don't want to keep a reference of the runnable, you could simply call:
handler.removeCallbacksAndMessages(null);
The official documentation says:
... If token is null, all callbacks and messages will be removed.
I had an instance where I needed to cancel a postDelayed while it was in the process of running, so the code above didn't work for my example. What I ended up doing was having a boolean check inside of my Runnable so when the code was executed after the given time it would either fire based on what the boolean value at the time was and that worked for me.
public static Runnable getCountRunnable() {
if (countRunnable == null) {
countRunnable = new Runnable() {
#Override
public void run() {
if (hasCountStopped)
getToast("The count has stopped");
}
};
}
return countRunnable;
}
FYI - in my activity destroy I do use code suggested by the other developers, handler.removecallback and handler = null; to cancel out the handle just to keep the code clean and make sure everything will be removed.
I have a problem. I'm using a FileObserver, which moves new files from the watched directories to another, former specified directory. In my thoughts there should be shown a toast message that says 'File xy has been moved', as long as the observer watches the directory, also if the applications is only in the background. But I didn't get it working.
It always tells me, that there is a RuntimeException, and that it cannot been done without calling Looper.prepare().
05-11 13:21:28.484:
WARN/System.err(3397):
java.lang.RuntimeException: Can't
create handler inside thread that has
not called Looper.prepare()
I tried the way with using an handler too, but I also didn't get it to work.
Has someone else an idea?
Thanks in advance.
Best regards, Tobi
Before your Toast statement add the following :
runOnUiThread(new Runnable() {
public void run()
{
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
}
});
This will make it run on UI thread.
Hope this helps.
Obviously, your FileObserver runs(or is) another thread. You can not modify the UI from non-UI thread. Pass a Handler to your FileObserver and send messages from it. Read about Handlers.
What are you using for the context of the Toast message? That will have to have a way to display something on the screen.
Put the following code in your class:
// Need handler for callbacks to UI Threads
// For background operations
final Handler mHandler = new Handler();
// Create Runnable for posting results
final Runnable mUpdateResults = new Runnable() {
public void run() {
// Show the toast here.
}
};
and in your fileobserver's thread call place following fragment of code:
mHandler.post(mUpdateResults);
and don't use the getApplicationContext() instead try YourClassPhysicalName.java for the context of the Toast.
I'm trying to create a simple multiplayer game. There's a WorkerService which is supposed to handle all network communication and all interaction between this service and my Activities is done with AIDL. I think this is a standard approach - to enable two way interaction I use also an IWorkerCallback interface (also AIDL).
The problem is that callbacks have to change things in UI which may be done only in UI thread. I've created a Handler (in UI thread) and believed that this is an obvious solution. But, surprisingly, it's not working.
My LoungeActivity calls startServer() method of IWorker interface. Corresponding method of my WorkerService does some job and makes a callback - this works fine. Then WorkerService spawns a new thread and callback from this thread results in a bad Exception being thrown:
Can't create handler inside thread that has not called Looper.prepare()
Here's some code to make it clear:
startServer() implementation:
private void startServerImpl(String name, float latStart, float latEnd,
float lonStart, float lonEnd)
{
// some instructions here
// this works fine:
callback.notifySocketCreated();
// my naughty thread:
new ServerThread().start();
// some instructions here
}
ServerThread code:
private class ServerThread extends Thread {
#Override
public void run()
{
//some instructions here
// this call will cause an error
callback.notifyGameRegistered();
}
}
Every method from callback looks like that:
public void notifyGameRegistered() throws RemoteException
{
handler.dispatchMessage(handler.obtainMessage(CALLBACK_GAME_REGISTERED));
}
In Handler's handleMessage() method I'm doing a simple switch(msg.what) and in every case there's a simple UI modification (showing a Toast, changing Text, etc.).
I have no idea why is this Exception thrown.. I've managed to fix it by packing code into a Runnable and calling runOnUiThread() but it still makes me curious - shouldn't a Handler always run in thread that created it? Or maybe I'm doing something wrong?
I know this is a bit late - but the problem is that you called dispatchMessage().
The correct method is sendMessage().
dispatchMessage() will call handleMessage() on the same thread.
I am guessing the problem is not your Handler being on the wrong thread - but that the UI is trying to create a Handler somewhere in yout onHandle() method. Because onHandle() is called on the wrong thread, you are getting an exception.
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg)
{
if (msg.callback != null) {
handleCallback(msg);
} else {
handleMessage(msg);
}
}
You have to somehow call the offending function from the main thread.
The function that changes the UI should be in the activity that owns the UI.
This link should help you out:
http://android-developers.blogspot.com/2009/05/painless-threading.html