Android中Binder的学习

在Android中Binder用于完成IPC(进程间通信),Binder工作在Linux层面,属于一个驱动,只是这个驱动不需要硬件,或者说其操作的硬件是一小段内存。客户端调用Binder是通过 系统调用完成的。

Binder简单介绍

Binder是一种架构,这种架构提供了服务端的接口,Binder的驱动,客户端接口三个模块。

首先来看服务端,一个Binder服务端就是一个Binder类的对象,该对象一创建,内部就启动一个隐藏线程,该线程接下来会接收Binder驱动发送来的消息, 收到消息后,会执行到Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务代码。要实现一个Binder服务,必须重载onTransact方法。
重载onTransact函数的主要内容是吧该函数的参数转换为服务函数的参数,而onTransact函数的参数来源是客户端调用transact函数时输入的,因此,如果transact有固定的格式的输入,那么onTransact就会有固定格式的输出。

下面看看Binder驱动,任意一个服务端Binder对象被创建时,同时会在Binder驱动中创建一个mRemote对象,该对象的类型也是Binder类。客户端访问远程服务时,都是通过mRemote对象。

最后来看看客户端,客户端想要访问远程服务,必须获取远程服务在Binder对象中对应的mRemote引用,获得该对象,就可以调用其transact方法。

以上就是客户端通过驱动调用了服务端的代码。
对应用程序的开发人员来讲,客户端似乎是直接调用远程服务对应的Binder,而事实上则是通过Binder驱动进行了中转。即存在两个Binder对象,一个是服务端的Binder对象,另一个则是Binder驱动中的对象,所不同的是Binder驱动中的对象不会再额外产生一个线程。


获取Binder对象

事实上, 可以完全不使用Service类,而仅仅基于Binder类编写服务程序,但是只是一部分。具体来讲,可以仅使用Binder类扩展系统服务,而对于客户端服务则必须基于Service类来编写。所谓的系统服务是指可以使用getSystemService方法获取的服务,所谓的客户端服务是指应用程序提供的自定义服务。

对于客户端来讲,可以使用以下两个函数来和服务建立连接,其原型在android.app.ContextImpl类中。

public ComponentName startServce(Intent intent);

该函数用于启动intent指定的服务,而启动后,客户端暂时还没有服务端的Binder引用,因此,暂时还不能调用任何服务功能。

public boolean bindService(Intent service, ServiceConnection conn, int flags);

该函数用于绑定一个服务,这就是第一个重要问题的关键所在。其中第二个参数是一个interface类,该interface的定义如以下代码所示:

public interface ServiceConnection{
public void onServiceConnected(ComponentName name, IBinder service);
public void onServiceDisconnected(ComponetName name);
}

请注意该interface中的onServiceConnected方法的第二个变量service,当客户端请求AmS启动某个Service后,该Service如果正常启动,那么AmS就会远程调用ActivityThread类中的ApplicationThread对象,调用的参数中会包含Service的Binder引用,然后在ApplicationThread中会回调binderService中的conn接口。因此,在客户端中,可以在onServiceConnected方法中将其参数service保存为一个全局变量,从而在客户端的任何地方都可以调用该远程服务。这就是客户端如何获取远程服务的Binder引用。


AIDL工具简单介绍

此处不进行aidl的使用介绍,及编写规范。

通过AIDL工具可以把一个aidl文件转换为一个java类文件,该java类文件,同时重载了transact和onTransact方法。这些代码主要完成以下三个任务:

  • 定义一个Java interface,内部包含aidl文件所声明的服务函数,并且该类基于android.os.IInterfce接口,即需要提供一个asBinder函数。
  • 定义一个Proxy类,该类将作为客户端程序访问服务端的代理。
  • 定义一个Stub类,这是一个abstract类,基于Binder类。

在Stub类中,除了以上所述的任务外,Stub还提供了一个asInterface函数。提供这个函数的作用是这样的,首先需要明确的是,aidl所产生的代码完全可以由程序员手工编写。提供这个函数的原因是服务端提供的服务除了其他进程可以使用外,在服务进程内部的其他类也可以使用该服务,对于后者,显然是不需要经过IPC调用。

而可以直接在进程内部调用,而Binder内部有个queryLocalInterface(String description)函数。

public static com.cqumonk.calculate.aidl.ICalculateAIDL asInterface(android.os.IBinder obj) { if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);//根据包名获取本地实现的一个接口的实例,如果是本地service则可以获取到
if (((iin != null) && (iin instanceof com.cqumonk.calculate.aidl.ICalculateAIDL))) { return ((com.cqumonk.calculate.aidl.ICalculateAIDL) iin); //如果得到的实例是ICalculateAIDL的对象,则返回
}
return new com.cqumonk.calculate.aidl.ICalculateAIDL.Stub.Proxy(obj);//如果无法得到本地实现的对象则会返回一个代理对象}

当创建一个Binder对象时,服务端进程内部创建一个Binder对象,Binder驱动中也会创建一个Binder对象。如果从远程获取服务端的Binder,则只会返回Binder驱动中的Binder对象,而如果从服务端进程内部获取Binder对象,则会获取服务端本身的Binder对象。

系统服务中的Binder对象

在编程时,经常使用getSystemService方法获取一个系统服务。getSystemService函数的实现是在ContextImpl类中,该函数所返回的Service比较多,这些Service一般都由ServiceManager管理。

ServiceManager是一个独立进程,本身也是一个Service,Framework提供了一个系统函数,可以获取该Service对应的Binder引用,那就是BinderInternal.getContextObject()。该静态函数返回ServiceManager后,就可以通过ServiceManager提供的方法获取其他系统Service的Binder引用。

以上内容记录Binder的学习理解。参考《Android内核剖析》。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章