Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Binder詳解

Binder詳解

編輯:關於Android編程

本篇開始去了解Binder的調用過程和運行機制,本文不會深入探討Binder驅動以及底層的實現,而是以AIDL的使用理清Java層面的Binder的實現和通信過程。

本文依然按照慣例,先總結下AIDL的使用步驟,然後根據日志、調試等信息整理其調用邏輯。

AIDL的使用步驟

AIDL通常用於Service和其它組件(如Activity)的通信。為什麼要用AIDL呢,因為AIDL可以幫我們快速實現跨進程通信,如果項目中的Service和Activity分別位於不同的兩個進程,而他們又需要互相通信,這時就涉及到跨進程通信了,此時就是AIDL的使用場景了。

(1)編寫AIDL文件

這一步請注意,AIDL文件只支持基本類型數據的遠程傳輸,而對於我們自定義的實體對象,需要實現Parcelable接口才行,具體不再贅述,看之前的文章。

(2)在Service中繼承Stub類,並重寫aidl文件中接口定義的方法實現自己的業務。編譯器會自動提示你去繼實現哪些方法的,因為Stub是一個抽象類,並且在aidl中的方法在Stub中仍然是待實現的空方法。以下是根據AIDL生成的類:

 

package com.example.appa;

import android.os.IInterface;
import android.util.Log;

public interface CustomBinder extends android.os.IInterface {
	final String TAG = "Binder";

	public static abstract class Stub extends android.os.Binder implements
			com.example.appa.CustomBinder {
		private static final java.lang.String DESCRIPTOR = "com.example.appa.Test";

		public Stub() {
			this.attachInterface((IInterface) this, DESCRIPTOR);
			Log.e(TAG, "CustomBinder.Stub-->" + "Stub()構造器執行");
		}

		/**
		 * 將IBinder對象轉換成Test接口實例
		 * 
		 * @param obj
		 *            這是一個Binder實例,如果Service另外開啟了一個進程那麼此obj就是一個Binder.
		 *            BinderProxy實例,
		 *            如果Service沒有另外開啟一個進程,那麼此obj就是一個Binder實例。總之,obj的類型取決於Binder是本地的還是服務端的
		 *            通常我們把Service另外開啟一個進程當做服務端
		 * @return
		 */
		public static com.example.appa.CustomBinder asInterface(
				android.os.IBinder obj) {
			if ((obj == null)) {
				return null;
			}
			// 這裡就會查找本地Binder對象,如果找到即iin不為null就說明Server端和Client端在同一個進程,那麼此時obj就是一個Binder實例
			// 如果沒有找到即iin為null就說明Server和Client不在同一個進程,此時iin就是null了。然後就要去創建Test.Stub.Proxy實例
			android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
			if (((iin != null) && (iin instanceof com.example.appa.Test))) {
				return ((com.example.appa.CustomBinder) iin);
			}
			Log.e(TAG, "CustomBinder.Stub-->" + "asInterface()執行:" + iin);
			return new com.example.appa.CustomBinder.Stub.Proxy(obj);
		}

		@Override
		public android.os.IBinder asBinder() {
			Log.e(TAG, "CustomBinder.Stub-->" + "asBinder()");
			return this;
		}

		/*
		 * Client調用hello方法的線程掛起等待返回;驅動完成一系列的操作之後喚醒Server進程,
		 * 調用了Server進程本地對象的onTransact函數(實際上由Server端線程池完成)。 (non-Javadoc)
		 * 
		 * @see android.os.Binder#onTransact(int, android.os.Parcel,
		 * android.os.Parcel, int)
		 */
		@Override
		public boolean onTransact(int code, android.os.Parcel data,
				android.os.Parcel reply, int flags)
				throws android.os.RemoteException {
			Log.e(TAG, "CustomBinder.Stub-->" + "onTransact()執行");
			switch (code) {
			case INTERFACE_TRANSACTION: {
				reply.writeString(DESCRIPTOR);
				return true;
			}
			case TRANSACTION_hello: {
				Log.e(TAG, "CustomBinder.Stub-->"
						+ "TRANSACTION_hello執行:服務端執行本地hello方法");
				data.enforceInterface(DESCRIPTOR);
				java.lang.String _arg0;
				_arg0 = data.readString();
				// 這裡調用了服務端本地的hello()方法,並且獲得結果,將結果寫回驅動,
				// 然後驅動喚醒掛起的Client進程裡面的線程並將結果返回。於是一次跨進程調用就完成了。
				java.lang.String _result = this.hello(_arg0);
				reply.writeNoException();
				reply.writeString(_result);
				return true;
			}
			}
			return super.onTransact(code, data, reply, flags);
		}

		/**
		 * Stub的代理類,也就是服務端的代理對象
		 * 
		 * @author Administrator
		 * 
		 */
		private static class Proxy implements com.example.appa.CustomBinder {
			private android.os.IBinder mRemote;

			Proxy(android.os.IBinder remote) {
				mRemote = remote;
			}

			@Override
			public android.os.IBinder asBinder() {
				Log.e(TAG, "CustomBinder.Stub.Proxy-->" + "asBinder()執行");
				return mRemote;
			}

			public java.lang.String getInterfaceDescriptor() {
				Log.e(TAG, "CustomBinder.Stub.Proxy-->"
						+ "getInterfaceDescriptor()執行");
				return DESCRIPTOR;
			}

			@Override
			public java.lang.String hello(java.lang.String content)
					throws android.os.RemoteException {
				Log.e(TAG, "CustomBinder.Stub.Proxy-->" + "hello()執行");
				android.os.Parcel _data = android.os.Parcel.obtain();
				android.os.Parcel _reply = android.os.Parcel.obtain();
				java.lang.String _result;
				try {
					_data.writeInterfaceToken(DESCRIPTOR);
					_data.writeString(content);
					// 前面說過,只有當Server和Client不在同一個進程才會執行此代理的方法,而如果Server和Client不在同一個進程
					// 那mRemote就是一個Binder.BinderProxy實例,那麼此transact()方法就是Binder.BinderProxy的方法,他是一個本地方法
					//
					mRemote.transact(Stub.TRANSACTION_hello, _data, _reply, 0);
					_reply.readException();
					_result = _reply.readString();
				} finally {
					_reply.recycle();
					_data.recycle();
				}
				return _result;
			}
		}

		static final int TRANSACTION_hello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
	}
    /**
     * 這個方法只是Stub的一個抽象方法,是在TestService中繼承的時候重寫了
     * @param content
     * @return
     * @throws android.os.RemoteException
     */
	public java.lang.String hello(java.lang.String content)
			throws android.os.RemoteException;
}

 

