The first step is coding the aidl file that includes our definition for the interface that lists the methods we want to enable their invocation from another process.
package com.abelski.currencyservice; interface ICurrencyService { double getCurrency(String country); }
The aidl compiler will go over that file and auto generate the interface we defined in the aidl file. Within that interface the aidl compiler will define Stub.
/* * This file is auto-generated. DO NOT MODIFY. * Original file: /Users/haimmichael/Desktop/android_remote_service/currency_service/src/com/abelski/currencyservice/ICurrencyService.aidl */ package com.abelski.currencyservice; public interface ICurrencyService extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.abelski.currencyservice.ICurrencyService { private static final java.lang.String DESCRIPTOR = "com.abelski.currencyservice.ICurrencyService"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.abelski.currencyservice.ICurrencyService interface, * generating a proxy if needed. */ public static com.abelski.currencyservice.ICurrencyService asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.abelski.currencyservice.ICurrencyService))) { return ((com.abelski.currencyservice.ICurrencyService)iin); } return new com.abelski.currencyservice.ICurrencyService.Stub.Proxy(obj); } public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_getCurrency: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); double _result = this.getCurrency(_arg0); reply.writeNoException(); reply.writeDouble(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.abelski.currencyservice.ICurrencyService { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } public double getCurrency(java.lang.String country) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); double _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(country); mRemote.transact(Stub.TRANSACTION_getCurrency, _data, _reply, 0); _reply.readException(); _result = _reply.readDouble(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_getCurrency = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public double getCurrency(java.lang.String country) throws android.os.RemoteException; }
The next step is defining a class that extends Service. This class should include the definition for the onBind method that should return a reference for a new object instantiated from a class that extends Stub and implements the abstract methods.
package com.abelski.currencyservice; import android.os.RemoteException; import android.app.Service; import android.content.Intent; import android.os.IBinder; public class CurrencyService extends Service { public class CurrencyServiceImpl extends ICurrencyService.Stub { @Override public double getCurrency(String ticker) throws RemoteException { return 3.86; } } @Override public void onCreate() { super.onCreate(); } @Override public void onDestroy() { super.onDestroy(); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); } @Override public IBinder onBind(Intent intent) { return new CurrencyServiceImpl(); } }
When coding the client we should use the very same aidl interface that we were using on the server. In addition we should define a class that implements ServiceConnection. That class includes the definition for call back functions that will be invoked when binding and when unbinding. When binding the call back function that will be invoked will take care of calling the Stub.asInterface method in order to get an object that serves as kind of a representative for the real object on the server side.
package com.abelski.currencyclient; import com.abelski.currencyservice.ICurrencyService; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.content.Intent; import android.content.ServiceConnection; import android.content.ComponentName; import android.content.Context; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; import android.app.Activity; public class MainActivity extends Activity { private ICurrencyService currencyService = null; private Button bindBt; private Button callBt; private Button unbindBt; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); bindBt = (Button) findViewById(R.id.bind); bindBt.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { if (bindService(new Intent(ICurrencyService.class.getName()), serviceConnection, Context.BIND_AUTO_CREATE)) { bindBt.setEnabled(false); callBt.setEnabled(true); unbindBt.setEnabled(true); Toast.makeText(MainActivity.this, "Currency Remote Service is Binded", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, "Currency Remote Service can not be Binded", Toast.LENGTH_SHORT).show(); } } }); callBt = (Button) findViewById(R.id.call); callBt.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { callService(); } }); unbindBt = (Button) findViewById(R.id.unbind); unbindBt.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { unbindService(serviceConnection); bindBt.setEnabled(true); callBt.setEnabled(false); unbindBt.setEnabled(false); Toast.makeText(MainActivity.this, "Currency Remote Service is not Binded", Toast.LENGTH_SHORT) .show(); } }); unbindBt.setEnabled(false); callBt.setEnabled(false); } private void callService() { try { double result = currencyService.getCurrency("USD"); Toast.makeText(MainActivity.this, "Currency Exchange Rate of USD is " + result, Toast.LENGTH_SHORT).show(); } catch (RemoteException exception) { } } private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { currencyService = ICurrencyService.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { currencyService = null; } }; }
The following video clip overviews this code sample, shows its execution and explains it. You can download the code sample and goes over it together with the video clip.