1. 程式人生 > >Android開發藝術探索學習-IPC之Binder(一)

Android開發藝術探索學習-IPC之Binder(一)

1. Binder簡介 1.1 What is Binder?     Android Dev Doc:Base class for a remotable object, the core part of a lightweight remote procedure call mechanism defined by . This class is an implementation of IBinder that provides standard local implementation of such an object.     Binder是一個遠端物件的基類,是一個輕量級遠端通訊機制的核心部分,通過IBinder定義。Binder類實現IBinder介面,並通過提供標準的本地介面物件實現IBinder。     我們以前也接觸過一些IPC方式,比如:管道(pipe)、有名管道(FIFO)、訊號(signal)、訊息佇列、共享記憶體、套接字(socket)等,但是Android系統並沒有採取這些方式,而是使用Binder完成IPC。 1.2 why is Binder?     由於移動裝置的特殊性,在IPC要儘量滿足兩個方面的要求:安全性高、傳輸效能高。 首先從傳輸效能上看:     socket傳輸效率低,開銷大,因此主要還是用於跨網路的程序間通訊。訊息佇列和管道採用的是儲存轉發方式,即資料先從傳送方快取區拷貝到核心開闢的快取區中,然後再從核心快取區拷貝到接收方快取區,
至少有兩次拷貝過程。
表1 各種IPC方式資料拷貝次數

IPC

資料拷貝次數

共享記憶體

0

Binder

1

Socket/管道/訊息佇列

2
其次從安全性上看:     傳統的IPC其實是沒有任何安全措施,完全依賴上層協議來確保,其接收方無法獲得對方程序可靠的UID/PID,從而無法鑑別對方身份。但是Binder會為傳送方新增UID/PID身份(由系統新增),安全性高。 1.3 Binder的注意事項    有一點我們需要牢記在心:當我們的程序被“Kill”,那麼就需要在該程序重啟時重新建立一個Binder物件並re-attach到該程序。例如,當你如果在Activity裡面使用Binder,如果你的Activity未啟動,那麼它就隨意可能會系統回收。當Activity重建時,你需要重建一個新的Binder物件並放在正確的地方。當然你也需要注意你的程序因其他原因而啟動(比如收到廣播),那麼這種情況activity不會重建也就不會跑到建立Binder物件的程式碼了。
1.4 AIDL   如果大家有興趣閱讀Android原始碼的話,就會發現Android FrameWork層存在著很多Binder機制。因此理解Binder的工作原理是很重要的。為了能讓咱們程式設計師能很簡單地使用Binder,Android為我們提供了一個非常簡單地使用Binder的工具,AIDL。那麼如何使用AIDL呢?請參考Android官方文件:http://developer.android.com/guide/components/aidl.html     這裡主要分析下,AIDL檔案自動生成的JAVA檔案。aidl檔名為:IAidlCall.aidl,程式碼如下。
package com.example.aidldemo;
 interface IAidlCall{
 String getName();
}
    自動生成的IAidlCall.java,程式碼如下。
/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/chenjiawei/Documents/AndroidSpace/ClientDemo/src/com/example/aidldemo/IAidlCall.aidl
 */
package com.example.aidldemo;

public interface IAidlCall extends android.os.IInterface {
	/** Local-side IPC implementation stub class. */
	public static abstract class Stub extends android.os.Binder implements
			com.example.aidldemo.IAidlCall {
		// Binder的唯一標示,一般用當前Binder的類名來標示
		private static final java.lang.String DESCRIPTOR = "com.example.aidldemo.IAidlCall";

		/** Construct the stub at attach it to the interface. */
		public Stub() {
			this.attachInterface(this, DESCRIPTOR);
		}

		/**
		 * Cast an IBinder object into an com.example.aidldemo.IAidlCall
		 * interface, generating a proxy if needed.
		 */
		public static com.example.aidldemo.IAidlCall asInterface(
				android.os.IBinder obj) {
			if ((obj == null)) {
				return null;
			}
			android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
			if (((iin != null) && (iin instanceof com.example.aidldemo.IAidlCall))) {
				return ((com.example.aidldemo.IAidlCall) iin);
			}
			return new com.example.aidldemo.IAidlCall.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_getName: {
				data.enforceInterface(DESCRIPTOR);
				java.lang.String _result = this.getName();
				reply.writeNoException();
				reply.writeString(_result);
				return true;
			}
			}
			return super.onTransact(code, data, reply, flags);
		}