在以上兩不完成以後實際上就是創建了一個Server端,接下來就是Client的使用了。

(3)在Service(此處demo中是TestService)中繼承Stub定義自己的Binder。

	private class MyBinder extends CustomBinder.Stub {
		@Override
		public String hello(String content) throws RemoteException {
			String con = "這是從Service發出來的內容..." + content;
			Log.e(TAG, "TestService.MyBinder-->"+"hello()執行");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return con;
		}

	}

 

(4)在Activity中實現ServiceConnection接口,創建其實例。

	private class MyServiceConnection implements ServiceConnection {
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			showToast("服務已連接");
			//mBinderConnection是CustomBinder類型的,這裡就是將Binder驅動返回的IBinder對象轉換成具體的我們定義的CustomBinder
			mBinderConnection = CustomBinder.Stub.asInterface(service);
			try {
				//這裡就是客戶端調用遠程的hello()方法了。
				String str = mBinderConnection.hello("來自Activity的問候");
				Log.e(TAG,
						"MainActivity.MyServiceConnection-->"
								+ "onServiceConnected()執行,此時Service已經連接上,Server和Client可以通信了");
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
		@Override
		public void onServiceDisconnected(ComponentName name) {
			mBinderConnection = null;
		}
	}

 

(5)調用bindService()即可。

 

AIDL跨進程的調用過程

 

當我們Server端(TestService)和Client端(MainActivity)都准備好了之後,客戶端發起調用時,比如MainActivity中需要調用hello()方法,我們調用hello()之後這中間發生了什麼呢?他的調用順序是按照如下過程來的:

第一步:執行Stub的構造器,因為當我們調用bindService()的時候在Service中的onCreate中new了CustomBinder.Stub對象,從而調用了Stub的構造器。

第二步:執行了Stub的asInterface():這個是onServiceConnected()中獲取Binder驅動發送過來的Stub實例。

第三步:就調用了Stub.Proxy的hello()方法了,因為TestService和當前Activity不是一個進程的,所以會使用Stub.Proxy實例的hello(),這個hello()會通過BinderProxy去調用Stub的onTransact()方法,然後就進入第四步。

第四步:調用了Stub的onTransact()方法,此方法就是用於調用服務端本地的hello()方法,在這裡,這個“服務端本地”就是指Stub.hello()具體來說就是我們在TestService中繼承Stub重寫的那個hello()方法,也就是TestService.MyBinder.hello()。

 

AIDL源碼分析

 

1、Client端獲取遠程Binder對象

 

就像上面我們的ServiceConnection中的onServiceConnected()方法中的那樣,是客戶端是通過Stub的asInterface()方法來獲取遠程具體對象的:

 

public void onServiceConnected(ComponentName name, IBinder service) {
			showToast("服務已連接");
			//mBinderConnection是CustomBinder類型的,這裡就是將Binder驅動返回的IBinder對象轉換成具體的我們定義的CustomBinder
			mBinderConnection = CustomBinder.Stub.asInterface(service);
		
這一步要注意,因為,如果服務端(TestService)和客戶端(mainActivity)不在同一個進程,那麼此處的onServiceConnectd()中的service參數就是一個TestService中我們定義的MyBinder實例,它對於程序員來說是一個本地的Binder;如果服務端和客戶端不在一個進程中,此處的service參數就是一個android.os.Binder.BinderProxy實例,一定要記住這一點。好,接下來我們去看看CustomBinder.Stub的asInterface()方法如下:

 

 

	public static com.example.appa.CustomBinder asInterface(
				android.os.IBinder obj) {
			// 如果客戶端和服務端還沒建立連接就直接return null
			if ((obj == null)) {
				return null;
			}
			// 這裡就會查找本地Binder對象,如果找到即iin不為null就說明Server端和Client端在同一個進程,
		    //那麼此時obj就是一個Binder實例
			android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
			if (((iin != null) && (iin instanceof com.example.appa.Test))) {
				return ((com.example.appa.CustomBinder) iin);
			}
			Log.e(TAG, "CustomBinder.Stub-->" + "asInterface()執行:" + iin);
			// 如果沒有找到即iin為null就說明Server和Client不在同一個進程,此時iin就是null了。
		    //然後就要去創建Test.Stub.Proxy實例,注意,這裡創建Proxy的時候傳遞了一個obj,這個obj一定是Binder.BinderProxy的實例
			
			return new com.example.appa.CustomBinder.Stub.Proxy(obj);
		}

正如上面的注釋那樣,如果Server(此處的Server就是TestService)和Client(就是MainActivity)是同一個進程那麼就會直接返回Binder實例,如果不在同一個進程就會返回一個Stub.Proxy實例。這是有很大區別的,如果在同一個進程那麼方法調用就是直接本地調用了,如果不在同一個進程就需要代理Proxy去利用BinderProxy進行遠程調用了。

 

 

2、客戶端調用服務端方法

 

(1)服務端和客戶端在同一個進

如果說服務端和客戶端在同一個進程,即不指定TestService和Activity的process屬性,那麼當我們在客戶端調用CustomBinder的hello()方法時就是直接調用了Stub的hello(實際上是TestService中的MyBinder重寫的hello方法)方法,不需要走Stub.Proxy的流程。

(2)服務端和客戶端不在同一個進程

這裡我們將TestService的process屬性取一個別名,就是讓TestService運行在另一個進程,此時服務端和客戶端就不在一個進程了,這時候我們在客戶端調用服務端的hello()方法時,情況就不一樣了,因為上面說了,當服務端和客戶端不在一個進程時,實際上調用的就是Stub.Proxy的hello()方法,下面我們去看下Stub.Proxy的hello()方法:

 

			@Override
			public java.lang.String hello(java.lang.String content)
					throws android.os.RemoteException {
				Log.e(TAG, "CustomBinder.Stub.Proxy-->" + "hello()執行");
				android.os.Parcel _data = android.os.Parcel.obtain();
				android.os.Parcel _reply = android.os.Parcel.obtain();
				java.lang.String _result;
				try {
					_data.writeInterfaceToken(DESCRIPTOR);
					_data.writeString(content);
					// 前面說過,只有當Server和Client不在同一個進程才會執行此代理的方法,而如果Server和Client不在同一個進程
					// 那mRemote就是一個Binder.BinderProxy實例,那麼此transact()方法就是Binder.BinderProxy的方法,
					//他是一個本地方法,是交由Binder驅動去告訴服務端去執行onTransact(0方法。
					mRemote.transact(Stub.TRANSACTION_hello, _data, _reply, 0);
					_reply.readException();
					_result = _reply.readString();
				} finally {
					_reply.recycle();
					_data.recycle();
				}
				return _result;
			}
		}

		static final int TRANSACTION_hello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
	}
