I recently wrote my first little app for my Android smartphone and I was surprised how easy it was. Being familiar with regular Java, learning the new Android APIs was very simple for the most part. However, there was one thing that wasn’t particularly straight forward: communicating between an Activity, i.e. the user interface, and a background Service started by the application. After many hours, I found some sample code on Stack Overflow which I used to create a very generic reusable solution to start, stop and communicate with your own Service implementation.
The code below provides an easy way to:
- Implement your own Android background Service
- Start and stop the service from an Activity, i.e. the UI
- Send/receive messages by the Service
- Send/receive messages by the Activity
Solution
Here are three simple steps how to use my code:
- Download and import the two Java classes AbstractService and ServiceManager
- Inherit from AbstractService to implement your own service.
- Add a ServiceManager to your Activity.
You can now:
- Control the service with ServiceManager.start() and .stop()
- Send messages to the service via ServiceManager.send()
- Receive messages from the service by passing a Handler in the ServiceManager constructor
- Send messages from the service to all listeners using AbstractService.send()
- Receive messages in the service via the onReceiveMessage() callback
Example
Here is a small excerpt that shows how to use the code, i.e. how to implement your service and control it from the activity. You can find a full example on Launchpad.
Step 1: Inherit from AbstractService: Override onStartService, onStopService and onReceiveMessage, and use send() to send messages to the activity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public class SomeService1 extends AbstractService { public static final int MSG_INCREMENT = 1; public static final int MSG_COUNTER = 2; private Timer timer = new Timer(); private int counter = 0, incrementby = 1; @Override public void onStartService() { // Increment counter and send to activity every 250ms timer.scheduleAtFixedRate(new TimerTask(){ public void run() { try { counter += incrementby; send(Message.obtain(null, MSG_COUNTER, counter, 0)); } catch (Throwable t) { } }, 0, 250L); } } @Override public void onStopService() { if (timer != null) {timer.cancel();} counter = 0; } @Override public void onReceiveMessage(Message msg) { if (msg.what == MSG_INCREMENT) { incrementby = msg.arg1; } } } |
Step 2: Use the service in an activity with a ServiceManager: Instantiate a service manager, control the service with .start()/.stop() and send messages to the service with .send(). Note: To unregister the activity from the service, you should call .unbind() in the onDestroy()-callback.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
public class ExampleActivity extends Activity { private ServiceManager service; @Override public void onCreate(Bundle savedInstanceState) { // Create a service 'SomeService1' (see below) and handle incoming messages this.service = new ServiceManager(this, SomeService1.class, new Handler() { @Override public void handleMessage(Message msg) { // Receive message from service switch (msg.what) { case SomeService1.MSG_COUNTER: textValue1.setText("Counter @ Service1: " + msg.arg1); break; default: super.handleMessage(msg); } } }); // Now you can control the service via .start(), .stop() service.start(); service.stop(); // Or send messages to it service.send(Message.obtain(null, SomeService1.MSG_VALUE, 12345, 0)); } @Override protected void onDestroy() { super.onDestroy(); try { service.unbind(); } catch (Throwable t) { } } ... } |
Full code example
Checkout on Launchpad:
https://code.launchpad.net/~binwiederhier/+junk/android-service-example
Browse code on Launchpad:
http://bazaar.launchpad.net/~binwiederhier/+junk/android-service-example/files
nice example… easy to understand the concept this is good one ! thank u…
this is good example , east to understand the service concept !
Great Work,
I am trying to incorporate into an upgrade I am doing to my app. For some reason, the onServiceConnected method is not being called, so I can not send messages to the service from the activity. Any Ideas
Very useful example, but I can not receive the message nell’activty, any ideas about this problem?
I’m sorry, there was an error of my code, now it work, fantastic !
How to call the service from another activity?
Made my life easy…Thank you
It is a great example for me. Thanks.
This is a very nice example.
But I have one question that I can’t figure out for a while.
When I’m trying to use service.send(msg) to send a message directly after I call the service.start(), the message will not be sent successfully. It seems the service will only be able to bind to the activity after “a while” after the start() is called.
Any ideas? Thanks a lot.
@Hang Liu: Interesting. I have not encountered that behavior — but then again I have not been using the classes extensively.
What’s odd about this is that ServiceManager.start() calls Context.bindService() which does synchronously bind and return. So there should not be such a behavior.
Please let me know if you find the reason!
Hi Phililpp, this is my first attempt at services so bare with me. I want the service to run in the background while my app switches activities. It’s a downloader downloading 100’s of images. An action by the user in one of the other activities may mean an image will have it’s download priority promoted to #1 – to be attended to soonest. Even though the service is started in my main activity, I want the other activity to be able to communicate with this service. I am wondering, to do this, do I attempt to start another instance of the service, which will effectively connect the Activity2 to the existing service instance and send it my messages? Or is there some way of passing a reference to a service between Activiities? I guess I am being clear as mud here!
Thanks for your code and any tips you can provide
Chris
Hi,
example is good but i am searching for only background service it’s run when device start and updating the location information to local database or web service without UI , i searched many sites from google but no solution could u pl help
The service will be running even if the Activity/UI is not running. That is the whole point of the service. Starting a service in the background is a different story and not part of this example.
can this service be a singleton and be called from multiple activities??
Hi – thanks for the example code – I like it.
One thing – I think I had a similar problem to Hang Liu above, where – as in your example code in this post, I tried to send a message immediately after starting the service in onCreate, but it didn’t work.
I’m not sure, but I suspect the answer might be in this post: http://stackoverflow.com/questions/3055599/android-how-do-i-wait-until-a-service-is-actually-connected
The salient point seems to be that the bind probably won’t even START until the activity’s onCreate is finished – and instead the early message should be put in the ServiceConnection object’s onServiceConnected method. The only problem with this, is that that code is inside your library code at present… in ServiceManager.java… if I’m not mistaken.
Anyway – thanks for your help and ideas.
Thank you for sharing.
I have one question.
I don’t know that the 1 value isn’t displayed in this view. Start from 2?
How do you bind to the service when an activity is on a tabhost?.
Very useful.
good example.
Thanks
Nice example. This made it a lot easier to understand.
Thanks
Hi, thank you very much, this is a great set of classes.
@John and @Hang Lui:
I ran into the same issue, the service will not accept any messages until the connection is established (onServiceConnected()) is called. I fixed this issue in my case by adding a queue for messages that get sent once onServiceConnected() is called:
public void send(Message msg) throws RemoteException {
if(mIsBound)
{
if(mService != null && mIsConnected == true)
{
mService.send(msg);
}
else
{
// If the service is not ready, queue the messages until the connection is properly established.
mMessages.add(msg);
}
}
}
Then inside onServiceConnected():
Message msg = Message.obtain(null, AbstractService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
mIsConnected = true;
// If there are messages waiting, send them out.
for(Message m : mMessages)
{
mService.send(m);
}
mMessages.clear();
Hope that helps.
Anyone figured out yet how to connect a new activity (one that did not start the service) to the service with this interface?
Hello. Please say me How to bind two Android Activites to one Service?