		private static class Proxy implements com.example.aidldemo.IAidlCall {
			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 java.lang.String getName() throws android.os.RemoteException {
				android.os.Parcel _data = android.os.Parcel.obtain();
				android.os.Parcel _reply = android.os.Parcel.obtain();
				java.lang.String _result;
				try {
					_data.writeInterfaceToken(DESCRIPTOR);
					mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
					_reply.readException();
					_result = _reply.readString();
				} finally {
					_reply.recycle();
					_data.recycle();
				}
				return _result;
			}
		}

		static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
	}

	public java.lang.String getName() throws android.os.RemoteException;
}
    IAidlCall是一個介面,其繼承於android.os.IInterface。所以AIDL自動生成的java檔案都要繼承該介面,該介面程式碼如下。
/**
 * Base class for Binder interfaces.  When defining a new interface,
 * you must derive it from IInterface.
 */
public interface IInterface
{
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    public IBinder asBinder();
}
    IAidlCall介面中聲明瞭一個靜態內部抽象類Stub,該類繼承Binder類並實現IAidlCall介面。因此Stub其實就是一個Binder類。 在我們實際的使用AIDL過程中,客戶端和服務端都要與Stub打交道。在服務端中,我們需要實現Stub類,程式碼如下。
public class MyServiceImpl extends IAidlCall.Stub {

   @Override
   public String getName() throws RemoteException {

     return "Jack";
   }

}
在客戶端中,我們需要呼叫Stub類中的asInterface方法來得到IAidlCall物件,程式碼如下。
private ServiceConnection conn = new ServiceConnection() {

		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub
		}

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// TODO Auto-generated method stub
			call = IAidlCall.Stub.asInterface(service);
			try {
				Toast.makeText(getApplicationContext(), call.getName(),
						Toast.LENGTH_LONG).show();
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	};
接著我們看下asInterface方法中的程式碼,看完該方法我們就可以瞭解IAidlCall.java工作流程。
/**
* Cast an IBinder object into an com.example.aidldemo.IAidlCall

* interface, generating a proxy if needed.

*/

public static com.example.aidldemo.IAidlCall asInterface(android.os.IBinder obj) {

     //首先判斷IBinder是否為空,因為Binder可以“死亡”,前面的有提過
     if ((obj == null)) {
         return null;
     }
     //這裡queryLocalInterface方法得到IInterface物件,這是通過attachInterface賦值過去的
     android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    //如果iin不為空並且是IAidlCall例項則將其強轉成IAidlCall物件並返回
    if (((iin != null) && (iin instanceof com.example.aidldemo.IAidlCall))){ 
         return ((com.example.aidldemo.IAidlCall) iin);
     }
   //如果lin為空或者不是IAidlCall例項,則返回Proxy物件
   return new com.example.aidldemo.IAidlCall.Stub.Proxy(obj);
}
這裡我們可以看到一個Proxy類,該類是Stub裡的靜態類且繼承於IAidlCall,在Proxy類中有真正完成getName()方法實現的過程。實現getName方法的程式碼如下。
	@Override
	public java.lang.String getName() throws android.os.RemoteException {
		android.os.Parcel _data = android.os.Parcel.obtain();
		android.os.Parcel _reply = android.os.Parcel.obtain();
		java.lang.String _result;
		try {
			_data.writeInterfaceToken(DESCRIPTOR);
			mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
			_reply.readException();
			_result = _reply.readString();
		} finally {
			_reply.recycle();
			_data.recycle();
		}
		return _result;
	}
