For studying the Android service, I wrote a test program that have three button "bind service", "unbind service" and "send echo" on the screen. When clicked, they use bindService()
, unbindService()
and a Messenger
to communicate with the service.
Here is the service codes:
public class MessengerService extends Service {
private final Messenger mMessenger = new Messenger(new TempHandler());
private class TempHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "Hi, there.", Toast.LENGTH_SHORT).show();
break;
case MSG_SAY_GOODBYE:
Toast.makeText(getApplicationContext(), "See you next time.", Toast.LENGTH_SHORT).show();
break;
case MSG_ECHO:
Toast.makeText(getApplicationContext(), "Received " + msg.arg1 + " from client.", Toast.LENGTH_SHORT).show();
Messenger replyMessenger = msg.replyTo;
Message replyMsg = Message.obtain(null, MSG_ECHO, msg.arg1, 0);
try {
replyMessenger.send(replyMsg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
default:
super.handleMessage(msg);
}
}
}
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "Service bound", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
@Override
public void onDestroy() {
Log.d("", "Service.onDestroy()...");
super.onDestroy();
}
}
And here is the activity code:
public class MessengerActivity extends Activity {
private Messenger mMessengerService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity2);
Button bind = (Button) findViewById(R.id.button5);
bind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doBindService();
}
});
Button unbind = (Button) findViewById(R.id.button6);
unbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doUnbindService();
}
});
Button echo = (Button) findViewById(R.id.button7);
echo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doSendEcho();
}
});
}
private void doBindService() {
Intent intent = new Intent(getApplicationContext(), MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private void doUnbindService() {
Message msg = Message.obtain(null, MessengerService.MSG_SAY_GOODBYE);
try {
mMessengerService.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
unbindService(mConnection);
}
private void doSendEcho() {
if (mMessengerService != null) {
Message msg = Message.obtain(null, MessengerService.MSG_ECHO, 12345, 0);
msg.replyTo = mMessenger;
try {
mMessengerService.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private final Messenger mMessenger = new Messenger(new TempHandler());
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Toast.makeText(getApplicationContext(), "Service is connected.", Toast.LENGTH_SHORT).show();
mMessengerService = new Messenger(service);
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO);
try {
mMessengerService.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mMessengerService = null;
Toast.makeText(getApplicationContext(), "Service is disconnected.", Toast.LENGTH_SHORT).show();
}
};
private class TempHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MessengerService.MSG_ECHO:
Toast.makeText(getApplicationContext(), "Get the echo message (" + msg.arg1 + ")", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
}
When I click "bind service" and "send echo" button. I can see the service is connected and the message communication is good. And then click "unbind service", I saw the service onDestroy()
be called, so I expect the service is stopped and should not respond to the coming message again. But actually is, the service seems still alive and I could get the echo message again when click the "send echo" button. So I'm wondering is there anything I made incorrect? Or maybe I'm not fully understand about the service?
Hope someone can help, thanks.
A service is "bound" when an application component binds to it by calling bindService()
. A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another application component is bound to it.
http://developer.android.com/guide/components/services.html
A service will shut down after all bindService()
calls have had their corresponding unbindService()
calls. If there are no bound clients, then the service will also need stopService() if and only if somebody called startService() on the service.
Drawing from the below link.
How to check if a service is running on Android?.
private void doSendEcho() {
if(isMyServiceRunning()) // if service is running
{
if (mMessengerService != null) {
Message msg = Message.obtain(null, MessengerService.MSG_ECHO, 12345, 0);
msg.replyTo = mMessenger;
try {
mMessengerService.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (MessengerService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
unbindService(mConnection);
Log.i("Stopped!",""+isMyServiceRunning());
Log.i("stopped", "Service Stopped");
}
Example:
I tested the below it works fine.
public class MessengerService extends Service {
public static final int MSG_SAY_HELLO =1;
public static final int MSG_SAY_GOODBYE =2;
ArrayList<Messenger> mClients = new ArrayList<Messenger>();
private final Messenger mMessenger = new Messenger(new TempHandler());
private class TempHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
mClients.add(msg.replyTo);
Toast.makeText(getApplicationContext(), "Hi, there.", Toast.LENGTH_SHORT).show();
break;
case MSG_SAY_GOODBYE:
mClients.add(msg.replyTo);
break;
default:
super.handleMessage(msg);
}
}
}
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "Service bound", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
@Override
public void onDestroy() {
Log.i("MessengerService", "Service Destroyed...");
super.onDestroy();
}
}
MainAactivity.java
public class MainActivity extends Activity {
boolean mIsBound=false;
Messenger mService = null;
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (MessengerService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bind = (Button) findViewById(R.id.button1);
bind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doBindService();
}
});
Button unbind = (Button) findViewById(R.id.button2);
unbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doUnbindService();
}
});
}
class TempHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MessengerService.MSG_SAY_GOODBYE:
Toast.makeText(MainActivity.this,"Received from service: " + msg.arg1,1000).show();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new TempHandler());
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = new Messenger(service);
// mCallbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
Message msg = Message.obtain(null,
MessengerService.MSG_SAY_HELLO);
msg.replyTo = mMessenger;
mService.send(msg);
// Give it some value as an example.
// msg = Message.obtain(null,
// MessengerService.MSG_E, this.hashCode(), 0);
// mService.send(msg);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(MainActivity.this, "remote_service_connected",
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
// mCallbackText.setText("Disconnected.");
// As part of the sample, tell the" user what happened.
Toast.makeText(MainActivity.this, "remote_service_disconnected",
Toast.LENGTH_SHORT).show();
}
};
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because there is no reason to be able to let other
// applications replace our component.
bindService(new Intent(MainActivity.this,
MessengerService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound=true;
Toast.makeText(MainActivity.this, "Binding",1000).show();
}
void doUnbindService() {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
Message msg = Message.obtain(null,
MessengerService.MSG_SAY_GOODBYE);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
Toast.makeText(MainActivity.this, "UnBinding"+isMyServiceRunning(),1000).show();
}
}
}
I personally find the terminology/nomenclature to be dissatisfying/misleading.
"onDestroy" and "stopService" might be better understood if they were called "FlagForAndroidOSDestruction" and "FlagForAndroidStopService".
If one downloads/compiles/runs any of the following examples, one can see that even when the OnHandleIntent is finished or stopService has been called, the process and even the service can still hang around! To see this simply launch the example(s) below, and then on your phone/tablet goto
Settings->Apps->Running->Show Running Services
and
Settings->Apps->Running->Show Cached Processes
When you see these, try launching a ton of other apps on the phone and THEN you'll see Android destroying said service & process.
http://developer.android.com/guide/components/services.html#ExtendingIntentService
http://android-er.blogspot.com/2013/03/stop-intentservice.html
How to check all the running services in android?
Yes, this is a conclusion drawn out of the official docs:
A service can be both started and have connections bound to it. In such a case, the system will keep the service running as long as either it is started or there are one or more connections to it with the Context.BIND_AUTO_CREATE flag. Once neither of these situations hold, the service's onDestroy() method is called and the service is effectively terminated. All cleanup (stopping threads, unregistering receivers) should be complete upon returning from onDestroy().
This link (Do I need to call both unbindService and stopService for Android services?) says that you need to call stopService before unbindService.
Try that.
From http://developer.android.com/guide/components/services.html :
These two paths are not entirely separate. That is, you can bind to a service that was already started with startService(). For example, a background music service could be started by calling startService() with an Intent that identifies the music to play. Later, possibly when the user wants to exercise some control over the player or get information about the current song, an activity can bind to the service by calling bindService(). In cases like this, stopService() or stopSelf() does not actually stop the service until all clients unbind.
So you have to call unBindService() and after stopService()