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

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

前言:回顾一下上一篇博文,上一篇说到,Binder是Android系统中IPC机制的底层依赖,描绘了Binder这种通信架构的基本原理图,并且谈到了使用Binder时需要解决的两个问题。即:1.客户端如何获取Binder对象的引用。2.如何协商服务端的函数标识和参数放入包裹的顺序问题。以便于客户端调用服务时,服务端能识别并提供正确的服务。通过上一篇的介绍,相信大家已经知道了客户端通过Service来获取Binder引用的方式,并且也了解了通过重写服务端Binder的onTransact()方法和客户端Binder的transact()方法来解决上述的第二个问题。对于有经验的程序员,完全可以通过这两种做法来实现IPC通信。于此同时,Google的大神们也为开发者提供了更为方便的AIDL工具,这个工具可以帮我们解决第二个问题,这样,开发者就可以完全专注于业务本身。俗话说:“不做重复的轮子”,既然有这种工具,何乐而不用呢?接下来让我们来走进AIDL。之所以将AIDL放在Binder这里讲,是因为这里着重分析AIDL如何解决Binder通信中的第二个问题。若是这里感觉有点迷糊的话,可以看看上一篇博文

一、什么是AIDL?

AIDL,全称为Android Interface Definition Language,即android接口定义语言。顾名思义,AIDL只是一种定义语言,用它的规范定义的接口,可以被aidl工具识别并生成对应的类。(aidl工具在sdk中,可以自行查看)。相信这样比较抽象,接下来我们通过一个小小的例子来看看aidl如何工作。

二、一个简单的AIDL通信示例

服务端代码,注意与上一篇中的服务端代码进行比较,区别已经在注释中体现出来了。

public class ServerService extends Service {	/**	 * 创建服务端binder对象(不用aidl时需要自定义Binder子类,并在Service中返回)	 * 并且我们不用在服务端自行书写onTransact()方法	 */	private Binder serverBinder = new IMyServerService.Stub() {		@Override		public void service2(String args) throws RemoteException {			System.out.println("---service2---:" + args);		}		@Override		public void service1() throws RemoteException {			System.out.println("---service1---");		}	};	@Override	public void onCreate() {		Log.i("DEBUG", "---onCreate()---");		super.onCreate();	}	@Override	public IBinder onBind(Intent intent) {		Log.i("DEBUG", "---onBind()---");		return serverBinder;	}	@Override	public boolean onUnbind(Intent intent) {		Log.i("DEBUG", "---onUnbind()---");		return super.onUnbind(intent);	}}
aidl接口定义,新建com.example.aidl包,新建IMyServerService接口(自行命名),点击保存,可以在gen目录下找到eclipse帮我们自动生成的类IMyServerService.java。之所以新建包,是因为这样等会可以直接将整个包拷贝到客户端中。

package com.example.aidl;interface IMyServerService{	void service1();	void service2(String args);}

客户端代码,同样注意与上一篇的比较,同样体现在注释中。

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 IMyServerService mService;	@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。在没有aidl的时候,客户端需要编写transact()方法,		// 反之则不用。aidl生成的类自动为我们屏蔽了这一细节,即为我们实现了transact()函数。		btn_getService1 = (Button) findViewById(R.id.btn_getService1);		btn_getService1.setOnClickListener(new OnClickListener() {			@Override			public void onClick(View v) {				try {					mService.service1();				} catch (RemoteException e) {					e.printStackTrace();				}			}		});		// 获取服务端服务2		btn_getService2 = (Button) findViewById(R.id.btn_getService2);		btn_getService2.setOnClickListener(new OnClickListener() {			@Override			public void onClick(View v) {				try {					mService.service2("Hello");				} catch (RemoteException e) {					e.printStackTrace();				}			}		});	}	/**	 * 通过回调获取Binder对象(可能是服务端的,也可能是Binder驱动中的Binder对象, 取决于本地或是远程调用。)	 */	private ServiceConnection connection = new ServiceConnection() {		@Override		public void onServiceDisconnected(ComponentName name) {		}		@Override		public void onServiceConnected(ComponentName name, IBinder service) {			// IMyServerService的内部类Stub的asInterface()方法会根据客户端获取到的			// Binder对象的来源来决定调用客户端服务的方式,即是否采用代理。			mService =IMyServerService.Stub.asInterface(service);		}	};}

服务端不需要界面直接运行,客户端界面比较简单就不写出来了。

运行结果如下:

工作是正常的。

三、AIDL如何解决前言中提到的Binder通信的第二个问题

打开gen目录,找到aidl工具为我们生成的类,我们发现这个类名字和我们定义的接口名称一致,并且继承了IInterface接口。接下来我们只看关键的代码。

