I have an android service, which is connected to a service connection. Upon initialization, I'd like to send a single String, for example "test message" to the Service connection. How would I do this?
This is my Service
class:
public class ExampleService extends Service {
private final IBinder iBinder = new Messenger(new IncomingHandler(this)).getBinder();
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
}
This is my ServiceConnection
implementation:
private ServiceConnection myService = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.i("exampleService", "Binding Connect");
messenger = new Messenger(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
messenger = null;
}
};
The ServiceConnection
monitors the state of the connection to the service, as opposed to communicating information to the service. To communicate with the service, you need to use the binder
that is passed as an argument to the onServiceConnected(ComponentName name, IBinder binder)
callback.
In your code sample, you are using a Messenger
to perform communication instead of directly interacting with the binder. A Messenger
is:
a simple wrapper around a Binder that is used to perform the communication
Sample code that does what you are asking:
public class MyService extends Service {
// if there are a lot of options, use an enum; its not 2012 anymore and devices have 4GB+ of memory
public static final int MSG_HELLO = 1;
private class IncomingHandler extends Handler {
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MSG_HELLO:
final String stringMessage = (String) message.obj;
Toast.makeText(MyService.this.getApplicationContext(), "MyService: " + stringMessage, Toast.LENGTH_SHORT).show();
default:
return; // message not understood, ignore
}
}
}
final private Messenger messenger = new Messenger(new IncomingHandler());
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
public class MainActivity extends Activity {
private static final String HELLO_MESSAGE = "hello originating from MyActivity";
private Messenger messenger = null;
private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
messenger = new Messenger(iBinder);
// send a HELLO message immediately when connected
final Message message = Message.obtain(null, MyService.MSG_HELLO, HELLO_MESSAGE);
try {
messenger.send(message);
} catch (RemoteException e) {
messenger = null;
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
messenger = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
// rest of implentation...
}
For a more detailed example of how to work with Messenger
, see the Remote Messenger Service Sample.
A couple notes:
Binder
that has a method which returns the instance of the service. See the Local Service Sample for an example.bindService(...)
has a corresponding unbindService(...)
and vice versa. For example, if you call bindService(...)
in onCreate()
, then call unbindService(...)
in onDestroy()
.IBinder
instances may stay in memory beyond the lifecycle of the component that is containing it, potentially until the process is destroyed; this can cause a severe memory leak. If you subclass Binder
inside of an Activity or Service class, then use a static inner class, as opposed to a non-static inner class which will have an implicit reference to the Service or Activity. If you need a reference to a context or lifecycle aware component, then use a WeakReference. The proper way to deal with this is outside the scope of this question. For related posts, see: