My activity shares the Handler around the app to worker threads in a service. Threads use the handler to sendMessage() Acitivity use handleMessage() to handle them
The Activity goes to background Android kills it and when come back it is started again.
The Activity calls Service's static method to check the last status (as to what the last sendMessage() was ) of threads in service. So that it can init its UI status based on the service thread's last message
Message m = SyncService.lastSyncMessage();
For some reason the bundle inside the message m is sometimes valid (contains key/value pairs) and sometimes key/values are absent
The code on the side of thread which forwards message to UI via handler looks like this.
Handler uiHandler = UIGlobals.getHandler();
Message msg = uiHandler.obtainMessage();
Bundle msgBundle = new Bundle();
msgBundle.putInt("status", syncEv.status.ordinal());
msgBundle.putString("param", syncEv.param);
msg.setData(msgBundle);
if(uiHandler.sendMessage(msg))
{
lastMessage = msg;
}
The static method in the service
public static Message lastSyncMessage()
{
return lastMessage;
}
The Message object came from pool. When Activity is gone, handler's callback points to unavailable object. Android seem to re-prepare the Message ref for usage in the Pool,
To solve it I stored the Actual Event in the static member rather than the Message.
Related
I have defined the required class and overridden the onTagDiscovered method to handle NFC events. Works as expected.
public class NfcReader implements NfcAdapter.ReaderCallback {
#Override
public void onTagDiscovered(Tag tag) {
// handle tag data
}
}
What I've noticed, and can't find any documents that explain this, this callback is run in its own thread. I'm not sure when this thread is created but if in onTagDiscovered I do:
Log.i(TAG, "Get ID: " + Thread.currentThread().getId());
It will return an arbitrary ID each time a new tag is discovered. I assume this means it's running in a separate thread that is not the main thread.
Can anyone shed some light on the lifecycle of this thread?
My issue is I have some asynchronous tasks that I need to do between the messages I need to send to the tag. What I previously did (in a modified example project) was start a looper and message handler in the onTagDiscovered and sent messages to indicate when my asynchronous tasks were done and tag communication could continue.
public class NfcReader implements NfcAdapter.ReaderCallback {
private NfcV nfcvTag;
#Override
public void onTagDiscovered(Tag tag) {
// handle initial tag discovery
nfcvTag = NfcV.get(tag);
...
// initiate long running task that needs to be
// complete before we can continue talking to tag
...
if(Looper.myLooper() == null)
Looper.prepare();
mHandlerNfc = new Handler(Looper.myLooper()) {
#Override
public void handleMessage(Message msg) {
// process incoming messages
...
// continue NFC transcieve.
response = nfcvTag.transceive(cmd);
}
};
Looper.loop();
}
}
But for whatever reason, in this new project, I'm inconsistently getting errors saying I'm trying to send messages to a thread that no longer exists. Sometimes my messages go through and the handler gets them, sometimes they don't due to the thread not being around anymore.
Shouldn't the looper keep the thread alive?
Is there a better way to do this than to try and keep this thread alive? I did this assuming that it was what was needed to continue to transceive with a tag. Would it be possible to continue a transceive from a previously discovered tag from the main thread?
The callback thread is supposed to be short-lived. If you want to do longer operations, you should use another thread instead. You could, for instance, spawn a separate worker thread, post some action to the UI thread or to some other looper thread/executor that you created before the onTagDiscovered() is invoked.
I use a bound service to run cpu-intensive tasks in the background as recommended to avoid ANR. The client application sends messages to the bound service that handles them in the handleMessage() method of its Handler.
As some requests can take very long to answer, I want to offer the user the ability to "abort/abandon" a running request. However, I can not destroy the service because I need to keep its context for future requests. Sending an "abort" request from the application will be queued and handled by the service after it will have completed its current task, which is obviously too late and not implementing the requested functionality.
Is there a way for the client application to kill a running request without killing the service and losing its associated context?
* EDIT *
Thanks to Mario's recommendation and this link, here is the solution I have implemented:
ExecutorService executor = Executors.newFixedThreadPool(1);
private volatile static Future<?> BackgroundSolveFuture = null;
public class IncomingHandler extends Handler {
#Override
public void handleMessage(Message message) {
msg = message;
msgArg1 = message.arg1;
msgWhat = message.what;
if (message.replyTo != null) {
mClient = new Messenger(message.replyTo.getBinder());
}
switch (msgWhat) {
case MSG_ABORT:
BackgroundSolveFuture = null;
break;
case MSG_SOLVE:case MSG_ANALYZE:case MSG_SUGGEST:case MSG_STEP:
BackgroundSolveFuture = executor.submit(new Runnable() {
public void run() {
solve();
}
});
break;
...
Inside solve() I regularly check whether BackgroundSolveFuture has been set to null:
if (BackgroundSolveFuture == null) {
undoAction(true);
return;
}
yes.
your idea with sending abort message is ok.
you just need create new thread for each task.
For example you have two types of messages DOIT and ABORT, when you get message DOIT you create and run new thread with task, you can even keep reference to this thread.
This allows finish quickly a handleMessage() method.
Then new message come: ABORT, you have reference to thread and you can interrupt the thread/task.
handler is passed as this:
public void getUserYouTubeFeed() {
new Thread(new GetYouTubeUserVideosTask(responseHandler, username, i)).start();
}
Handler responseHandler = new Handler() {
public void handleMessage(Message msg) {
populateListWithVideos(msg);
}
};
and in the run method of thread
public class GetYouTubeUserVideosTask implements Runnable {
// A handler that will be notified when the task is finished
private final Handler replyTo;
public GetYouTubeUserVideosTask(Handler replyTo, String username, int frag) {
this.replyTo = replyTo;
}
#Override
public void run() {
// some code here
Library lib = new Library(username, videos);
// Pack the Library into the bundle to send back to the Activity
Bundle data = new Bundle();
data.putSerializable(LIBRARY, lib);
// Send the Bundle of data (our Library) back to the handler (our Activity)
//Message msg = Message.obtain();
Message msg = new Message();
msg.setData(data);
// getting null pointer exception here
replyTo.sendMessage(msg);
}
}
had this same issue. I wanted to create a client thread class in a separate .java file. In order to work, however, it would need to know the handler of the main UI thread. Unfortunately, since Java does not support pointers, passing the handler from the UI to your custom class and assigning it:
public GetYouTubeUserVideosTask(Handler replyTo, String username, int frag) {
this.replyTo = replyTo;
}
simply creates a copy of the handler and associates it with your thread (not a link to the main UI handler).
Messages sent to a thread (or main UI) require a Looper which dispatches the messages from the message queue, which then can be processed by the message handler. The main UI has a message loop associated with it by default, accessed through Looper.getMainLooper() and, therefore, you can simply create a handler in your main UI which threads can post to. Threads, however, don't have a message loop by default, so when you try to call:
replyTo.sendMessage(msg); // NullPointerException
you are actually trying to send the message to your new thread's handler which doesn't have a message loop associated with it causing the exception.
You can look at the Looper documentation to see how to create a message loop for you thread, but remember: the looper and the handler in your thread ONLY handle messages TO your thread (this is how you can communicate between threads).
I am trying to make use of HandlerThread in android and am ending up with either a situation in which the UI thread is not responding anymore, or a strange IllegalStateException. I want to give you a minimal example of my problem.
I have a class DataManager which instantiates a worker thread on creation:
public class DataManager
{
private final HandlerThread loaderThread = new HandlerThread( "Worker" );
private final Producer loader;
Inside of this class I have my Handler defined:
private static class Producer extends Handler
{
public Producer( Looper looper )
{
super( looper );
}
#Override
public void handleMessage( Message msg )
{
msg.recycle();
}
}
The constructor of my DataManager runs the worker thread and associates the handler with the thread's looper:
public DataManager()
{
loaderThread.start();
this.loader = new Producer( loaderThread.getLooper() );
}
Before DataManager is destroyed, it stops the thread and waits for it to finish. Actually I believe this part is not relevant to my problem, because my DataManager instance is definitely alive all the time:
#Override
protected void finalize() throws Throwable
{
loaderThread.quit();
loaderThread.join();
super.finalize();
}
Finally, I have doSomething method, which simply posts a message to the worker thread:
public void doSomething()
{
Message msg = Message.obtain();
loader.sendMessage( msg );
}
Now I'm instantiating the DataManager from inside of a custom view on the UI thread. When the view is about to paint itself using onDraw it calls doSomething on the DataManager. The further behavior depends on whether an AsyncTask is currently running in background or not:
If it is running, than the UI thread is becoming unresponsive form this moment on.
Otherwise, I get an IllegalStateException, thrown from within a subroutine of Looper.loop of the UI thread, saying:
IllegalStateException: The specified message queue synchronization barrier token has not been posted or has already been removed.
Google gives absolutely no results for this message. I've been reading documentation and searching for similar problems for a few hours now and still have no idea what I might be doing wrong. Any ideas?
Got it. Obsiously the situation about recycling messages is this:
If you send it to a handler, the handler/looper will recycle it for you.
So one must not recycle the message within handleMessage.
I have an Android Service that does some background processing on an image using a separate Thread. If an error occurs in this Service or even worse in the thread, what is the best practice to inform the launching Activity of the problem and allow the application to recover to a stable state (i.e. the state it was in before launching the service).
From within the Service I could post a Toast or a Notification, but that doesn't help me. I would like to inform the user about the problem but at the same time recover the application to a stable state.
In case anyone searches for this I will explain what I ended up doing.
Inside the service I added a private class that extends AsyncTask. This is were all the processing is done.
In this class I have a private variable 'exception'. The content of the doInBackground method is surrounded by a try/catch and any exception catched is stored in 'exception'. In the onPostExecute method I check if 'exception' is set and if that is the case I send a broadcast PROCESSING_ERROR with the exception details so that the calling Activity will be informed.
If you don't know what AsyncTask, doInBackground or onPostExecute are you should read following:
http://developer.android.com/guide/components/processes-and-threads.html#AsyncTask
http://developer.android.com/reference/android/os/AsyncTask.html
You can use a Messenger to pass information between the service and main application.
Define a messenger in your main activity, as follow:
private Messenger = mMessengerCliente = new Messenger(new IncomingHandler());
/**
* Handler of incoming messages from service.
*/
private class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_1:
//actions to perform on message type 1
break;
case MSG_2:
//actions to perform on message type 2
break;
default:
super.handleMessage(msg);
}
}
}
Pass the Messenger object as a Extra or when binding to your service.
In your service, recover the Messenger object and use it to communicate back:
mMsgClientMain = (Messenger)intent.getExtras().get(EXTRA_MESSENGER);
Message msg = Message.obtain(null, MSG_1, arg1, arg2);
msg.replyTo=reply_to; // if you need to have bidirectional communication, pass here the service messenger object
mMsgClientMain.send(msg);
Regards.