Android AIDL的使用与理解
2017-05-03 17:02
513 查看
AIDL(Android Interface Definition Language),它是Android接口定义语言。它是一种辅助性语言。使用它来达到自动生成接口的目的。它实际上还是使用binder来达到进程间通讯的一个辅助工具。
我们在使用binder时,实际上需要达到两个目的。一个是通讯逻辑的实现,即要找到谁。二是业务逻辑的实现,即要做什么。
通讯逻辑上,都是一样的,所以代码上都是一样的。业务逻辑则是无法统一的(你要做什么事情,只有你自己知道)。
AIDL就是为了方便的实现这两个目的的。
我们先来实现服务端:
使用eclipse新建一个android项目,然后创建一个基础类Person.java:
在Person 这个类中,我们实现于Parcelable接口。这主要是为了让数据能够序列化,通过序列化在进程间(或网络中)传递对象。
然后我们来写aidl文件 Person.aidl:
Person.aidl是为了让Person能供其它的aidl文件使用。
下面我们在写一个实现业务逻辑的aidl IPersonManager.aidl:
这个aidl有两个业务接口:getPersons() 和 addPerson(in Person person),在这个aidl里,我们看不出有通讯相关的逻辑。
然后我们重新生成一下项目,此时会在gen目录下,产生一个IPersonManager.java的文件:
到此时,之前的两个aidl文件就可以删除了,它们的目的就是为了生成这个java接口。
这个java接口,最主要的是它的这个抽象类abstract class Stub,这是个抽象类,实现了IPersonManager接口的同时,又继承了android.os.Binder。所以它具有了跨进程通讯的能力,同时又有了实现业务逻辑的能力。
接下来,我们来真正的实现相关的业务逻辑 MyIPC_Service.java:
它有一个重要的成员private final IPersonManager.Stub mPersonManager,注意到它就是我们使用aidl生成的IPersonManager下的stub这个抽象类。并真正的实现了getPersons和addPerson这两个接口函数。
接下来,我们来实现客户端的访问功能:
新建一个android项目,并将上面服务端产生的Person.java和IPersonManager.java拷贝过去,并建立相应的包。(注意:这个包名是要与服务端的包名一样的)
然后,我们来做相关的访问代码 MainActivity.java:
这里bindService(intent, conn, Service.BIND_AUTO_CREATE);高版本已经不支持这种匿名绑定了,所以这里我们在AndroidManifest.xml里限制SDK的版本号 ,将
我们知道系统的服务都会向ServiceManager注册,这样客户端要访问服务时,只要向ServiceManager查询到该服务对应的binder,就能与服务通讯了。
但是我们这里是自定义的匿名服务,并没有向ServiceManager注册,那客户端是如何得到服务的handle或binder引用的呢?答案就在bindService函数。以后我们再来分析bindService,这里我们只要知道无论是客户端还是服务端的进程都是在AMS里启动的,所以AMS持有客户端和服务端双方的进程信息。
所以使用bindService是可以让服务端与客户端通过BINDER通讯的,而使用startService则不行。
在客户端中实现了一个ServiceConnection类,在调用了bindService或unbindService后,会相应的调用到ServiceConnection类中的onServiceConnected和onServiceDisconnected。
我们看到IPersonManager这个接口里有一个抽象类stub,同时stub又有一个内部类Proxy,它也是实现于IPersonManager。为什么要搞的这么复杂呢?
原因在于这个IPersonManager既要在服务端使用,又要在客户端使用。对于客户端来说,使用proxy这个代理类,来调用服务端的函数功能。这个代理类的存在就是为了屏蔽binder的相关信息的。让客户端的调用者像是直接在操作服务端的函数一样。
像在客户端的ServiceConnection类中onServiceConnected函数中:
返回的就是proxy对象,而Proxy真正的执行者是它的mRemote这个成员对象,
private android.os.IBinder mRemote;可以看出它仅是一个IBinder引用而已,
而此时调用mPersonManager.addPerson时实现上就是调用了Proxy类中的addPerson函数:
最终就是通过mRemote这个binder引用去传送数据给服务端。
而在服务端中,实现的却是private final IPersonManager.Stub mPersonManager=new IPersonManager.Stub(){…}
这样在客户端使用了mRemote.transact后,服务端会在stub.onTransact函数中处理请求:
这里的
然后把结果打包并写回binder驱动中,返回给客户端。
所以,IPersonManager.java既是给客户端用的,也是给服务端用的。客户端主要是使用里边的stub.proxy的抽象类,服务端主要是使用里边的stub抽象类。(这个描述不准确,意思理解了就行)。
在 IPersonManager.java中定义了一些操作:
static final int TRANSACTION_getPersons = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
这些是为了在进程通讯中使用一个统一的协议。
RPC(Remote Procedure Call Protocol)——远程过程调用协议
毕竟,远程通讯或者说跨进程调用中,使用一套统一的协议才能方便通讯。
我们在使用binder时,实际上需要达到两个目的。一个是通讯逻辑的实现,即要找到谁。二是业务逻辑的实现,即要做什么。
通讯逻辑上,都是一样的,所以代码上都是一样的。业务逻辑则是无法统一的(你要做什么事情,只有你自己知道)。
AIDL就是为了方便的实现这两个目的的。
我们先来实现服务端:
使用eclipse新建一个android项目,然后创建一个基础类Person.java:
package com.zyftest.myipc; import android.os.Parcel; import android.os.Parcelable; public class Person implements Parcelable{ private String mName; private String mICCard; public String getName() { return this.mName; } public void setName(String name) { this.mName=name; } public String getICCard() { return this.mICCard; } public void setICCard(String card) { this.mICCard=card; } @Override public int describeContents() { // TODO Auto-generated method stub return 0; } @Override public void writeToParcel(Parcel dest, int flags) { // TODO Auto-generated method stub dest.writeString(mName); dest.writeString(mICCard); } public void readFromParcel(Parcel dest) { //注意,此处的读值顺序应当是和writeToParcel()方法中一致的 mName = dest.readString(); mICCard = dest.readString(); } public Person() {} public Person(Parcel in) { mName = in.readString(); mICCard = in.readString(); } public static final Creator<Person> CREATOR = new Creator<Person>() { @Override public Person createFromParcel(Parcel in) { return new Person(in); } @Override public Person[] newArray(int size) { return new Person[size]; } }; }
在Person 这个类中,我们实现于Parcelable接口。这主要是为了让数据能够序列化,通过序列化在进程间(或网络中)传递对象。
然后我们来写aidl文件 Person.aidl:
// Person.aidl //这个文件的作用是引入了一个序列化对象 Person 供其他的AIDL文件使用 //注意:Person.aidl与Person.java的包名应当是一样的 package com.zyftest.myipc; //注意parcelable是小写 parcelable Person;
Person.aidl是为了让Person能供其它的aidl文件使用。
下面我们在写一个实现业务逻辑的aidl IPersonManager.aidl:
package com.zyftest.myipc; import com.zyftest.myipc.Person; interface IPersonManager { //所有的返回值前都不需要加任何东西,不管是什么数据类型 List<Person> getPersons(); //传参时除了Java基本类型以及String,CharSequence之外的类型 //都需要在前面加上定向tag,具体加什么量需而定 void addPerson(in Person person); }
这个aidl有两个业务接口:getPersons() 和 addPerson(in Person person),在这个aidl里,我们看不出有通讯相关的逻辑。
然后我们重新生成一下项目,此时会在gen目录下,产生一个IPersonManager.java的文件:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: /home/zhangyafei/workspace/MyIpc/MyIPC/src/com/zyftest/myipc/IPersonManager.aidl */ package com.zyftest.myipc; public interface IPersonManager extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.zyftest.myipc.IPersonManager { private static final java.lang.String DESCRIPTOR = "com.zyftest.myipc.IPersonManager"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.zyftest.myipc.IPersonManager * interface, generating a proxy if needed. */ public static com.zyftest.myipc.IPersonManager asInterface( android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.zyftest.myipc.IPersonManager))) { return ((com.zyftest.myipc.IPersonManager) iin); } return new com.zyftest.myipc.IPersonManager.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_getPersons: { data.enforceInterface(DESCRIPTOR); java.util.List<com.zyftest.myipc.Person> _result = this .getPersons(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addPerson: { data.enforceInterface(DESCRIPTOR); com.zyftest.myipc.Person _arg0; if ((0 != data.readInt())) { _arg0 = com.zyftest.myipc.Person.CREATOR .createFromParcel(data); } else { _arg0 = null; } this.addPerson(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.zyftest.myipc.IPersonManager { 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 java.util.List<com.zyftest.myipc.Person> getPersons() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.zyftest.myipc.Person> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getPersons, _data, _reply, 0); _reply.readException(); _result = _reply .createTypedArrayList(com.zyftest.myipc.Person.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result; } // 传参时除了Java基本类型以及String,CharSequence之外的类型 // 都需要在前面加上定向tag,具体加什么量需而定 @Override public void addPerson(com.zyftest.myipc.Person person) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((person != null)) { _data.writeInt(1); person.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_getPersons = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } // 所有的返回值前都不需要加任何东西,不管是什么数据类型 public java.util.List<com.zyftest.myipc.Person> getPersons() throws android.os.RemoteException; // 传参时除了Java基本类型以及String,CharSequence之外的类型 // 都需要在前面加上定向tag,具体加什么量需而定 public void addPerson(com.zyftest.myipc.Person person) throws android.os.RemoteException; }
到此时,之前的两个aidl文件就可以删除了,它们的目的就是为了生成这个java接口。
这个java接口,最主要的是它的这个抽象类abstract class Stub,这是个抽象类,实现了IPersonManager接口的同时,又继承了android.os.Binder。所以它具有了跨进程通讯的能力,同时又有了实现业务逻辑的能力。
接下来,我们来真正的实现相关的业务逻辑 MyIPC_Service.java:
package com.zyftest.myipc; import java.util.ArrayList; import java.util.List; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; public class MyIPC_Service extends Service{ public final String TAG = this.getClass().getSimpleName(); private List<Person> mPersons = new ArrayList<Person>(); private final IPersonManager.Stub mPersonManager=new IPersonManager.Stub() { @Override public List<Person> getPersons() throws RemoteException { // TODO Auto-generated method stub synchronized (this) { Log.e(TAG, "invoking getPersons() method , now the list is : " + mPersons.toString()); if (mPersons != null) { return mPersons; } return new ArrayList<Person>(); } } @Override public void addPerson(Person person) throws RemoteException { // TODO Auto-generated method stub synchronized (this) { if (mPersons == null) { mPersons = new ArrayList<Person>(); } if (person == null) { Log.e(TAG, " is null in In"); person = new Person(); } person.setICCard("1111111111111111"); if (!mPersons.contains(person)) { mPersons.add(person); } //打印Person列表,观察客户端传过来的值 Log.e(TAG, "invoking addPerson() method , now the list is : " + mPersons.toString()); } } }; @Override public void onCreate() { super.onCreate(); Person person = new Person(); person.setName("刘备"); person.setICCard("0000000000000000"); mPersons.add(person); } @Override public IBinder onBind(Intent intent) { Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString())); return mPersonManager; } @Override public boolean onUnbind(Intent intent) { // TODO Auto-generated method stub Log.d("zyfipctest","onUnbind...stopSelf"); System.exit(0); return super.onUnbind(intent); } }
它有一个重要的成员private final IPersonManager.Stub mPersonManager,注意到它就是我们使用aidl生成的IPersonManager下的stub这个抽象类。并真正的实现了getPersons和addPerson这两个接口函数。
接下来,我们来实现客户端的访问功能:
新建一个android项目,并将上面服务端产生的Person.java和IPersonManager.java拷贝过去,并建立相应的包。(注意:这个包名是要与服务端的包名一样的)
然后,我们来做相关的访问代码 MainActivity.java:
package com.zyftest.ipcclient; import java.util.List; import com.zyftest.myipc.IPersonManager; import com.zyftest.myipc.Person; import android.app.Activity; import android.app.Service; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity { private IPersonManager mPersonManager; private Button setName, getName; private TextView show; private EditText input; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获取各组件 setName = (Button)findViewById(R.id.set); getName = (Button)findViewById(R.id.get); show = (TextView)findViewById(R.id.show); input = (EditText)findViewById(R.id.input); //为设置按钮绑定监听 setName.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub if(input.getText() != null) { try { //将输入的信息保存到远程服务端 Person person=new Person(); person.setName(input.getText().toString()); person.setICCard("2222222222"); mPersonManager.addPerson(person); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); //获取按钮绑定监听 getName.setOnClickListener(new OnClickListener() { //String name = mIMyService.getName(); @Override public void onClick(View v) { // TODO Auto-generated method stub try { List<Person> persons= mPersonManager.getPersons(); if(persons!=null&&persons.size()>0) { StringBuilder sbuilder=new StringBuilder(); for(Person person : persons) { if(person.getName() != null) { sbuilder.append(person.getName()); sbuilder.append(" : "); sbuilder.append(person.getICCard()); sbuilder.append(" | "); } } show.setText(sbuilder.toString()); } } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } @Override protected void onResume() { // TODO Auto-generated method stub super.onResume(); //创建Intent,对应服务端注册的Intent Log.d("zyfipctest","onResume......bindService"); final Intent intent = new Intent(); intent.setAction("com.zyftest.action.myipcservice"); //绑定连接远程服务 bindService(intent, conn, Service.BIND_AUTO_CREATE); /* final Intent intent_explicit = new Intent(this,MyIPC_Service.class); Log.d("zyfipctest","onCreate......bindService"); bindService(intent_explicit,conn,Service.BIND_AUTO_CREATE); */ } @Override protected void onPause() { // TODO Auto-generated method stub super.onPause(); Log.d("zyfipctest","onPause......unbindService"); //解除绑定 unbindService(conn); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); Log.d("zyfipctest","onDestroy......unbindService"); //解除绑定 //unbindService(conn); } //实现客户端与服务端绑定的关键部分 private ServiceConnection conn = new ServiceConnection() { //解除连接服务端 @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub mPersonManager = null; Log.d("zyfipctest","onServiceDisconnected......"); } //连接服务端 @Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub /*此处实现获取通过IpcService的onBind方法返回的mServiceBinder对象的代理。 * 参数service为绑定获得的远程服务IpcService的mServiceBinder对象, * 而调用mPersonManager.Stub的函数asInterface获取的是mServiceBinder对象的代理。 */ Log.d("zyfipctest","onServiceConnected......"); mPersonManager = IPersonManager.Stub.asInterface(service); Log.d("zyfipctest","onServiceConnected......mPersonManager = "+mPersonManager); } }; }
这里bindService(intent, conn, Service.BIND_AUTO_CREATE);高版本已经不支持这种匿名绑定了,所以这里我们在AndroidManifest.xml里限制SDK的版本号 ,将
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" /> 改为 <uses-sdk android:minSdkVersion="15" />
我们知道系统的服务都会向ServiceManager注册,这样客户端要访问服务时,只要向ServiceManager查询到该服务对应的binder,就能与服务通讯了。
但是我们这里是自定义的匿名服务,并没有向ServiceManager注册,那客户端是如何得到服务的handle或binder引用的呢?答案就在bindService函数。以后我们再来分析bindService,这里我们只要知道无论是客户端还是服务端的进程都是在AMS里启动的,所以AMS持有客户端和服务端双方的进程信息。
所以使用bindService是可以让服务端与客户端通过BINDER通讯的,而使用startService则不行。
在客户端中实现了一个ServiceConnection类,在调用了bindService或unbindService后,会相应的调用到ServiceConnection类中的onServiceConnected和onServiceDisconnected。
我们看到IPersonManager这个接口里有一个抽象类stub,同时stub又有一个内部类Proxy,它也是实现于IPersonManager。为什么要搞的这么复杂呢?
原因在于这个IPersonManager既要在服务端使用,又要在客户端使用。对于客户端来说,使用proxy这个代理类,来调用服务端的函数功能。这个代理类的存在就是为了屏蔽binder的相关信息的。让客户端的调用者像是直接在操作服务端的函数一样。
像在客户端的ServiceConnection类中onServiceConnected函数中:
public void onServiceConnected(ComponentName name, IBinder service) { ... mPersonManager = IPersonManager.Stub.asInterface(service); ... }
返回的就是proxy对象,而Proxy真正的执行者是它的mRemote这个成员对象,
private android.os.IBinder mRemote;可以看出它仅是一个IBinder引用而已,
而此时调用mPersonManager.addPerson时实现上就是调用了Proxy类中的addPerson函数:
@Override public void addPerson(com.zyftest.myipc.Person person) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); if ((person != null)) { _data.writeInt(1); person.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } }
最终就是通过mRemote这个binder引用去传送数据给服务端。
而在服务端中,实现的却是private final IPersonManager.Stub mPersonManager=new IPersonManager.Stub(){…}
这样在客户端使用了mRemote.transact后,服务端会在stub.onTransact函数中处理请求:
case TRANSACTION_getPersons: { data.enforceInterface(DESCRIPTOR); java.util.List<com.zyftest.myipc.Person> _result = this.getPersons(); reply.writeNoException(); reply.writeTypedList(_result); return true; }
这里的
java.util.List<com.zyftest.myipc.Person> _result = this.getPersons();就会调到MyIPC_Service.java 的IPersonManager.Stub mPersonManager中的:
public List<Person> getPersons() throws RemoteException { // TODO Auto-generated method stub synchronized (this) { Log.e(TAG, "invoking getPersons() method , now the list is : " + mPersons.toString()); if (mPersons != null) { return mPersons; } return new ArrayList<Person>(); } }
然后把结果打包并写回binder驱动中,返回给客户端。
所以,IPersonManager.java既是给客户端用的,也是给服务端用的。客户端主要是使用里边的stub.proxy的抽象类,服务端主要是使用里边的stub抽象类。(这个描述不准确,意思理解了就行)。
在 IPersonManager.java中定义了一些操作:
static final int TRANSACTION_getPersons = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
这些是为了在进程通讯中使用一个统一的协议。
RPC(Remote Procedure Call Protocol)——远程过程调用协议
毕竟,远程通讯或者说跨进程调用中,使用一套统一的协议才能方便通讯。
相关文章推荐
- Android 中 AIDL 的理解与使用
- [学习笔记]Android中AIDL的理解与使用
- Android 中AIDL的使用与理解
- Android 中AIDL的使用与理解
- Android 中AIDL的使用与理解 (一)
- AIDL 的理解与使用(一种android内部进程通信接口的描述语言)
- Android 中AIDL的使用与理解
- Android AIDL(接口定义语言)简单理解和基本使用方法
- Android基础知识--9.Android中AIDL的理解与使用
- Android中Binder机制理解及AIDL使用基本步骤
- Android AIDL的理解和使用
- Android 中AIDL的使用与理解
- android: 通过AIDL使用SERVICE
- android: 使用AIDL实现进程间通信(附示例源码下载)
- Android AIDL远程服务使用示例
- android进程间通信:使用AIDL
- Android 使用 aidl 文件创建服务示例
- (转)使用android中的AIDL让Service与Activity通信(service回调activity)
- **android进程间通信:使用AIDL .**
- Android中ListView中使用CheckedTextView和CheckBox的理解