Android中的Binder(二)
2016-05-06 23:30
525 查看
前言:回顾一下上一篇博文,上一篇说到,Binder是Android系统中IPC机制的底层依赖,描绘了Binder这种通信架构的基本原理图,并且谈到了使用Binder时需要解决的两个问题。即:1.客户端如何获取Binder对象的引用。2.如何协商服务端的函数标识和参数放入包裹的顺序问题。以便于客户端调用服务时,服务端能识别并提供正确的服务。通过上一篇的介绍,相信大家已经知道了客户端通过Service来获取Binder引用的方式,并且也了解了通过重写服务端Binder的onTransact()方法和客户端Binder的transact()方法来解决上述的第二个问题。对于有经验的程序员,完全可以通过这两种做法来实现IPC通信。于此同时,Google的大神们也为开发者提供了更为方便的AIDL工具,这个工具可以帮我们解决第二个问题,这样,开发者就可以完全专注于业务本身。俗话说:“不做重复的轮子”,既然有这种工具,何乐而不用呢?接下来让我们来走进AIDL。之所以将AIDL放在Binder这里讲,是因为这里着重分析AIDL如何解决Binder通信中的第二个问题。若是这里感觉有点迷糊的话,可以看看上一篇博文《Android中的Binder(一)》
aidl接口定义,新建com.example.aidl包,新建IMyServerService接口(自行命名),点击保存,可以在gen目录下找到eclipse帮我们自动生成的类IMyServerService.java。之所以新建包,是因为这样等会可以直接将整个包拷贝到客户端中。
客户端代码,同样注意与上一篇的比较,同样体现在注释中。
服务端不需要界面直接运行,客户端界面比较简单就不写出来了。
运行结果如下:
工作是正常的。
我们看到Stub类继承自Binder类,因此其本质也是一个Binder类,这也是我们能将其当成服务端Binder返回的原因。其次Stub类实现了我们在aidl文件中定义的接口,但是它本身是一个抽象类,并未为我们提供接口方法的实现,因此我们需要在服务端新建的Binder(Stub)对象的时候提供接口方法的具体实现,这便是该服务端提供的服务。
接着我们看到Stub类中“最重要”的一个方法asInterface()。不难发现,这个方法在客户端被调用,客户端同时会将获取到的Binder对象传入这个方法中。因此在asInterface()中会进行一次判断,若是发现从客户端传过来的Binder对象是服务端的(queryLocalInterface()方法),则直接向上转型为IMyServerService类型并返回。若是发现从客户端传过来的Binder对象是Binder驱动中的,那么就需要用到Stub的Proxy代理子类了。
仔细看Proxy代理类,它在asInterface()方法判断为远程调用时被实例化并返回给客户端。那么为什么需要代理类呢?为什么不能像本地Binder那样直接向上转型为IMyServerService呢?其实这样是行不通的,因为就像上篇说的,远程调用客户端获得的Binder对象是Binder驱动中的,需要注意的是,虽然服务端的Binder(Stub)对象实现了IMyServerService接口,可以正确转型,但是Binder驱动中的Binder对象并不能实现这种转型!因此我们需要代理,让它为我们操纵这个Binder对象,调用其transact()方法。
到这里相信很多人已经发现了,aidl工具为我们生成的Stub类里面重载了本该在服务端实现的onTransact()方法和本该在客户端实现的transact()方法,这两个方法就是决定方法调用的。因此我们说aidl为我们统一了放入参数的顺序和函数标识的问题!
可能大家对于上述的第三点胡有点疑虑,即代理类真的有必要存在吗的问题,下面进行简单的验证。将CilentActivity作一点小小的修改,改动如下,直接将客户端获取到的Binder对象进行转型。
看到这里,相信大家对于Binder有了比较深的理解了,对AIDL也有了比较浅的认识。利用AIDL可以实现的功能远不及此,有机会下次在讨论。同时对于Binder通信机制来说,AIDL也不是必须的,我们不仅可以像上一篇说的那样,自己重写onTransact()和transact()方法来实现,甚至你可以完全不需要aidl文件,直接写出一个类似于IMyServerService的java类(aidl工具只是为我们生成这个类而已)。
最后在附上aidl中支持的类型:
基本数据类型
String和CharSequence
List(元素需要时aidl支持的)
Map(元素需要是aidl支持的)
AIDL
Parcelable
一、什么是AIDL?
AIDL,全称为Android Interface Definition Language,即android接口定义语言。顾名思义,AIDL只是一种定义语言,用它的规范定义的接口,可以被aidl工具识别并生成对应的类。(aidl工具在sdk中,可以自行查看)。相信这样比较抽象,接下来我们通过一个小小的例子来看看aidl如何工作。二、一个简单的AIDL通信示例
服务端代码,注意与上一篇中的服务端代码进行比较,区别已经在注释中体现出来了。public class ServerService extends Service { /** * 创建服务端binder对象(不用aidl时需要自定义Binder子类,并在Service中返回) * 并且我们不用在服务端自行书写onTransact()方法 */ private Binder serverBinder = new IMyServerService.Stub() { @Override public void service2(String args) throws RemoteException { System.out.println("---service2---:" + args); } @Override public void service1() throws RemoteException { System.out.println("---service1---"); } }; @Override public void onCreate() { Log.i("DEBUG", "---onCreate()---"); super.onCreate(); } @Override public IBinder onBind(Intent intent) { Log.i("DEBUG", "---onBind()---"); return serverBinder; } @Override public boolean onUnbind(Intent intent) { Log.i("DEBUG", "---onUnbind()---"); return super.onUnbind(intent); } }
aidl接口定义,新建com.example.aidl包,新建IMyServerService接口(自行命名),点击保存,可以在gen目录下找到eclipse帮我们自动生成的类IMyServerService.java。之所以新建包,是因为这样等会可以直接将整个包拷贝到客户端中。
package com.example.aidl; interface IMyServerService{ void service1(); void service2(String args); }
客户端代码,同样注意与上一篇的比较,同样体现在注释中。
public class ClientActivity extends Activity { private Button btn_connectService; private Button btn_disconnectService; private Button btn_getService1; private Button btn_getService2; // 服务端Service的action,根据自己而变 private Intent intent = new Intent("com.wwt.server.ServerService"); private IMyServerService mService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 绑定服务端 btn_connectService = (Button) findViewById(R.id.btn_connectService); btn_connectService.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { bindService(intent, connection, Context.BIND_AUTO_CREATE); } }); // 取消绑定服务 btn_disconnectService = (Button) findViewById(R.id.btn_disconnectService); btn_disconnectService.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { unbindService(connection); } }); // 获取服务端服务1。在没有aidl的时候,客户端需要编写transact()方法, // 反之则不用。aidl生成的类自动为我们屏蔽了这一细节,即为我们实现了transact()函数。 btn_getService1 = (Button) findViewById(R.id.btn_getService1); btn_getService1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { mService.service1(); } catch (RemoteException e) { e.printStackTrace(); } } }); // 获取服务端服务2 btn_getService2 = (Button) findViewById(R.id.btn_getService2); btn_getService2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { mService.service2("Hello"); } catch (RemoteException e) { e.printStackTrace(); } } }); } /** * 通过回调获取Binder对象(可能是服务端的,也可能是Binder驱动中的Binder对象, 取决于本地或是远程调用。) */ private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { // IMyServerService的内部类Stub的asInterface()方法会根据客户端获取到的 // Binder对象的来源来决定调用客户端服务的方式,即是否采用代理。 mService =IMyServerService.Stub.asInterface(service); } }; }
服务端不需要界面直接运行,客户端界面比较简单就不写出来了。
运行结果如下:
工作是正常的。
三、AIDL如何解决前言中提到的Binder通信的第二个问题
打开gen目录,找到aidl工具为我们生成的类,我们发现这个类名字和我们定义的接口名称一致,并且继承了IInterface接口。接下来我们只看关键的代码。public static abstract class Stub extends android.os.Binder implements com.example.aidl.IMyServerService { private static final java.lang.String DESCRIPTOR = "com.example.aidl.IMyServerService"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.aidl.IMyServerService * interface, generating a proxy if needed. */ public static com.example.aidl.IMyServerService asInterface( android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.aidl.IMyServerService))) { return ((com.example.aidl.IMyServerService) iin); } return new com.example.aidl.IMyServerService.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_service1: { data.enforceInterface(DESCRIPTOR); this.service1(); reply.writeNoException(); return true; } case TRANSACTION_service2: { data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); this.service2(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.aidl.IMyServerService { 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 service1() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_service1, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } @Override public void service2(java.lang.String args) 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(args); mRemote.transact(Stub.TRANSACTION_service2, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_service1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_service2 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); }需要注意的有以下这些点:
我们看到Stub类继承自Binder类,因此其本质也是一个Binder类,这也是我们能将其当成服务端Binder返回的原因。其次Stub类实现了我们在aidl文件中定义的接口,但是它本身是一个抽象类,并未为我们提供接口方法的实现,因此我们需要在服务端新建的Binder(Stub)对象的时候提供接口方法的具体实现,这便是该服务端提供的服务。
接着我们看到Stub类中“最重要”的一个方法asInterface()。不难发现,这个方法在客户端被调用,客户端同时会将获取到的Binder对象传入这个方法中。因此在asInterface()中会进行一次判断,若是发现从客户端传过来的Binder对象是服务端的(queryLocalInterface()方法),则直接向上转型为IMyServerService类型并返回。若是发现从客户端传过来的Binder对象是Binder驱动中的,那么就需要用到Stub的Proxy代理子类了。
仔细看Proxy代理类,它在asInterface()方法判断为远程调用时被实例化并返回给客户端。那么为什么需要代理类呢?为什么不能像本地Binder那样直接向上转型为IMyServerService呢?其实这样是行不通的,因为就像上篇说的,远程调用客户端获得的Binder对象是Binder驱动中的,需要注意的是,虽然服务端的Binder(Stub)对象实现了IMyServerService接口,可以正确转型,但是Binder驱动中的Binder对象并不能实现这种转型!因此我们需要代理,让它为我们操纵这个Binder对象,调用其transact()方法。
到这里相信很多人已经发现了,aidl工具为我们生成的Stub类里面重载了本该在服务端实现的onTransact()方法和本该在客户端实现的transact()方法,这两个方法就是决定方法调用的。因此我们说aidl为我们统一了放入参数的顺序和函数标识的问题!
可能大家对于上述的第三点胡有点疑虑,即代理类真的有必要存在吗的问题,下面进行简单的验证。将CilentActivity作一点小小的修改,改动如下,直接将客户端获取到的Binder对象进行转型。
<span style="font-size:14px;">mService =IMyServerService.Stub.asInterface(service);</span>改为:
mService =(IMyServerService) service运行结果如下:(类型转换错误,得证)
四、总结
看到这里,相信大家对于Binder有了比较深的理解了,对AIDL也有了比较浅的认识。利用AIDL可以实现的功能远不及此,有机会下次在讨论。同时对于Binder通信机制来说,AIDL也不是必须的,我们不仅可以像上一篇说的那样,自己重写onTransact()和transact()方法来实现,甚至你可以完全不需要aidl文件,直接写出一个类似于IMyServerService的java类(aidl工具只是为我们生成这个类而已)。最后在附上aidl中支持的类型:
基本数据类型
String和CharSequence
List(元素需要时aidl支持的)
Map(元素需要是aidl支持的)
AIDL
Parcelable
相关文章推荐
- Android之十SlidingMenu侧滑菜单的实现分析
- ActiveAndroid:活性记录的风格ORM(对象关系映射)
- Android OpenGL10 输入响应 <6>
- Android中pendingIntent的深入理解
- 【Android】Bitmap优化
- android 插件化 模块化开发
- 使用svn管理android工程
- android-java.net.ConnectException: failed to connect to localhost/127.0.0.1 (port 8080)解决方法
- android 中文文档
- Android的所有权限说明
- Android-文件存储
- Android05-UI02布局,自定义控件,ListView
- Android获取屏幕宽高及状态栏高度的方法
- Android 客户端与服务器端进行数据交互(二、登录客户端)
- 如何使用Google Map API开发Android地图应用
- Android测试之旅之JUnit(二)
- android:ToolBar详解(手把手教程)
- Android 客户端与服务器端进行数据交互(一、登录服务器端)
- android weight属性
- 第10章 Android的消息机制