天天看點

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