這裡需要注意的是_data、_reply兩個Parcel物件和transact方法。 _data是入參序列,如果呼叫的遠端方法需要入參就通過_datawriteInt、writeLong等方法將引數寫入_data中。 _reply是方法的返回值序列,如果呼叫的遠端方法有返回值,就通過_replyreadInt、readLong等方法將引數讀到_reply中。 這裡最主要的方法是transact(intcode, Parceldata, Parcelreplyintflags)方法。 1)第一個引數code代表的是方法的程式碼,例如getName()的方法code為, static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 知道code就能知道呼叫的是哪個方法。 2)第二個引數是指遠端方法的入參。 3)第三個引數是指遠端方法的返回值。 4)第四個引數是指額外操作標誌,0代表的是普通的RPC,其他值代表的是one-way RPC。 因此我們,可以通過模仿寫出transact方法所需要的引數,通過RemoteObject(Binder)呼叫其transact方法就可以完成遠端方法的呼叫,最後會給出相應地例子。 Binder中transact方法實現程式碼如下。
    /**
     * Default implementation rewinds the parcels and calls onTransact.  On
     * the remote side, transact calls into the binder to do the IPC.
     */
    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }
從該程式碼中我們可以看到呼叫了onTransact方法,在該方法中會把遠端方法返回值寫入到_reply中。在IAidlCall介面中的Stub類中,我們可以看到onTransact方法的實現,程式碼如下。
	@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_getName: {
			data.enforceInterface(DESCRIPTOR);
			java.lang.String _result = this.getName();
			reply.writeNoException();
			// 這裡將getName的返回值寫入到reply中
			reply.writeString(_result);
			return true;
		}
	}
		return super.onTransact(code, data, reply, flags);
	}
至此,我們分析完了IAidlCall介面類及其工作過程。其實我們也可以完全自己動手寫出IAidlCall.java,在《Android開發藝術探索》書中就有給出相應地例子。下面可以看看AIDL的工作圖。
這裡有兩點需要注意: 首先,當客戶端發起遠端請求的時候,由於當前程序會被掛起直到服務端程序返回資料,所以如果一個遠端方法是很耗時的,那麼不能在UI執行緒中發起此遠端請求;其次,由於服務端的Binder方法執行在Binder的執行緒池中,所以Binder方法不管是否耗時都應該採用同步的方式去實現,因為它已經執行在一個執行緒中了。 通過以上介紹,可以大致瞭解了Binder的工作方式,後續還將繼續深入瞭解,當然也不會太深,都是結合《Android開發藝術探索》和工作上所碰到的問題進行概述。





相關推薦

Android開發藝術探索學習-IPCBinder()

1. Binder簡介 1.1 What is Binder?     Android Dev Doc:Base class for a remotable object, the core part of a lightweight remote procedure

android開發藝術探索學習 結合Activity的生命周期了解Activity的LaunchMode

友情 dsm ask resume () new onstop androi sum 轉載請標明出處: http://blog.csdn.net/lxk_1993/article/details/50749728 本文出自:【lxk_1993的博客】;

Android開發藝術探索 學習筆記

