一、前言
Android中的RPC参考了JAVA中的RMI(remote methord invocation)方案;Android中的RPC机制是为了实现进程间通信,即一个进程使用另一个进程中的远程对象,Android用AIDL(android interface defined language,Android接口定义语言)来实现,使用户可以很方便的定义一套接口,然后通过远程Service为代理,客户端在绑定该远程Service时,获得远程对象,然后就可以使用该对象。
二、流程
1. 定义一个.aidl接口文件,ADT则会在gen目录下,自动生成一个对应的.java文件(此文件为自动生成,不可修改);
2. 自动生成的.java中,有一个抽象的Stub类,因此,创建一个基于Stub的子类,并实现在.aidl中定义的接口;
3. 创建一个Service子类,在onCreate中,创建步骤2中的实例对象,在onBind中,返回该实例对象;
4. 在Activity中,发送一个bindService(it, conn, BIND_AUTO_CREATE),请求绑定远程Service;
5. 若成功,则在ServiceConnection.onServiceConnected中获得步骤3中的实例对象;
6. 在AndroidManifest.xml中,注册远程Service;
三、例子
按照流程中的步骤,实现该例子:
3.1 定义.aidl文件
package com.chris.rpc.aidl; interface ITest { void setCustomerName(String name); String getCustomerName(); }
很简单,定义了两个接口,然后,ADT就会自动在gen目录下生成ITest.java文件,我们来看看:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: E:\\chris\\Android_Dev\\eclipse\\workspace\\RpcAidl\\src\\com\\chris\\rpc\\aidl\\ITest.aidl */ package com.chris.rpc.aidl; public interface ITest extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.chris.rpc.aidl.ITest { private static final java.lang.String DESCRIPTOR = "com.chris.rpc.aidl.ITest"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.chris.rpc.aidl.ITest interface, * generating a proxy if needed. */ public static com.chris.rpc.aidl.ITest asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.chris.rpc.aidl.ITest))) { return ((com.chris.rpc.aidl.ITest)iin); } return new com.chris.rpc.aidl.ITest.Stub.Proxy(obj); } @Override 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_setCustomerName: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); this.setCustomerName(_arg0); reply.writeNoException(); return true; } case TRANSACTION_getCustomerName: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getCustomerName(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.chris.rpc.aidl.ITest { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public void setCustomerName(java.lang.String name) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(name); mRemote.transact(Stub.TRANSACTION_setCustomerName, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public java.lang.String getCustomerName() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getCustomerName, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_setCustomerName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getCustomerName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public void setCustomerName(java.lang.String name) throws android.os.RemoteException; public java.lang.String getCustomerName() throws android.os.RemoteException; }
3.2 创建Stub子类,并实现接口:
package com.chris.rpc.aidl; import android.os.RemoteException; import android.util.Log; import com.chris.rpc.aidl.ITest.Stub; public class MyTest extends Stub { private final static String TAG = "ChrisRpcAidl"; private String strName = ""; @Override public void setCustomerName(String name) throws RemoteException { Log.d(TAG, "[MyTest] setCustomerName name = " + name); strName = name; } @Override public String getCustomerName() throws RemoteException { Log.d(TAG, "[MyTest] getCustomerName name = " + strName); return strName; } }
3.3 创建远程Service类:
package com.chris.rpc.aidl; import com.chris.rpc.aidl.ITest.Stub; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.util.Log; public class MyService extends Service { private final static String TAG = "ChrisRpcAidl"; private Stub mTest = null; @Override public IBinder onBind(Intent arg0) { Log.d(TAG, "onBind ..."); return mTest; } @Override public void onCreate() { Log.d(TAG, "onCreate ..."); mTest = new MyTest(); super.onCreate(); } }
3.4 在Activity中请求绑定该远程Service,并获取实例:
package com.chris.rpc.aidl; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; public class MainActivity extends Activity { private final static String TAG = "ChrisRpcAidl"; private final static String SERVICE_ACTION = "com.chris.rpc.aidl.action.MYSERVICE"; private ITest mTest = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent it = new Intent(); it.setAction(SERVICE_ACTION); // 发送绑定Service请求,如果成功,则会调用ServiceConnection bindService(it, conn, BIND_AUTO_CREATE); } private ServiceConnection conn = new ServiceConnection(){ // 绑定成功,返回的对象是IBinder接口,通过asInterface转换为定义的ITest接口 @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG, "onServiceConnected ..."); mTest = ITest.Stub.asInterface(service); if(mTest != null){ try { mTest.setCustomerName("chris"); Log.d(TAG, "-------------------"); Log.d(TAG, "get name = " + mTest.getCustomerName()); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected(ComponentName name) { } }; }
3.5 在AndroidManifest.xml中,注册远程Service:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.chris.rpc.aidl" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="10" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.chris.rpc.aidl.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="com.chris.rpc.aidl.MyService"> <intent-filter> <action android:name="com.chris.rpc.aidl.action.MYSERVICE" /> </intent-filter> </service> </application> </manifest>
3.6 运行截图:
四、小结
本篇文章是让大家了解AIDL的过程,特别是大家需要理解一个自动生成的接口类的中Stub, asInterface, onTransact方法,这些方法在Android Java Framework中有对应的实现,并最终通过native来完成。然后就是大家要了解这个远程Service的用法就可以了。AIDL通常用在如手机播放器这类应用中,各个activity与service绑定后,可以控制播放器的各个状态。
(之后要是有空,我会继续写androd java framework中关于binder的理解)