Best practice for error handling in an Android Service - android

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.

Related

Keeping the Android NFC onTagDiscovered callback thread alive

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.

Is there a way for the client application to kill a running request without destroying the service and losing its associated context?

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.

Bundle keys/values stored in android service disappear in Message

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.

Show ProgressDialog from thread inside the Service

I have a Service with registered ContentObserver. When my ContentObserver detects changes it sets Service's boolean variable to true. I also have a Thread running in the service which sleeps for some time and wakes up to check that variable.
When it detects change it needs some time to process some other code and I need to show ProgressDialog during the delay. How can I do this?
You should use AsyncTask instead.
Here is the link to the library. It is fairly simple:
1) onPreExecute() = show ProgressDialog
2) doInBackground() = execute your code
3) onPostExecute() = dismiss ProgressDialog
DONE :-)
The essence of your question is that you want your service to send a message of some kind to your UI (to show a loading dialog).
There are four (or more) ways of going about this:
Intents: have your service send an intent to your activity
AIDL
Using the service object itself (as singleton)
Having your activity be a broadcast receiver
These options may seem familiar: How to have Android Service communicate with Activity
You'll have to read up on those options and take your pick.
AsyncTask is a good alternative, but still if you decided to go with threads, then in order to show the ProgressDialog on UI you will need to call runOnUiThread() method of the activity.
Let suppose you want to display the ProgressDialog in the MainActivity. Inside your Thread from Service you should have something like this:
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// Display ProgressDialog here
}
});
Thanks everyone for answers.
I solve the problem using these steps
- broadcast Intent when my variable was changed
- create BroadcastReceiver for the intent( in Activity )
- inside BroadcastReceiver's method onReceive call runOnUiThread for my activity
I know this is an old thread but I have exactly what you needed because I just implemented this from a thread here. Please read Rachit Mishra's answer further down the page talking about a ProgressBar:
Communication between Activity and Service
I have this in my service:
public void sendMessage(int state) {
Message message = Message.obtain();
switch (state) {
case 1://SHOW:
message.arg1 = 1;
break;
case 0:
message.arg1 = 0;
break;
}
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
Call sendMessage() with 1 or 0 to show or dismiss the ProgressDialog within your service.
And this is in my Main Activity:
private ProgressDialog progress;
public class MessageHandler extends Handler {
#Override
public void handleMessage(Message message) {
int state = message.arg1;
switch (state) {
case 0://HIDE
progress.dismiss();
break;
case 1://SHOW
progress = ProgressDialog.show(MainActivity.this, (getResources().getString(R.string.CONNECTING) + "..."), (getResources().getString(R.string.PLEASE_WAIT) + "!")); //show a progress dialog
break;
}
}
}
The ProgressDialog cannot be shown from the service, it must be called from the activity or fragment. I hope I added all the code you need and that it works well for your needs. To be honest I'm not sure how the message handler works but it works for me! The naming is probably not the best either lol. Sorry.

Having a long running service in its own task for networking

so my intentions is the following:
I need a background service that mainly takes send requests (UDP) from the main UI and waits for its responses which in turn are dispatched back to the main UI. This is a pretty synchronous process. But additionally, I want the Service to wait for asynchronous messages that can be received any time over the network, for example a network client wants to notify me or to report an error.
What would be the best way to implement this?
Well for IPC the Messenger class could be useful to handle data.
Could it be implemented like the "Remote Messenger Service Sample" found here http://developer.android.com/reference/android/app/Service.html ??
Any help is appreciated.
In my project, i have a service that is permanently running.
I have also 3 threads created in this service, so all in background. Each one of them does some work, requesting http data also: i've done it with the Handler, managing the message queue.
It's easy and grows well, growing the dimension and complexity of project.
So, yes, i should follow that way.
P.S.: If you have to do work in background, remember to acquire wake_locks with PowerManager, ensuring you that CPU will stay on when phone goes in standby (it tooked me some days to understand why threads stop to work when phone goes in standby).
In the main thread, i declared the 4 handlers (2 for two-side communication with recordingThread, and 2 for two-side communication with sendingThread):
Then simply create the other 2 threads; look at Looper calls: it's needed to do not let your thread to die when the code is executed, and to wait for incoming messages coming on handlers created inside its own execution (in this case inside the run() method).
I've also declared a private class for each handler associated to a thread: this way every thread has its own channel to communicate with different 'action' to do (the switch cases).
There's also the other handler, the recordingThread handler, not shown here; but the idea is the same:
private Handler m_recordingThreadHandler, m_sendingThreadHandler,
m_thisRecordingThreadhandler, m_thisSendingThreadHandler;
public void run() {
Looper.prepare();
m_thisRecordingThreadhandler = new UpdRecHandler();
m_recordingThread = new RecordingThread(m_mainThreadContext,
m_thisRecordingThreadhandler, m_configuration, m_picturesDir);
m_recordingThread.setName("recordingThread");
m_recordingThread.start();
m_thisSendingThreadHandler = new UpdSenHandler();
m_sendingThread = new SendingThread(m_mainThreadContext,
m_thisSendingThreadHandler, m_configuration);
m_sendingThread.setName("sendingThread");
m_sendingThread.start();
Looper.loop();
}
private class UpdRecHandler extends Handler {
#Override
public void handleMessage(Message msg) {
Message m;
switch (msg.what) {
case Utils.SEND_THIS_THREAD_HANDLER:
Log.i(TAG, "Ricevuto Handler di recordingThread.");
m_recordingThreadHandler = (Handler) msg.obj;
m_recordingThreadReady = true;
checkForAllThreadsReady();
break;
case Utils.FORCE_RESET:
Log.i(TAG,
"RecordingThread ha compeltato la procedura per il reset.");
m_recordingThreadResetted = true;
checkForThreadsResetted();
break;
...more cases...
}
This code is instead from recordingThread class.
Once created, the run method is executed, in which i pass to the main thread (the code previously seen) the handler of this thread, created in this run() method: in this way i enable 2-sided communication.
Again, in the recordingThread i create a private class for the handler. And again i've put the Looper calls to let thread be alive once run method is executed.
public void run() {
Looper.prepare();
Log.i(TAG, "In esecuzione, mando il mio handler a updateThread");
m_thisThreadHandler = new RecUpdHandler();
m_mainThreadHandler.obtainMessage(Utils.SEND_THIS_THREAD_HANDLER,
m_thisThreadHandler).sendToTarget();
Looper.loop();
}
private class RecUpdHandler extends Handler {
public void handleMessage(Message msg) {
Message m;
switch (msg.what) {
case Utils.TAKE_PHOTO:
...do work....
break;
case Utils.UPDATE_CONFIGURATION:
... do other work...
break;
}
If you want to let your thread terminate, you have simply to kill the Looper associated to that thread, doing as follow:
Looper.myLooper().quit();
And remember that an handler is associated automatically to the thread in which it's created: if handler A is created in thread T1, then you can pass a reference of A to T2 and T3. Those thread are then capable of send messages to T1, but not viceversa.
That's because i've created 4 handlers, for enabling 2-side communication.

Categories

Resources