public static abstract class Stub extends android.os.Binder implements			com.example.aidl.IMyServerService {		private static final java.lang.String DESCRIPTOR = "com.example.aidl.IMyServerService";		/** Construct the stub at attach it to the interface. */		public Stub() {			this.attachInterface(this, DESCRIPTOR);		}		/**		 * Cast an IBinder object into an com.example.aidl.IMyServerService		 * interface, generating a proxy if needed.		 */		public static com.example.aidl.IMyServerService asInterface(				android.os.IBinder obj) {			if ((obj == null)) {				return null;			}			android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);			if (((iin != null) && (iin instanceof com.example.aidl.IMyServerService))) {				return ((com.example.aidl.IMyServerService) iin);			}			return new com.example.aidl.IMyServerService.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_service1: {				data.enforceInterface(DESCRIPTOR);				this.service1();				reply.writeNoException();				return true;			}			case TRANSACTION_service2: {				data.enforceInterface(DESCRIPTOR);				java.lang.String _arg0;				_arg0 = data.readString();				this.service2(_arg0);				reply.writeNoException();				return true;			}			}			return super.onTransact(code, data, reply, flags);		}		private static class Proxy implements com.example.aidl.IMyServerService {			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 service1() throws android.os.RemoteException {				android.os.Parcel _data = android.os.Parcel.obtain();				android.os.Parcel _reply = android.os.Parcel.obtain();				try {					_data.writeInterfaceToken(DESCRIPTOR);					mRemote.transact(Stub.TRANSACTION_service1, _data, _reply,							0);					_reply.readException();				} finally {					_reply.recycle();					_data.recycle();				}			}			@Override			public void service2(java.lang.String args)					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(args);					mRemote.transact(Stub.TRANSACTION_service2, _data, _reply,							0);					_reply.readException();				} finally {					_reply.recycle();					_data.recycle();				}			}		}		static final int TRANSACTION_service1 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);		static final int TRANSACTION_service2 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);	}
需要注意的有以下这些点:

  • 我们看到Stub类继承自Binder类,因此其本质也是一个Binder类,这也是我们能将其当成服务端Binder返回的原因。其次Stub类实现了我们在aidl文件中定义的接口,但是它本身是一个抽象类,并未为我们提供接口方法的实现,因此我们需要在服务端新建的Binder(Stub)对象的时候提供接口方法的具体实现,这便是该服务端提供的服务。
  • 接着我们看到Stub类中“最重要”的一个方法asInterface()。不难发现,这个方法在客户端被调用,客户端同时会将获取到的Binder对象传入这个方法中。因此在asInterface()中会进行一次判断,若是发现从客户端传过来的Binder对象是服务端的(queryLocalInterface()方法),则直接向上转型为IMyServerService类型并返回。若是发现从客户端传过来的Binder对象是Binder驱动中的,那么就需要用到Stub的Proxy代理子类了。
  • 仔细看Proxy代理类,它在asInterface()方法判断为远程调用时被实例化并返回给客户端。那么为什么需要代理类呢?为什么不能像本地Binder那样直接向上转型为IMyServerService呢?其实这样是行不通的,因为就像上篇说的,远程调用客户端获得的Binder对象是Binder驱动中的,需要注意的是,虽然服务端的Binder(Stub)对象实现了IMyServerService接口,可以正确转型,但是Binder驱动中的Binder对象并不能实现这种转型!因此我们需要代理,让它为我们操纵这个Binder对象,调用其transact()方法。
  • 到这里相信很多人已经发现了,aidl工具为我们生成的Stub类里面重载了本该在服务端实现的onTransact()方法和本该在客户端实现的transact()方法,这两个方法就是决定方法调用的。因此我们说aidl为我们统一了放入参数的顺序和函数标识的问题!

可能大家对于上述的第三点胡有点疑虑,即代理类真的有必要存在吗的问题,下面进行简单的验证。将CilentActivity作一点小小的修改,改动如下,直接将客户端获取到的Binder对象进行转型。

mService =IMyServerService.Stub.asInterface(service);
改为:

mService =(IMyServerService) service
运行结果如下:(类型转换错误,得证)

四、总结

看到这里,相信大家对于Binder有了比较深的理解了,对AIDL也有了比较浅的认识。利用AIDL可以实现的功能远不及此,有机会下次在讨论。同时对于Binder通信机制来说,AIDL也不是必须的,我们不仅可以像上一篇说的那样,自己重写onTransact()和transact()方法来实现,甚至你可以完全不需要aidl文件,直接写出一个类似于IMyServerService的java类(aidl工具只是为我们生成这个类而已)。
最后在附上aidl中支持的类型:
  • 基本数据类型
  • String和CharSequence
  • List(元素需要时aidl支持的)
  • Map(元素需要是aidl支持的)
  • AIDL
  • Parcelable
你可能感兴趣的文章
WPF中PATH使用AI导出SVG的方法
查看>>
QT打开项目提示no valid settings file could be found
查看>>
java LinkedList与ArrayList迭代器遍历和for遍历对比
查看>>
如何用好碎片化时间,让思维更有效率?
查看>>
带WiringPi库的交叉笔译如何处理二之软链接概念
查看>>
Java8 HashMap集合解析
查看>>
自定义 select 下拉框 多选插件
查看>>
fastcgi_param 详解
查看>>
poj 1976 A Mini Locomotive (dp 二维01背包)
查看>>
《计算机网络》第五章 运输层 ——TCP和UDP 可靠传输原理 TCP流量控制 拥塞控制 连接管理
查看>>
《PostgreSQL技术内幕:查询优化深度探索》养成记
查看>>
剑指_复杂链表的复制
查看>>
FTP 常见问题
查看>>
shell 快捷键
查看>>
MODULE_DEVICE_TABLE的理解
查看>>
No devices detected. Fatal server error: no screens found
查看>>
db db2_monitorTool IBM Rational Performace Tester
查看>>
postgresql监控工具pgstatspack的安装及使用
查看>>
【JAVA数据结构】双向链表
查看>>
【JAVA数据结构】先进先出队列
查看>>