其實Stub.Proxy的hello()方法什麼也沒做,只是將一些必要的數據寫入和讀出Parcel,然後通過mRemote來調用transact()方法去告訴Binder驅動通知服務端調用真實的hello()方法。mRemote之前說過了,當服務端和客戶端不在同一個進程時才會創建Stub.Proxy並且傳遞一個Binder.BinderProxy實例進來,此mRemote就是一個Binder.BinderProxy實例,transact()方法自然就是BinderProxy的方法了,他是一個native方法,用於告訴Binder驅動去通知服務端調用onTransact()方法調用真實的hello()方法!也就是TestService.MyBinder的hello()方法!!!好,這一步我們說了客戶端向服務端請求調用hello()方法,然而代理Stub.Proxy並沒有直接去調用,而是告知Binder驅動。當我們通過mRemote調用transact()時客戶端線程會被掛起,直到服務端有結果返回,確切的說是要等到Stub的onTransact()方法返回。

 

3、服務端響應客戶端請求

注意:如果服務端和客戶端不在一個進程,那麼就不存在這一步了,請忽略。

上一步說了,客戶端已經調用hello()向服務端發起請求,而那時候服務端還沒有收到通知,因為客戶端的請求被服務端的代理Stub.Proxy發送給Binder驅動了(通過BinderProxy的transac方法實現的),Binder驅動做一些必要的操作後再通知服務端有客戶端請求了,你快去執行onTransact()方法來響應請求吧!接下來我們看下Stub的onTransact()方法:

 

		@Override
		public boolean onTransact(int code, android.os.Parcel data,
				android.os.Parcel reply, int flags)
				throws android.os.RemoteException {
			Log.e(TAG, "CustomBinder.Stub-->" + "onTransact()執行");
			switch (code) {
			case INTERFACE_TRANSACTION: {
				reply.writeString(DESCRIPTOR);
				return true;
			}
			case TRANSACTION_hello: {
				Log.e(TAG, "CustomBinder.Stub-->"
						+ "TRANSACTION_hello執行:服務端執行本地hello方法");
				data.enforceInterface(DESCRIPTOR);
				java.lang.String _arg0;
				_arg0 = data.readString();
				// 這裡調用了服務端本地的hello()方法,並且獲得結果,將結果寫回驅動,
				// 然後驅動喚醒掛起的Client進程裡面的線程並將結果返回。於是一次跨進程調用就完成了。
				java.lang.String _result = this.hello(_arg0);
				reply.writeNoException();
				reply.writeString(_result);
				return true;
			}
			}
			return super.onTransact(code, data, reply, flags);
		}

