Android中的Binder(一)
2016-04-24 11:37
477 查看
前言:Binder的中文意思是“别针”/“回形针”,顾名思义,Binder的作用在于将不同的进程之间“别”在一起,完成IPC(Inter-process communication进程间通信)过程,那么这个Binder究竟是什么东西呢?原理又是什么呢?aidl又是必要的吗?本地调用和远程调用又有什么区别呢?接下来让我们一起来揭开其神秘面纱,在此谢过《Android内核剖析》一书,其中多处引自此书。aidl见下一篇博文。
注意:由图我们注意到事实上对于一个服务来讲,存在着两个Binder对象,一个是服务端的Binder对象,一个是Binder驱动中的Binder对象(mRemote)。但是对于程序员来讲,这又是透明的,即是客户端获得的Binder引用是哪一个程序员客户端程序员无需搭理。事实上,当客户端和服务端处于同一个Android应用的时候,客户端直接获取服务端的Binder对象的引用(非IPC);而当客户端服务端分属不同应用的时候,客户端获取到的是Binder驱动中的Binder对象mRemote的引用。至于为什么如此,因为不同的应用中数据是不能够直接进行共享的,因此必须通过Binder驱动进行中转。而对于同一应用,直接获取服务端Binder对象并进行方法调用显然是更加高效的方法。
(上图可以看出服务端和客户端属于不同的应用,并且客户端获取到的是Binder驱动中的mRemote对象!而且客户端访问服务端的时候有两种方式,一种是直接通过mRemote的transact()方法,另一种是使用aidl。这也证明了aidl并不是必须的,我们将在下一篇进行讨论)
下面对上图中的各个模块进行介绍:
服务端:一个Binder服务端实际上就是一个Binder对象。该对象一旦创建,内部就会启动一个隐藏线程,正是这个线程可以跟Binder驱动进行交互。收到Binder驱动消息之后,调用服务端的onTransact()函数,根据不同的参数执行不同的功能代码,执行完毕之后同样通过隐藏线程返回消息。
Binder驱动:在任意一个服务端Binder创建的时候会在Binder驱动中创建一个mRemote对象(也是Binder类)。当客户端本地访问服务端的时候,直接将服务端Binder返回,当客户端远程访问服务端的时候,会将Binder驱动中的mRemote对象返回。这对于程序员是透明的。
mRemote对象重载了transact()方法:
a.向服务端“隐藏线程”发送调用消息。
b.将当前访问客户端的线程挂起,等待服务完毕之后的通知。
c.服务端执行完毕之后,通过“隐藏线程”返回消息给Binder驱动,此时重新激活客户端线程,返回客户端代码区。
客户端:获取到Binder对象的引用之后,便可以访问服务端的服务了。
ServiceManager:用于管理Service服务。
客户端和服务端必须约定好服务端不同函数的标识,还有服务端函数的参数在包裹中的顺序。
为了获得服务端Binder的引用,Android为我们提供了Service类,它有两个方法。
a,startService()启动服务,客户端暂时没有服务端Binder的引用,不能调用服务端的任何服务。
b.bindService()方法,这个方法的第二个参数是ServiceConnection对象,这个对象的onServiceConnected()方法能帮助我们获得服务端的Binder引用,这是一种回调。
因此startService()启动服务的方法不在我们的讨论范围。我们重点来看看bindService()方法启动服务的情况下,本地调用和远程调用的区别。
客户端请求AmS启动某一个Service之后,该Service若是启动正常,那么AmS会远程调用ActivityThread类中的ApplicationThread对象,调用的参数会包含Service的Binder引用,然后在ApplicationThread中会回调bindService中的conn接口,于是客户端便有了Binder对象的引用。若是本地调用,则返回的是服务端的Binder对象,若是远程调用,则返回的是Binder驱动中的Binder对象。底层细节对程序员透明。
小结:那么我们看到,本地客户端使用bindService()方法获取到的Binder对象的确是客户端的Binder对象!而非Binder驱动中的Binder对象。如下图:
服务端:
位于另一应用中的客户端:
运行结果:
小结:
由上例我们可以看到,aidl工具并非是必须的,aidl只是为了保证我们上面提到的第二个约定,即是统一了包裹内写入参数的顺序和函数标识的问题。事实上也正如我们看到的,我们可以手动保证这个约定的实现!而对于第一个约定,即我们如何获取到Binder的问题,也可以看到,我们是通过ServiceConnection的回调方法获取到的,注意这个Binder是Binder驱动中的Binder对象(mRemote),并非是服务端的Binder。如下图:
一、什么是Binder?
事实上,Binder是一个工作在Linux层面的驱动,其驱动代码运行于内核态。为IPC提供了重要的底层基础。比如Messenger和aidl等IPC方式均是由Binder支持的。当然也有诸如共享文件等不依赖于Binder的IPC方式。二、Binder架构的组成
Binder架构包含服务端接口,Binder驱动,客户端接口三个部分。框架图如下:注意:由图我们注意到事实上对于一个服务来讲,存在着两个Binder对象,一个是服务端的Binder对象,一个是Binder驱动中的Binder对象(mRemote)。但是对于程序员来讲,这又是透明的,即是客户端获得的Binder引用是哪一个程序员客户端程序员无需搭理。事实上,当客户端和服务端处于同一个Android应用的时候,客户端直接获取服务端的Binder对象的引用(非IPC);而当客户端服务端分属不同应用的时候,客户端获取到的是Binder驱动中的Binder对象mRemote的引用。至于为什么如此,因为不同的应用中数据是不能够直接进行共享的,因此必须通过Binder驱动进行中转。而对于同一应用,直接获取服务端Binder对象并进行方法调用显然是更加高效的方法。
(上图可以看出服务端和客户端属于不同的应用,并且客户端获取到的是Binder驱动中的mRemote对象!而且客户端访问服务端的时候有两种方式,一种是直接通过mRemote的transact()方法,另一种是使用aidl。这也证明了aidl并不是必须的,我们将在下一篇进行讨论)
下面对上图中的各个模块进行介绍:
服务端:一个Binder服务端实际上就是一个Binder对象。该对象一旦创建,内部就会启动一个隐藏线程,正是这个线程可以跟Binder驱动进行交互。收到Binder驱动消息之后,调用服务端的onTransact()函数,根据不同的参数执行不同的功能代码,执行完毕之后同样通过隐藏线程返回消息。
Binder驱动:在任意一个服务端Binder创建的时候会在Binder驱动中创建一个mRemote对象(也是Binder类)。当客户端本地访问服务端的时候,直接将服务端Binder返回,当客户端远程访问服务端的时候,会将Binder驱动中的mRemote对象返回。这对于程序员是透明的。
mRemote对象重载了transact()方法:
a.向服务端“隐藏线程”发送调用消息。
b.将当前访问客户端的线程挂起,等待服务完毕之后的通知。
c.服务端执行完毕之后,通过“隐藏线程”返回消息给Binder驱动,此时重新激活客户端线程,返回客户端代码区。
客户端:获取到Binder对象的引用之后,便可以访问服务端的服务了。
ServiceManager:用于管理Service服务。
三、使用Binder的时候必须解决的两个问题
客户端如何获得服务端Binder对象的引用。客户端和服务端必须约定好服务端不同函数的标识,还有服务端函数的参数在包裹中的顺序。
为了获得服务端Binder的引用,Android为我们提供了Service类,它有两个方法。
a,startService()启动服务,客户端暂时没有服务端Binder的引用,不能调用服务端的任何服务。
b.bindService()方法,这个方法的第二个参数是ServiceConnection对象,这个对象的onServiceConnected()方法能帮助我们获得服务端的Binder引用,这是一种回调。
因此startService()启动服务的方法不在我们的讨论范围。我们重点来看看bindService()方法启动服务的情况下,本地调用和远程调用的区别。
四、客户端获取Binder对象的流程
客户端请求AmS启动某一个Service之后,该Service若是启动正常,那么AmS会远程调用ActivityThread类中的ApplicationThread对象,调用的参数会包含Service的Binder引用,然后在ApplicationThread中会回调bindService中的conn接口,于是客户端便有了Binder对象的引用。若是本地调用,则返回的是服务端的Binder对象,若是远程调用,则返回的是Binder驱动中的Binder对象。底层细节对程序员透明。
五、本地调用服务端实例
服务端:public class MyServer extends Binder { /** * Binder父类的一个方法,用于区分客户端的请求,决定调用哪一个服务端的方法 */ @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case 1000: service1(); break; case 1001: data.enforceInterface("MyServer"); String arg = data.readString(); service2(arg); break; default: break; } return super.onTransact(code, data, reply, flags); } public void service1() { System.out.println("----service1---"); } public void service2(String arg) { System.out.println("----service2---the arg is :" + arg); } }注意这里我们用了一个全局MyServer对象。用于验证客户端获取到的Binder对象和服务端Binder对象是否是同一个。
public class ServerService extends Service { public static MyServer myServer = new MyServer(); @Override public void onCreate() { Log.i("DEBUG", "---onCreate()---"); super.onCreate(); } @Override public IBinder onBind(Intent intent) { Log.i("DEBUG", "---onBind()---"); return myServer; } @Override public boolean onUnbind(Intent intent) { Log.i("DEBUG", "---onUnbind()---"); return super.onUnbind(intent); } }客户端:
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 IBinder mIBinder; @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 btn_getService1 = (Button) findViewById(R.id.btn_getService1); btn_getService1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int code = 1000; Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); // 用于对访问的服务端Binder进行验证 data.writeInterfaceToken("MyServer"); try { mIBinder.transact(code, data, reply, 0); } catch (RemoteException e) { e.printStackTrace(); } finally { data.recycle(); reply.recycle(); } } }); // 获取服务端服务2 btn_getService2 = (Button) findViewById(R.id.btn_getService2); btn_getService2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int code = 1001; Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); // 用于对访问的服务端Binder进行验证 data.writeInterfaceToken("MyServer"); data.writeString("hello"); try { mIBinder.transact(code, data, reply, 0); } catch (RemoteException e) { e.printStackTrace(); } finally { data.recycle(); reply.recycle(); } } }); } /** * 将会获得从服务端传来的Binder对象 */ private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { mIBinder = service; if (mIBinder == ServerService.myServer) { System.out.println("客户端获取到了服务端的Binder对象引用"); } } }; }运行结果:
小结:那么我们看到,本地客户端使用bindService()方法获取到的Binder对象的确是客户端的Binder对象!而非Binder驱动中的Binder对象。如下图:
六、远程调用服务端实例
代码基本一致,但是无法模拟上面设置全局变量进行验证的方法,但是我们知道,客户端获取的Binder对象必然不是服务端中的Binder对象,因为不同应用中的变量无法直接共享。这个Bidner对象是Binder驱动中的mRemote。服务端:
public class MyServer extends Binder { @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case 1000: service1(); break; case 1001: data.enforceInterface("MyServer");//用于验证客户端确实想访问这个服务端 String arg = data.readString(); service2(arg); break; default: break; } return super.onTransact(code, data, reply, flags); } public void service1() { System.out.println("----service1---"); } public void service2(String arg) { System.out.println("----service2---the arg is :" + arg); } }
public class ServerService extends Service { @Override public void onCreate() { Log.i("DEBUG", "---onCreate()---"); super.onCreate(); } @Override public IBinder onBind(Intent intent) { Log.i("DEBUG", "---onBind()---"); return new MyServer();//当客户端请求的时候,将服务端Binder返回。 } @Override public boolean onUnbind(Intent intent) { Log.i("DEBUG", "---onUnbind()---"); return super.onUnbind(intent); } }
位于另一应用中的客户端:
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 IBinder mIBinder; @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 btn_getService1 = (Button) findViewById(R.id.btn_getService1); btn_getService1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int code = 1000; Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); // 用于对访问的服务端Binder进行验证 data.writeInterfaceToken("MyServer"); try { mIBinder.transact(code, data, reply, 0); } catch (RemoteException e) { e.printStackTrace(); } finally { data.recycle(); reply.recycle(); } } }); // 获取服务端服务2 btn_getService2 = (Button) findViewById(R.id.btn_getService2); btn_getService2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int code = 1001; Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); // 用于对访问的服务端Binder进行验证 data.writeInterfaceToken("MyServer"); data.writeString("hello"); try { mIBinder.transact(code, data, reply, 0); } catch (RemoteException e) { e.printStackTrace(); } finally { data.recycle(); reply.recycle(); } } }); } /** * 将会获得从服务端传来的Binder对象(Binder驱动中的) */ private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { mIBinder = service; } }; }
运行结果:
小结:
由上例我们可以看到,aidl工具并非是必须的,aidl只是为了保证我们上面提到的第二个约定,即是统一了包裹内写入参数的顺序和函数标识的问题。事实上也正如我们看到的,我们可以手动保证这个约定的实现!而对于第一个约定,即我们如何获取到Binder的问题,也可以看到,我们是通过ServiceConnection的回调方法获取到的,注意这个Binder是Binder驱动中的Binder对象(mRemote),并非是服务端的Binder。如下图:
相关文章推荐
- mac android studio config
- Android中的diglog对话框
- Android中使用GridLayout网格布局来制作简单的计算器App
- 【读书笔记】Android安全机制解析与应用实践之源码分析
- 在ADT和Android Studio中配置和使用Git
- Android中使用ContentProvider管理系统资源的实例
- Android控件之ListView(一)
- Android中Intent组件详解
- RxJava大全以及RxAndroid
- Android学习第八周_使用率超高的多线程开发
- Android控件之View的事件分发机制探索
- Android控件篇之视图控件scrollview探索
- 初步了解响应式框架——agera
- Android中的Intent对象完全解析
- 【整理自慕课网】Android动感系列-扫一扫 ZXing
- android Webview中注入js,用于解决第三方页中出现返回事件window.history.go(-1),无法响应问题
- 加速Android Studio/Gradle构建
- Android AIDL 进程之间的通信
- Xamarin.Android之布局文件智能提示问题
- Android中调用系统的文件浏览器及自制简单的文件浏览器