1、Android多程序 ~1 Android多程序模式的開啟 ~~Android多程序 四個元件指定process屬性 ~~使用多程序只有一種方法,無法給一個執行緒或者一個實體類指定其執行所在的程序。(特殊情況:通過JNI在native層區域fork一

Window和WindowManager(Android開發藝術探索學習筆記)

概述 Window表示一個視窗的概念,它是一個抽象類,它的具體實現類是PhoneWindow。 WindowManager是外界訪問Window的入口,Window的具體實現位於WindowManagerService中。 WindowManager和Wi

Android 開發藝術探索》讀書筆記()——Activity 的生命週期和啟動模式

Activity 作為 Android 四大元件之首,它作為和使用者互動的介面,在開發中使用得可謂極其頻繁,所以弄清楚 Activity 的生命週期和啟動方式是非常重要的,要牢記。 1 Activity 的生命週期全面分析 1.1 典型情況下的生命週期分析 onCrea

Android 開發藝術探索》讀書筆記六 IPC機制Binder

一、Binder簡介 Binder是Android中的一個類,它實現於IBinder介面: (1)從IPC角度來說,Binder是一種跨程序通訊的方式; (2)從Android Framework角度來講,Binder是ServiceManager連線各種

Android開發藝術探索學習總結7 Android中為實現IPCBinder連線池的使用

     學習基礎和材料來源於《Android開發藝術探索》;本篇主要簡單介紹一下在Android中實現IPC是Binder連線池的使用;原理性的東西介紹不多主要介紹一下使用場景和使用步驟;其實看懂下面兩張圖感覺就沒有什麼好說的;除了Binder連線池核心程式碼注意一下,

Android開發藝術探索學習筆記Android的訊息機制.md

《Android開發藝術探索》學習筆記之Android的訊息機制 一、概述 1、Handler的主要作用是將某個任務切換到指定的執行緒中去執行 eg:子執行緒中無法更新UI,需切換到主執行緒 V

Android開發藝術探索學習筆記Android的執行緒和執行緒池

一、概述 1、主執行緒與子執行緒 主執行緒 又叫UI執行緒 主要作用是執行四大元件以及處理它們和使用者的互動,主要用來處理和介面相關的事情 子執行緒 執行耗時任務,比如網路請求、I/O操作等

關於IPC機制 相關學習Android開發藝術探索

主要包含三個內容  ,Serializable 介面,parcelable介面以及Binder,Serializable介面和parcelable介面可以完成物件的序列化過程,當我們需要通過intent和binder 傳輸資料時候就需要用parcelable 或者serializable。 Serial

Android 開發藝術探索筆記五 -- 理解 RemoteViews

學習內容: RemoteViews 在通知欄和桌面小部件上的應用 RemoteViews 的內部機制 RemoteViews 的意義 RemoteView 的應用 實際開發中,RemoteViews 主要用在通知欄和桌面小部件的開發過程中。通知欄主

Android 開發藝術探索筆記六 -- Android 的 Drawable

整理一下,基本只作為 知識清單 使用 學習內容: Drawable 的層次關係 Drawable 分類 自定義 Drawable 的相關知識 Drawable 簡介 Drawable 表示的是一種可以在 canvas 上進行繪製的影象的 抽象

Android 開發藝術探索》讀書筆記(二)——IPC 機制

Android 多程序開發我在平時開發中還沒有遇到,但不代表不重要,仍需要了解一下基本概念,Android 的序列化機制和 Binder 是。 1 Android IPC 簡介 IPC 是 Inter Process Communication 的縮寫,意為程序間通訊或跨程序通訊,是指兩

Android開發藝術探索初探AIDL(

package service; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteCa

android開發藝術探索2:binder淺析

什麼是binder? 直觀來說,binder是android中的一個類,它實現了IBinder介面。 從ipc角度來說,binder是android中的一種跨程序通訊方式,binder還可以勒戒為一種虛擬的物理裝置,該通訊方式在linux中沒有 從android f

Android開發藝術-第二章 IPC 機制

個數 ++ long udp 結構 數據類型 this ces java 2.1 Android IPC 簡單介紹 IPC 意為進程間通信或者跨進程通信,線程是 CPU 調度的最小單元,是一種有限的系統資源。進程一般指一個執行單元。不論什麽操作系統都須

Android開發藝術探索》第11章 Android的線程和線程池

ctas serial 主線程 message minute 方法 同時 pre 控制線 第11章 Android的線程和線程池 11.1 主線程和子線程 (1)在Java中默認情況下一個進程只有一個線程,也就是主線程,其他線程都是子線程,也叫工作線程。Android中的主

Android開發藝術探索 圖書勘誤

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Android開發藝術探索——————要點總結

  Activity異常情況的生命週期: 預設情況下,Activity不做特殊處理,當系統配置發生改變後,Activity會被銷燬並重建 。由於Activity是在異常情況下終止的,系統會呼叫onSaveInstanceState來儲存當前Activity的狀態(在onStop之前)。當Acti

Android 開發藝術探索》 第11章 --- android 執行緒和執行緒池

如果程序中沒有四大元件,其優先順序將會降低,intentservice 是service封裝了handerthread ,這是intentservice的優點 執行緒是作業系統的最小排程單元,是系統的一種受限制的系統資源,建立和銷燬執行緒都將有對應的開銷,所以使用執行緒池來避免這種開銷 Andr