博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android中的Binder(一)
阅读量:4095 次
发布时间:2019-05-25

本文共 9385 字,大约阅读时间需要 31 分钟。

前言: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。如下图:

你可能感兴趣的文章
【JAVA数据结构】双向链表
查看>>
【JAVA数据结构】先进先出队列
查看>>
谈谈加密和混淆吧[转]
查看>>
乘法逆元
查看>>
Objective-C 基础入门(一)
查看>>
关于fwrite写入文件后打开查看是乱码的问题
查看>>
用结构体指针前必须要用malloc,不然会出现段错误
查看>>
Linux系统中的美
查看>>
一些实战项目(linux应用层编程,多线程编程,网络编程)
查看>>
STM32CubeMX 真的不要太好用
查看>>
不要买铝合金机架的无人机,不耐摔,易变形弯曲。
查看>>
ACfly也是基于FreeRTOS的
查看>>
ROS的安装(包含文字和视频教程,我的ROS安装教程以这篇为准)
查看>>
原来我之前一直用的APM固件....现在很多东西明白了。
查看>>
realsense-ros里里程计相关代码
查看>>
似乎写个ROS功能包并不难,你会订阅话题发布话题,加点逻辑处理,就可以写一些基础的ROS功能包了。
查看>>
我觉得在室内弄无人机开发装个防撞机架还是很有必要的,TBUS就做得很好。
查看>>
serial也是见到很多次了,似乎它就是一种串行通信协议
查看>>
TBUS的一些信息
查看>>
专业和业余的区别就在于你在基础在基本功打磨练习花的时间
查看>>