Android local service binding - quick howto

So you've decided you need a Service in your Android app.
The question is how to invoke its methods? How to interact with it?

In a nutshell, you define an interface to the service.
Upon request of such an interface (by an activity), the code binds an asynchronous implementation of the interface to the real service and returns that implementation.
The activity invokes methods on that implementation (interface) and they are later (async) propagated to the service.
The result will be two toasts fired by the service, but originated from the activity.

Below is the how code.
When you test the code, don't forget to add the activity and service to the manifest file.
Enjoy!

Manifest:
<activity android:name="info.fastpace.android.sample.LocalServiceBindingDemo$SampleActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="info.fastpace.android.sample.LocalServiceBindingDemo$MyService"/>
view raw gistfile1.xml hosted with ❤ by GitHub


Code:
package info.fastpace.android.sample;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class LocalServiceBindingDemo {
public static class SampleActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bindUI();
// Binding
MainInterface mainInterface = MyService.getAsyncService(this);
// invoke methods of the service. The code will run in the UI thread asynchronously.
mainInterface.do1();
mainInterface.do2();
}
private void bindUI() {
// ...
}
}
public interface MainInterface {
void do1();
void do2();
}
// MyService implements MainInterface
public static class MyService extends Service implements MainInterface {
private final IBinder binder = new LocalBinder();
@Override
public IBinder onBind(Intent intent) {
return binder;
}
private class LocalBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
@Override
public void do1() {
// The implementation of the service
Toast.makeText(this, "Doing 111", Toast.LENGTH_LONG).show();
}
@Override
public void do2() {
// The implementation of the service
Toast.makeText(this, "Doing 222", Toast.LENGTH_LONG).show();
}
// This is how activities get hold of the service implementation as an interface!!!
// All invocations will be executed async in the UI thread!!!
public static MainInterface getAsyncService(final Context context) {
final Intent intent = new Intent(context, MyService.class);
// Unmark the following if you want the service to keep on leaving even after the passed activity was destroyed.
// context.startService(intent);
MainInterface proxy = (MainInterface) Proxy.newProxyInstance(context.getClassLoader(), new Class<?>[] { MainInterface.class }, new InvocationHandler() {
@Override
public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// this is the implementation itself!!!
MainInterface mainInterface = ((LocalBinder) service).getService();
try {
method.invoke(mainInterface, args);
} catch (Exception e) {
// Alternatively, you can notify the service's error mechanism
Log.e("", "Error invoking service method: " + method.getName(), e);
}
}
};
// This is how you bind async the connection to the service.
// The service will be created if it was not yet created.
context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
return null;
}
});
return proxy;
}
}
}
view raw gistfile1.java hosted with ❤ by GitHub