您的位置:首页 > 移动开发 > Android开发

Android中的Binder(一)

2016-04-24 11:37 477 查看
前言:Binder的中文意思是“别针”/“回形针”,顾名思义,Binder的作用在于将不同的进程之间“别”在一起,完成IPC(Inter-process communication进程间通信)过程,那么这个Binder究竟是什么东西呢?原理又是什么呢?aidl又是必要的吗?本地调用和远程调用又有什么区别呢?接下来让我们一起来揭开其神秘面纱,在此谢过《Android内核剖析》一书,其中多处引自此书。aidl见下一篇博文。

一、什么是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。如下图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: