androidandroid-studioandroid-serviceandroid-service-binding

How to send an initial message using ServiceConnection?


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;
    }
};

Solution

  • 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:

    1. If you are communicating with a local service (i.e. the service is in the same process as the activity), I recommend not using messenger as this will make things more complicated than necessary. Instead, you should create a subclass of Binder that has a method which returns the instance of the service. See the Local Service Sample for an example.
    2. Make sure every bindService(...) has a corresponding unbindService(...) and vice versa. For example, if you call bindService(...) in onCreate(), then call unbindService(...) in onDestroy().
    3. Regardless of whether the service is local or remote, be aware of memory leaks. 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: