Binder是Android系统提供的一种IPC机制(Inter-Process Communication,进程间通信)。
进程是系统进行资源分配和调度的基本单位,进程的用户空间是互相独立的,那么两个进程之间的数据如何交互?一种简单的方式,采用c/s模式,分成客户端和服务端,客户端分送数据,服务端接收处理后,给出响应,这样就实现了数据的交互。
Binder架构
Binder就是一种c/s架构,分为客户端、Binder 驱动、服务端三个模块。
- 客户端通过 IBinder.transact() 方法将数据传递给Binder 驱动。
- Binder 驱动接收到数据,将这个数据传递给对应的服务端,并将当前线程(客户端线程)阻塞,等待服务端消息返回。
- 服务端接收到数据,并调用Binder.onTransact()方法进行相应处理,再将结果返回给Binder 驱动。
- Binder 驱动接收的数据,唤醒客户端线程,将结果返回给客户端。
整个Binder框架远比这个复杂,应用层Binder其实都是基于native(C/C++)层的binder架构,Binder 驱动其实是在Linux的kernel层。这里我们只分析Binder在应用层的体现,而native层的binder架构在下一篇文章中介绍。
上面的流程,有两个重要的方法 IBinder.transact、Binder.onTransact。
public interface IBinder {
public boolean transact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException;
}
注意这个方法是定义在IBinder接口中的,发送Binder请求。
- code:请求码,主要用于在Binder.onTransact方法中,标识客户端期望调用服务端的哪个函数。
- data:客户端请求数据都储存这个对象里面。
- reply:服务端返回的数据都储存在这个对象里。(当然要等服务端正确返回之后,这个对象里才有值)。
- flags:这是一个标志位,一种是双向,用常量 0 表示, 其含义是服务端执行完指定服务后会返回一定的数据;另一种是单向,用常量 1 表示,其含义是不返回 任何数据。
public class Binder implements IBinder {
protected boolean onTransact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
}
}
这个方法在Binder里,根据客户端发送过来的数据,调用响应的方法处理后,将结果存储在reply里,然后返回给Binder驱动。
注意onTransact方法中的data和reply和transact方法里的,并不是同一个对象,因为它们是在两个进程中的。其实过程是这个样子:
- 客户端通过transact方法将data数据赋值给自己进程的Binder驱动。
- 然后本进程的Binder驱动,会找到目标进程的Binder驱动,将数据重新复制一份传递给目标Binder驱动,然后目标进程的Binder驱动会调用服务端,并将数据传递给它。
- 服务端处理完数据后,将结果写入reply,然后在传给Binder驱动。
因此我们要进行Binder调用,只要获取一个IBinder对象,调用它的transact方法,然后它就会调用到Binder实体的onTransact方法,返回对应的结果。
但是这样做起来比较繁琐:
- 要保证transact和onTransact方法中code是对应的,这样你才能调用到onTransact正确的处理逻辑。
- 我们每次都要手动地将数据设置到Parcel中,然后在另一个方法从Parcel中按照放入顺序一一取出来,这样才能使用。
AIDL (Android接口定义语言)
那么有没有一种简单地方式,将这些繁琐的操作都封装起来呢?
一种简单地方式就是定义一系列接口方法,每个接口方法对应一个code,按照方法中参数的顺序,将参数放入Parcel,然后在另一个方法中一一取出。这种方式就是aidl(Android Interface Definition Language)。
package com.binder.bindertest;
interface IMyService {
void setName(String name);
String getName();
}
简单定义了IMyService.aidl,下面看看工具帮我们生成的java类。
package com.binder.bindertest;
public interface IMyService extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder
implements com.binder.bindertest.IMyService {
private static final java.lang.String DESCRIPTOR =
"com.binder.bindertest.IMyService";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.binder.bindertest.IMyService
* interface,generating a proxy if needed.
*/
public static com.binder.bindertest.IMyService asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.binder.bindertest.IMyService))) {
return ((com.binder.bindertest.IMyService) iin);
}
return new com.binder.bindertest.IMyService.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_setName: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setName(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getName: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.binder.bindertest.IMyService {
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 setName(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_setName, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.lang.String getName() 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_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_setName =
(android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getName =
(android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void setName(java.lang.String name) throws android.os.RemoteException;
public java.lang.String getName() throws android.os.RemoteException;
}
IMyService
首先来看一下,IMyService继承了IInterface接口,但没有继承IBinder接口。
public interface IMyService extends android.os.IInterface {
}
public interface IInterface
{
public IBinder asBinder();
}
这个接口非常简单,就是返回一个IBinder对象,因此它的子类中肯定有IBinder这个成员变量。所以IMyService是采用的组合的方式,通过IBinder这个成员属性的transact方法,来发送Binder请求的。
代理类Proxy
private static class Proxy implements com.binder.bindertest.IMyService {
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 setName(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_setName, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.lang.String getName() 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_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
这个Proxy类有一个mRemote成员变量,通过它来进行Binder请求,它实现IMyService方法,只是将方法中的参数,存放到_data中,然后调用transact方法,最后再从_reply中取出结果值,返回给调用处。
抽象类Stub
public static abstract class Stub extends android.os.Binder implements
com.binder.bindertest.IMyService {
private static final java.lang.String DESCRIPTOR =
"com.binder.bindertest.IMyService";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
}
继承自Binder类,说明它要实现onTransact方法,作为服务端处理客户端transact方法发送来的消息。它implements了IMyService接口,它并没有真正实现IMyService接口方法,而是由它子类去实现这些方法。
DESCRIPTOR:这个常量作为Parcel的验证参数。
public static com.binder.bindertest.IMyService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.binder.bindertest.IMyService)))
{
return ((com.binder.bindertest.IMyService) iin);
}
return new com.binder.bindertest.IMyService.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
asInterface方法并不是Binder中的方法,它返回一个IMyService对象的实例,目前来看这个IMyService对象有两种类型的子实例:
- Stub这个抽样类子类的实例,它就是Binder实体,那么就不用通过Binder机制了,直接调用Binder实体对应方法了。
- Proxy这个类的实例。那么它就要调用IBinder的transact方法,通过Binder驱动,接着调用到Stub的onTransact方法,最后服务端实例对应的方法。
@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_setName: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
this.setName(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getName: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
根据code码,调用不同的方法,最后将结果写入reply中。
通过aidl,我们就将Binder请求中一些繁琐的参数准备细节都封装起来了,我们只需要实现接口方法就行了。
自定义远程service
下面我们用一个自定义远程service的例子,来详细说明一下。
package com.binder.bindertest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onCreate() {
super.onCreate();
Log.e("zhang", "onCreate pid=="+ Process.myPid());
}
private IBinder binder = new IMyService.Stub() {
private String name;
@Override
public void setName(String name) throws RemoteException {
this.name = name;
Log.e("zhang", "setName name== "+name+" pid=="+ Process.myPid());
}
@Override
public String getName() throws RemoteException {
Log.e("zhang", "getName name== "+name+" pid=="+ Process.myPid());
return name;
}
};
}
自定义一个MyService,在onBind方法中返回一个IMyService.Stub对象。它是Binder子类,实现了IMyService接口的方法。
package com.binder.bindertest;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bindService();
}
});
}
private void bindService() {
Intent intent = new Intent(this, MyService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("zhang", "onServiceConnected start pid=="+ Process.myPid());
IMyService myService = IMyService.Stub.asInterface(service);
try {
myService.setName("zhang");
String result = myService.getName();
Log.e("zhang", "onServiceConnected result=="+result+" pid=="+ Process.myPid());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
}
通过bindService方法启动并绑定自定义的Service,然后在ServiceConnection的onServiceConnected获取我们想要的IBinder对象。
主要这个IBinder对象,根据所在进程不同,值不一样。
- 如果MainActivity和MyService在同一个进程,那么这个IBinder对象就是MyService onBind方法返回的那个IBinder实例。
- 如果MainActivity和MyService不在同一个进程,那么这个IBinder对象就是返回的那个IBinder实例代理对象。
所以调用asInterface方法获取的结果也是不一样的。
<service
android:name=".MyService"
android:process=":remote"
>
<intent-filter>
<action android:name="com.binder.bindertest.MyService" />
</intent-filter>
</service>
最后注意在AndroidManifest.xml文件中将<service 修改成上面方式。
这样当点击按钮时,会发现MainActivity和MyService中调用Process.myPid()方法获取的pid是不一样的,但是它们实现了数据交互。
各种系统服务
在android中各种系统服务就是通过Binder机制进行信息交互的。来看看它们是怎么实现的。就以InputMethodManager为例:
InputMethodManager
public static InputMethodManager getInstance() {
synchronized (InputMethodManager.class) {
if (sInstance == null) {
try {
sInstance = new InputMethodManager(Looper.getMainLooper());
} catch (ServiceNotFoundException e) {
throw new IllegalStateException(e);
}
}
return sInstance;
}
}
调用InputMethodManager构造函数方法。
InputMethodManager(Looper looper) throws ServiceNotFoundException {
this(IInputMethodManager.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE)), looper);
}
它也有个IInputMethodManager接口,定义了对InputMethodManager操作一系列方法,通过IBinder对象向InputMethodManager的服务端进行请求,而它的IBinder对象是通过ServiceManager获取。
ServiceManager
public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
final IBinder binder = getService(name);
if (binder != null) {
return binder;
} else {
throw new ServiceNotFoundException(name);
}
}
调用getService方法,如果得到的binder对象是空,就抛出异常。
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(getIServiceManager().getService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
先从缓存sCache中获取,如果获取不到,那么就要通过IServiceManager对象来获取了。
public interface IServiceManager extends IInterface
{
public IBinder getService(String name) throws RemoteException;
public IBinder checkService(String name) throws RemoteException;
public void addService(String name, IBinder service, boolean allowIsolated)
throws RemoteException;
public String[] listServices() throws RemoteException;
public void setPermissionController(IPermissionController controller)
throws RemoteException;
static final String descriptor = "android.os.IServiceManager";
}
- IServiceManager是一个接口,继承了IInterface(就相当于我们自定义的IMyService),
- ServiceManagerNative类继承了Binder实现了IServiceManager(就相当于Stub)
- ServiceManagerProxy实现了IServiceManager(就相当于Proxy)。这两个类都在ServiceManagerNative.java文件中。
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}
可以看出IServiceManager中所需要的远程代理IBinder实例,是由BinderInternal.getContextObject()方法返回的,而这个方法是通过jni从native端获取的。
小结
由此可以得出,每个系统服务Service,都有一个IXXX接口(例如InputMethodManager对应IInputMethodManager),这个接口定义了操作本服务一系列方法,这个接口的实例中有一个IBinder属性,通过它来调用远程Service真正实现的方法。而IBinder这个实例是通过IServiceManager获取的。
而IServiceManager它对应的实例ServiceManagerProxy也是一个代理对象。它也是通过Binder机制,从服务端ServiceManager中获取这个IBinder对象的。而服务端ServiceManager是在native层实现的,下一章我们会说明的。
网友评论