天天看点

Android中的Binder(二)一、什么是AIDL?二、一个简单的AIDL通信示例三、AIDL如何解决前言中提到的Binder通信的第二个问题 四、总结

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

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

运行结果如下:

Android中的Binder(二)一、什么是AIDL?二、一个简单的AIDL通信示例三、AIDL如何解决前言中提到的Binder通信的第二个问题 四、总结

工作是正常的。

三、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对象进行转型。

<span style="font-size:14px;">mService =IMyServerService.Stub.asInterface(service);</span>
           

改为:

mService =(IMyServerService) service
           

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

Android中的Binder(二)一、什么是AIDL?二、一个简单的AIDL通信示例三、AIDL如何解决前言中提到的Binder通信的第二个问题 四、总结

四、总结

看到这里,相信大家对于Binder有了比较深的理解了,对AIDL也有了比较浅的认识。利用AIDL可以实现的功能远不及此,有机会下次在讨论。同时对于Binder通信机制来说,AIDL也不是必须的,我们不仅可以像上一篇说的那样,自己重写onTransact()和transact()方法来实现,甚至你可以完全不需要aidl文件,直接写出一个类似于IMyServerService的java类(aidl工具只是为我们生成这个类而已)。 最后在附上aidl中支持的类型:

  • 基本数据类型
  • String和CharSequence
  • List(元素需要时aidl支持的)
  • Map(元素需要是aidl支持的)
  • AIDL
  • Parcelable