其它不用管,我們就看上面注釋的地方,這一行代碼:java.lang.String _result = this.hello(),它就是去調用了服務端(我們此時是處於服務端的情景之中)自己的hello()方法,也就是TestService.MyBinder的hello()方法,並返回結果,將結果寫到reply中去,即寫回Binder驅動,之後Binder驅動就喚醒還掛著的客戶端線程,並將結果返回,這樣,一個跨進程通信的過程就結束了。

 

小結

 

如果Server和Clienr在同一個進程,那麼Server會直接調用Service中的相關方法,如果Server和Clienr不在同一個進程,那麼就會通過跨進程通信遠程調用,首先是Stub的代理類使用BinderProxy執行其transact(),這個方法是一個native的,裡面進行了一系列的函數調用,最後就交給Binder驅動完成了;在通信過程中,Client進程陷入內核態,Client調用hello方法的線程掛起等待返回;驅動完成一系列的操作之後喚醒Server進程,調用了Server進程本地對象(Stub)的onTransact函數(實際上由Server端線程池完成)。我們再看Binder本地對象的onTransact方法(這裡就是Stub類裡面的此方法),然後onTransact()就調用了服務端的hello()方法並將結果寫回Binder驅動,然後驅動喚醒掛起的Client進程裡面的線程並將結果返回,一次跨進程遠程調用就結束了。

Android中Binder相關接口

(1)IBinder

IBinder是一個接口,它代表了一種跨進程傳輸的能力;只要實現了這個接口,就能將這個對象進行跨進程傳遞;這是驅動底層支持的;在跨進程數據流經驅動的時候,驅動會識別IBinder類型的數據,從而自動完成不同進程Binder本地對象以及Binder代理對象的轉換。

(2)Binder

代表的其實就是Binder本地對象。BinderProxy類是Binder類的一個內部類,它代表遠程進程的Binder對象的本地代理;這兩個類都繼承自IBinder, 因而都具有跨進程傳輸的能力;實際上,在跨越進程的時候,Binder驅動會自動完成這兩個對象的轉換。

到此,我們基本理清了Server端和Client端的跨進程通信過程,那麼我們是否想過,這一切都是建立在客戶端獲取到服務端對象的基礎上的,也就是我們在Activity中實現ServiceConnection接口,他的onServiceConnected()方法返回給我們一個遠程的服務端對象。那麼我們是否考慮過,這個onServiceConnected()方法何時調用,即何時會返回給我們一個遠程的對象?這就是bindService()所處理的了,下一篇我們就解開bindService()之謎。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved