原文出處點擊[這里](http://blog.csdn.net/zizidemenghanxiao/article/details/50341773)
### Android的IPC機制
#### 一、IPC簡介
(1)IPC是Inter-Process Communication的縮寫,含義為進程間通信或者跨進程通信,是指兩個進程之間進行數據交換的過程。
(2)ANR是Application Not Responding的縮寫,即應用無響應。主線程執行大量的耗時操作容易導致ANR現象發生。
(3)在Android中最有特色的進程間通信方式就是Binder了,通過Binder可以輕松地實現進程間通信。
(4)Android還支持Socket,通過Socket也可以實現任意兩個終端或者兩個進程之間的通信。
### 二、Android中的多進程模式
#### 1、在Android中使用多進程只有一種方法:
就是給四大組件(Activity、Service、Receiver、ContentProvider)在AndroidManifest中指定android:process屬性。
可以在Eclipse的DDMS視圖中查看進程信息,還可以用shell來查看,命令為:adb shell ps 或者 adb shell ps|grep com.ryg.chapter_2。
~~~
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
android:launchMode="standard" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity
android:name=".SecondActivity"
android:configChanges="screenLayout"
android:label="@string/app_name"
android:process=":remote" />
<activity
android:name=".ThirdActivity"
android:configChanges="screenLayout"
android:label="@string/app_name"
android:process="com.ryg.chapter_2.remote" />
~~~
上面的代碼中,
(1)MainActivity沒有指定process屬性,所以它運行在默認的進程中,默認進程的進程名是包名。
(2)SecondActivity會運行在一個單獨的進程中,進程名為“com.ryg.chapter_2:remote”,其中com.ryg.chapter_2是包名。在程序中的冒號“:”的含義是指要在當前的進程名前面附加上當前的包名,是一種簡寫的方法。而且以“:”開頭的進程屬于當前應用的私有進程,其他應用的組件不可以和它跑在同一個進程中。
(3)ThirdActivity會運行在另一個單獨的進程中,進程名為“com.ryg.chapter_2.remote”。這是一種完整的命名方式。屬于全局進程,其他應用通過ShareUID方式可以和它跑在同一個進程中。
> **注意點一**:Android系統會為每一個應用分配一個唯一的UID,具有相同UID的應用才能共享數據。要求兩個應用具有相同的ShareUID并且簽名相同才可以跑在同一個進程中。在這種情況下,它們可以互相訪問對方的私有數據,比如data目錄、組件信息等,不管它們是否跑在同一個進程中。當然如果它們跑在同一個進程中,那么除了能共享data目錄、組件信息,還可以共享內存數據,或者說它們看起來就像是一個應用的兩個部分。
#### 2、多進程模式的運行機制
(1)多進程會帶來很多意想不到的麻煩,因為Android為每一個應用都分配了一個獨立的虛擬機,或者說為每個進程都分配了一個獨立的虛擬機,不同的虛擬機在內存分配上有不同的地址空間,這就導致在不同的虛擬機中訪問同一個類的對象會產生多份副本。這樣很就容易導致數據不同步。
(2)所有運行在不同進程的四大組件,只要它們之間需要通過內存在共享數據,都會共享失敗。
(3)主要有以下四方面的問題:
* 靜態成員和單例模式完全失效。(由獨立虛擬機造成)
* 線程同步機制完全失效。(同上)
* SharedPreferences的可靠性下降。(存在并發讀寫的問題)
* Application會多次創建。(新的進程中又會導致進程所在的Application在新的虛擬機中再次創建)
(4)運行在同一個進程中的組件是屬于同一個虛擬機和同一個Application的,同理運行在不同進程的組件是屬于兩個不同的虛擬機和Application的。
基于上面的這些問題,因為我們需要學習進程間通信機制!!!!!
### 四、Binder
#### 1、Binder簡介
(1)Binder實現了IBinder接口。
(2)從IPC角度來說,Binder是Android中的一種跨進程通信方式。Binder還可以理解為一種虛擬的物理設備,它的設備驅動是/dev/binder,這種通信方式在Linux中沒有。
(3)從Android Framework角度來說,Binder是ServiceManager連接各種Manager(ActivityManager、WindowManager,等等)和相應ManagerService的橋梁。
(4)從Android應用層來說,Binder是客戶端和服務端進行通信的媒介,當bindService的時候,服務端會返回一個包含了服務端業務調用的Binder對象,通過這個對象,客戶端就可以獲取服務端提供的服務或者數據,這里的服務包括普通服務和基于AIDL的服務。
(5)AIDL即Android interface definition Language,即Android接口定義語言。
#### 2、在分析Binder的工作原理之前,我們先補充一下Android設計模式之Proxy模式#####
##### (1)Proxy代理模式簡介
代理模式是對象的結構模式。代理模式給某一個對象提供一個代理對象,并由代理對象控制對原對象的引用。
模式的使用場景:就是一個人或者機構代表另一個人或者機構采取行動。在一些情況下,一個客戶不想或者不能夠直接引用一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。


* 抽象對象角色AbstarctObject:聲明了目標對象和代理對象的共同接口,這樣一來在任何可以使用目標對象的地方都可以使用代理對象。
* 目標對象角色RealObject:定義了代理對象所代表的目標對象。
* 代理對象角色ProxyObject:代理對象內部含有目標對象的引用,從而可以在任何時候操作目標對象;代理對象提供一個與目標對象相同的接口,以便可以在任何時候替代目標對象。代理對象通常在客戶端調用傳遞給目標對象之前或之后,執行某個操作,而不是單純地將調用傳遞給目標對象。
##### (2)Proxy代理模式的簡單實現
抽象對象角色:
~~~
public abstract class AbstractObject {
//操作
public abstract void operation();
}
~~~
目標對象角色:
~~~
public class RealObject extends AbstractObject {
@Override
public void operation() {
//一些操作
System.out.println("一些操作");
}
}
~~~
代理對象角色:
~~~
public class ProxyObject extends AbstractObject{
RealObject realObject = new RealObject();//目標對象角色
@Override
public void operation() {
//調用目標對象之前可以做相關操作
System.out.println("before");
realObject.operation(); //目標對象角色的操作函數
//調用目標對象之后可以做相關操作
System.out.println("after");
}
}
~~~
客戶端:
~~~
public class Client {
public static void main(String[] args) {
AbstractObject obj = new ProxyObject();
obj.operation();
}
}
~~~
##### (3)代理模式在Binder中的使用
直觀來說,Binder是Android中的一個類,它繼承了IBinder接口。從IPC角度來說,Binder是Android中的一種跨進程通信方式,Binder還可以理解為一種虛擬的物理設備,它的設備驅動是/dev/binder,該通信方式在linux中沒有;從Android Framework角度來說,Binder是ServiceManager連接各種Manager(ActivityManager、WindowManager,etc)和相應ManagerService的橋梁;從Android應用層來說,Binder是客戶端和服務端進行通信的媒介,當你bindService的時候,服務端會返回一個包含了服務端業務調用的Binder對象,通過這個Binder對象,客戶端就可以獲取服務端提供的服務或者數據,這里的服務包括普通服務和基于AIDL的服務。
Binder一個很重要的作用是:將客戶端的請求參數通過Parcel包裝后傳到遠程服務端,遠程服務端解析數據并執行對應的操作,同時客戶端線程掛起,當服務端方法執行完畢后,再將返回結果寫入到另外一個Parcel中并將其通過Binder傳回到客戶端,客戶端接收到返回數據的Parcel后,Binder會解析數據包中的內容并將原始結果返回給客戶端,至此,整個Binder的工作過程就完成了。由此可見,Binder更像一個數據通道,Parcel對象就在這個通道中跨進程傳輸,至于雙方如何通信,這并不負責,只需要雙方按照約定好的規范去打包和解包數據即可。
為了更好地說明Binder,這里我們先手動實現了一個Binder。為了使得邏輯更清晰,這里簡化一下,我們來模擬一個銀行系統,這個銀行提供的功能只有一個:即查詢余額,只有傳遞一個int的id過來,銀行就會將你的余額設置為id*10,滿足下大家的發財夢。
1)先定義一個Binder接口(抽象對象角色):
~~~
package com.ryg.design.manualbinder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
public interface IBank extends IInterface {
/*
* Binder的唯一標識符:
* */
static final String DESCRIPTOR = "com.ryg.design.manualbinder.IBank";
/*
* queryMoney方法的code標識:
* */
static final int TRANSACTION_queryMoney = (IBinder.FIRST_CALL_TRANSACTION + 0);
/*
* queryMoney方法聲明:
* */
public long queryMoney(int uid) throws RemoteException;
}
~~~
##### 2)創建一個Binder并實現上述接口:
~~~
package com.ryg.design.manualbinder;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
public class BankImpl extends Binder implements IBank {
public BankImpl() {
this.attachInterface(this, DESCRIPTOR);
}
/*
* 如果在同一進程,則返回目標對象本身,
* 如果在不同僅此,則返回代理類
* */
public static IBank asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBank))) {
return ((IBank) iin);
}
return new BankImpl.Proxy(obj);
}
@Override
public IBinder asBinder() {
return this;
}
/*
* 這個onTransact方法是在目標對象角色中重寫的,
* 在目標對象角色調用Transact方法時回調的!
* */
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_queryMoney: {
data.enforceInterface(DESCRIPTOR);
int uid = data.readInt();
long result = this.queryMoney(uid);
reply.writeNoException();
reply.writeLong(result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/*
* 這是正兒八經的目標對象角色的queryMoney函數:
* */
@Override
public long queryMoney(int uid) throws RemoteException {
return uid * 10l;
}
/*
* 內部代理類(代理對象角色)
* */
private static class Proxy implements IBank {
/*
* 代表目標對象角色:
* */
private IBinder mRemote;
/*
* 構造函數:
* */
Proxy(IBinder remote) {
>// 接收目標對象角色:
mRemote = remote;
}
/*
* 返回目標對象角色:
* */
@Override
public IBinder asBinder() {
return mRemote;
}
/*
* 返回Binder唯一標識符:
* */
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/*
* 這是代理類Proxy中的queryMoney方法:
* */
@Override
public long queryMoney(int uid) throws RemoteException {
/*
* 先創建兩個Parcel對象
* */
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
long result;
try {
*
* 在操作前向data中寫入一些數據:
* */
data.writeInterfaceToken(DESCRIPTOR);
data.writeInt(uid);
/*
* 這里執行的mRemote.transact其實是目標對象角色的transact函數。
* 因為mRemote是IBinder對象,所以調用它的transact函數會回調它的onTransact方法,
* 這個onTransact方法是在mRemote這個目標對象角色中重寫了的,哈
* 然后要根據TRANSACTION_queryMoney的code代碼來執行相應的函數。
* data負責傳遞信息,
* reply負責回收信息。
* */
mRemote.transact(TRANSACTION_queryMoney, data, reply, 0);
/*
* 這里是返回的數據。
* */
reply.readException();
result = reply.readLong();
} finally {
reply.recycle();
data.recycle();
}
return result;
}
}
}
~~~
ok,到此為止,我們的Binder就完成了,這里只要創建服務端和客戶端,二者就能通過我們的Binder來通信了。這里就不做這個示例了,我們的目的是分析代理模式在Binder中的使用。
我們看上述Binder的實現中,有一個叫做“Proxy”的類,它的構造方法如下:
~~~
Proxy(IBinder remote) {
mRemote = remote;
}
~~~
Proxy類接收一個IBinder參數,這個參數實際上就是服務端Service中的onBind方法返回的Binder對象在客戶端重新打包后的結果,因為客戶端無法直接通過這個打包的Binder和服務端通信,因此客戶端必須借助Proxy類來和服務端通信,這里Proxy的作用就是代理的作用,客戶端所有的請求全部通過Proxy來代理,具體工作流程為:Proxy接收到客戶端的請求后,會將客戶端的請求參數打包到Parcel對象中,然后將Parcel對象通過它內部持有的Ibinder對象傳送到服務端,服務端接收數據、執行方法后返回結果給客戶端的Proxy,Proxy解析數據后返回給客戶端的真正調用者。很顯然,上述所分析的就是典型的代理模式。至于Binder如何傳輸數據,這涉及到很底層的知識,這個很難搞懂,但是數據傳輸的核心思想是共享內存。
#### 3、我們通過一個案例來分析Binder工作原理
我們需要新建一個AIDL示例,SDK會自動為我們生產AIDL所對應的Binder類。
##### (1)Book.java:這里面沒有什么特殊之處,為了實現Parcelable,添加了幾個方法,上面在Parcelable部分已經介紹過了。
~~~
package com.ryg.chapter_2.aidl;
import android.os.Parcel;
import android.os.Parcelable;
/*
* (1)它是一個表示圖示信息的類,
* 它實現了Parcelable接口,因為實現了Parcelable接口便可以進行序列化
* (2)Book.aidl是Book類在ADIL中的聲明。
* (3)IBookManager.aidl是我們定義的一個接口,里面有兩個方法:getBookList和addBook,
* 其中getBookList用于從遠程服務端獲取圖書列表,而addBook用于往圖書列表中添加一本書,
* 當然這兩個方法主要是示例用,不一定要有實際意義。
* (4)盡管Book類和IBookManager位于相同的包中,但是在IBookManager中仍然要導入Book類,
* 這就是AIDL的特殊之處。
* */
public class Book implements Parcelable {
<span style="white-space:pre"> </span>public int bookId;
public String bookName;
/*
* 普通構造函數:
* */
public Book() {
<span style="white-space:pre"> </span>
}
/*
* 普通構造函數:
* */
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
public int describeContents() {
return 0;
}
/*
* 序列化:
* */
public void writeToParcel(Parcel out, int flags) {
out.writeInt(bookId);
out.writeString(bookName);
}
/*
* 反序列化,
* 這個creator就是通過一個Parcle來創建一個book對象或者數組。
* */
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
public Book createFromParcel(Parcel in) {
return new Book(in);
}
public Book[] newArray(int size) {
return new Book[size];
}
};
/*
* 用于反序列化的構造函數:
* */
private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
@Override
public String toString() {
return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
}
}
~~~
##### (2)Book.aidl:它是Book在AIDL中的聲明。
~~~
package com.ryg.chapter_2.aidl;
parcelable Book;
~~~
(3)IBookManager.aidl:雖然Book類已經和IBookManager位于相同的包中,但是這里依然需要導入Book類。這是AIDL的特殊之處。
它是一個接口,里面有四個方法。
~~~
package com.ryg.chapter_2.aidl;
import com.ryg.chapter_2.aidl.Book;
import com.ryg.chapter_2.aidl.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
~~~
##### (4)下面我們要看一下系統為IBookManager.aidl生產的Binder類,在gen目錄下有一個IBookManager.java的類,這就是我們要找的類。
~~~
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.ryg.chapter_2.aidl;
/*
* IBookManager它繼承了IInterface這個接口,同時它自己也還是個接口,
* 所有可以在Binder中傳輸的接口都要繼承IInterface接口。
* 首先,它聲明了兩個方法getBookList和addBook,顯然這就是我們在IBookManager.aidl中所聲明的方法,
* 同時它還聲明了兩個整型的id分別用于標識這兩個方法。
* 接著,它聲明了一個內部類Stub,這個Stub就是一個Binder類,
* 當客戶端和服務端都位于同一個進程時,方法調用不會走跨進程的transact過程,
* 而當兩者位于不同進程時,方法調用需要走transact過程,
* 這個邏輯由Stub的內部代理類Proxy來完成。
* */
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
/*
* 首先這個Stub,它是一個內部類,它繼承了Binder,所以它是一個Binder,
* 同時Stub還實現了IBookManager中的方法。
* */
public static abstract class Stub extends android.os.Binder implements com.ryg.chapter_2.aidl.IBookManager
{
/*
* Binder的唯一標識符。
* */
private static final java.lang.String DESCRIPTOR = "com.ryg.chapter_2.aidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ryg.chapter_2.aidl.IBookManager interface,
* generating a proxy if needed.
*/
/*
* 用于將服務端的Binder對象轉換成客戶端所需的AIDL接口類型的對象,
* 這種轉換過程是區分進程的,
* 如果客戶端和服務端位于同一進程,那么此方法返回的就是服務端的Stub對象本身,
* 否則返回的是系統封裝后的Stub.proxy代理對象。
* */
public static com.ryg.chapter_2.aidl.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
// 同一進程
if (((iin!=null)&&(iin instanceof com.ryg.chapter_2.aidl.IBookManager))) {
return ((com.ryg.chapter_2.aidl.IBookManager)iin);
}
// 不同進程
return new com.ryg.chapter_2.aidl.IBookManager.Stub.Proxy(obj);
}
/*
* 此方法用于返回當前Binder對象,也就是內部類Stub。
* */
@Override public android.os.IBinder asBinder()
{
return this;
}
/*
* 這個方法運行在服務端中的Binder線程池中,
* 當客戶端發起跨進程請求時,遠程請求會通過系統底層封裝后交由此方法來處理。
* 服務端通過code可以確定客戶端所請求的目標方法是什么,
* 接著從data中取出目標方法所需的參數,
* 然后執行目標方法。
* 當目標方法執行完畢后,就向reply中寫入返回值。
* 如果此方法返回false,那么客戶端的請求會失敗,因此我們可以利用這個特性來做權限驗證。
* */
@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_getBookList:
{
data.enforceInterface(DESCRIPTOR);
/*
* 這句才是調用了真正的執行過程呢
* */
java.util.List<com.ryg.chapter_2.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.ryg.chapter_2.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.ryg.chapter_2.aidl.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
/*
* 這句才是調用了真正的執行過程呢
* */
this.addBook(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_registerListener:
{
data.enforceInterface(DESCRIPTOR);
com.ryg.chapter_2.aidl.IOnNewBookArrivedListener _arg0;
_arg0 = com.ryg.chapter_2.aidl.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
this.registerListener(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unregisterListener:
{
data.enforceInterface(DESCRIPTOR);
com.ryg.chapter_2.aidl.IOnNewBookArrivedListener _arg0;
_arg0 = com.ryg.chapter_2.aidl.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
this.unregisterListener(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/*
* 代理類Proxy。
* */
private static class Proxy implements com.ryg.chapter_2.aidl.IBookManager
{
/*
* 這個mRemote代表的就是目標對象角色,
* */
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;
}
/*
* 這個方法運行在客戶端,
* 因為當客戶端和服務端不在同一進程時,服務端返回代理類Proxy,所以客戶端會通過Proxy調用到代理類的getBookList方法,
* 當客戶端遠程調用此方法時,它的內部實現是這樣的:
* 首先創建該方法所需要的輸入型Parcel對象_data、輸出型Parcel對象_reply和返回值對象List,
* 然后把該方法的參數信息寫入_data中,
* 接著調用transact方法來發起RPC(遠程過程調用)請求,同時當前線程掛起,
* 然后服務端的onTransact方法會被調用,直到RPC過程返回后,當前線程繼續執行,
* 并從_reply中取出RPC過程的返回結果。
* 最后返回_reply中的數據。
* */
@Override public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.ryg.chapter_2.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.ryg.chapter_2.aidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.ryg.chapter_2.aidl.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void registerListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) 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.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void unregisterListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) 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.writeStrongBinder((((listener!=null))?(listener.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_unregisterListener, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
/*
* 用于標識方法的整型id。
* 它們用于在transact過程總客戶端所請求的到底是哪個方法。
* */
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
/*
* 聲明了在IBookManager.aidl中所聲明的方法。
* 這里才是真正的方法聲明。具體實現我們仍然沒有看到呢。
* */
public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.ryg.chapter_2.aidl.Book book) throws android.os.RemoteException;
public void registerListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
public void unregisterListener(com.ryg.chapter_2.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
}
~~~
**注意點一:**上面的Book類,就是一個可以Parcelable序列化的簡單的Book類,它里面沒有任何的方法,就是定義了一個簡單的Book類結構。
**注意點二**:Book.aidl的存在是因為在IBookManager.aidl中出現的對象也必須有aidl聲明。
**注意點三:**在IBookManager.aidl中,對于自動生成的IBookManager.java文件,它是服務器端的代碼。當客戶端向服務端發送連接請求時,如果客戶端和服務端在同一進程中,那么服務端就向客戶端返回Stub這個Binder對象,如果客戶端和服務端在不同進程中,那么服務端就向客戶端返回內部類Stub的內部代理類Proxy,然后客戶端根據這個Proxy來調用Proxy內部的方法,這個Proxy內部含有服務端真正的Binder對象也就是那個內部類Stub,在客戶端調用Proxy內部的方法也就會導致調用Stub的transact方法,而Stub的transact方法又會回調它自己的onTransact方法,onTransact方法是在服務端運行的,而transact方法是在客戶端調用的,這樣就實現了客戶端調用服務端的方法了。當然這所有的傳遞過程也少不了Parcel這個數據包的協助。整個過程懂了嗎?

這次應該完全懂了吧,再不懂去屎吧!
#### 4、linkToDeath和unlinkToDeath
Binder運行在服務端進程,如果服務端進程由于某些原因異常終止,這個時候我們到服務端的Binder連接斷裂,會導致我們的遠程調用失敗。Binder提供了兩個配對的方法linkToDeath和unlinkToDeath,通過linkToDeath我們可以給Binder設置一個死亡代理,當Binder死亡時,我們會收到通知,這個時候我們就可以重新發起連接請求從而恢復連接。
~~~
/*
* 聲明這個接口就好:
* */
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DearhRecipient(){
// 只需要重寫這個方法就可以了。
@Override
public void binderDied(){
if(mBookManager == null)
return;
mBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mBookManager = null;
// TODO: 這里重新綁定遠程Service。
}
}
~~~
在客戶端綁定遠程服務之后,給Binder設置死亡代理:
~~~
mService = IMessageBoxManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient, 0);
~~~
### 五、使用Messenger
#### 1、特點:
(1)Messenger對AIDL做了封裝,使得我們可以更簡便地進行進程間通信。由于它一次處理一個請求,所以在服務端我們不考慮線程同步的問題,因為服務端中不存在并發執行的情形。
(2)通過它可以在不同進程中傳遞Message對象,在Message中仿佛我們需要傳遞的數據,就可以輕松地實現數據的進程間傳遞了。
(3)有兩個構造函數,分別接收Handler對象和IBinder對象。
#### 2、 實現一個Messenger有如下步驟:
(1)服務端進程:
首先需要在服務端創建一個Service來處理客戶端的連接請求,同時創建一個Handler并以它作為參數來創建一個Messenger對象,然后在Service的onBind中返回這個Messenger對象底層的Binder即可。關鍵點就在于它的返回是返回給了要綁定這個服務端的客戶端,然后客戶端拿到這個Binder再去創建Messenger,再去發送Message等等。
(2)客戶端進程:
客戶端進程中,首先要綁定服務端的Service,綁定后服務端的onBind會返回一個Binder對象,然后客戶端用服務端返回的這個Binder對象創建一個Messenger,通過這個Messenger就可以向服務器端發送消息了,發送消息類型為Message對象,如果需要服務端能夠回應客戶端,就像和服務端一個,我們還需要創建一個Handler并創建一個新的Messenger,并把這個Messenger對象在第一次客戶端像服務端發送消息時通過Message的replyTo參數傳遞給服務端,服務端通過讀取Message中的replyTo參數就是服務端給客戶端的的Messenger,然后就可以回應客戶端。
(3)注意點:
客戶端給服務端發送消息的時候所用的Messenger是通過綁定服務端,然后依據onBind返回的Binder對象為參數來創建Messenger,而服務端在回應客戶端的時候所用的Messenger是客戶端在剛剛發送消息的時候將自身創建的Messenger作為剛剛發送消息的Message的replyTo參數傳遞給服務端的,所以在服務端直接讀取出這個Messenger。
#### 3、舉例:客戶端像服務端發送消息,服務端回應客戶端
##### (1)先看服務端代碼:
~~~
package com.ryg.chapter_2.messenger;
import com.ryg.chapter_2.model.User;
import com.ryg.chapter_2.utils.MyConstants;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
/*
* 首先,這是一個服務。
* 其次,這個服務是需要注冊的,并且要給它另起一個進程。
* <service
android:name=".messenger.MessengerService"
android:process=":remote" >
<intent-filter>
<action android:name="com.ryg.MessengerService.launch" />
</intent-filter>
</service>
* */
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
/*
* 繼承Handler,
* MessengerHandler用來處理客戶端發送的消息,
* 并從消息中取出客戶端發來的文本信息。
* */
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
/*
* MyConstants是我們這個應用中的一個類,其中包含了幾個變量的聲明:
* public static final int MSG_FROM_CLIENT = 0;
public static final int MSG_FROM_SERVICE = 1;
* */
case MyConstants.MSG_FROM_CLIENT:
/*
* 這一條語句是在處理從客戶端發來的消息,用Log日志打印出來:
* */
Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
/*
* 這下面的語句是用來響應客戶端,給客戶端回饋消息的。
* (1)第一步是通過replyTo來獲取客戶端的Messenger對象。
* (2)第二步是創建一個Message消息,
* Message.obtain這個方法的第一個參數是Handler,第二個參數是消息的what字段。
* (3)第三步創建一個Bundle對象,然后向這個對象中添加String內容。
* (4)第四步是將Bundle對象設置給Message。
* (5)第五步是通過Messenger將Message發送出去,
* 因為我們的Messenger是通過客戶端來獲取的,而在客戶端那邊這個Messenger是以Handler為參數創建的,
* 所以在服務端通過客戶端的Messenger發送消息后,在客戶端的Handler就會處理這條消息,嘻嘻,就達到了消息傳送的目的。
* */
Messenger client = msg.replyTo;
Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString("reply", "嗯,你的消息我已經收到,稍后會回復你。");
relpyMessage.setData(bundle);
try {
client.send(relpyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
/*
* 這是我們服務端自己的Messenger,它是以上面的Handler對象為參數創建的,
* 這個Messenger是要通過綁定該服務器的時候onBind方法傳遞給客戶端,
* 然后客戶端獲取了該Messenger,再以該Messenger來發送消息,
* 這樣服務端就可以接收到該消息并處理。
* */
private final Messenger mMessenger = new Messenger(new MessengerHandler());
/*
* 這個方法是在綁定服務的過程中調用的并將結果返回給客戶端的,
* 所以通過onBind方法客戶端就可以獲取我們Messenger的Binder對象了,
* 然后客戶端可以根據該Binder對象來創建一個Messenger,
* 這樣客戶端中用的Messenger和這里的Messenger就是向對應的了。
* */
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
}
~~~
##### (2)再看看客戶端代碼:
~~~
package com.ryg.chapter_2.messenger;
import com.ryg.chapter_2.R;
import com.ryg.chapter_2.R.layout;
import com.ryg.chapter_2.model.User;
import com.ryg.chapter_2.utils.MyConstants;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
/*
* 客戶端,首先它是一個活動。
* 其次它也需要注冊的。
* */
public class MessengerActivity extends Activity {
private static final String TAG = "MessengerActivity";
// 用來獲取服務端的Messenger,用來給服務端傳遞消息用的。
private Messenger mService;
// 這是客戶端自己的Messenger,傳遞給服務端,讓服務端返回消息用的。
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
/*
* 這個Handler是用來處理服務端返回的消息的,
* 這個Handler將作為一個參數來創建自己的Messenger,
* 然后將這個Messenger傳遞給服務端,讓服務端根據它返回消息。
* */
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MyConstants.MSG_FROM_SERVICE:
// 處理消息,以Log日志顯示出來。
Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
break;
default:
super.handleMessage(msg);
}
}
}
/*
* 這個是客戶端用來綁定服務端用的,
* 在綁定過程中會調用onServiceConnected,
* 它的第二個參數IBinder service,就是在服務端中onBind方法返回的結果,
* 這個結果是服務端的Messenger對象的Binder對象,
* 然后客戶端通過這個Binder對象就可以創建一個Messenger,
* 所以就是在綁定服務的過程中將服務端的Messenger傳遞給了客戶端,建立起了兩者之間的橋梁
* */
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
/*
* (1)第一步是根據服務端的IBinder service對象為參數創建Messenger。
* (2)第二步是創建一個Message消息,其中第二個參數是msg的what字段。
* 這里有個重要的點就是設置msg的replyTo字段,這個字段保存了客戶端自己的Messenger,
* 客戶端將自己的Messenger傳遞給服務端,然后方便服務端根據這個Messenger將反饋消息用同樣的方法傳遞回來。
* (3)第三步是創建一個Bundle對象,這個對象中添加了要返回的消息內容。
* (4)第四步將Bundle對象賦給Message。
* (5)第五步用Messenger的send方法將消息發送出去。
* */
mService = new Messenger(service);
Log.d(TAG, "bind service");
Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "hello, this is client.");
msg.setData(data);
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
/*
* 這個Intent的跳轉是需要服務端設置的:
* <service
android:name=".messenger.MessengerService"
android:process=":remote" >
<intent-filter>
<action android:name="com.ryg.MessengerService.launch" />
</intent-filter>
</service>
* */
Intent intent = new Intent("com.ryg.MessengerService.launch");
/*
* 在bindService的時候,服務端會通過onBind方法返回一個包含了服務端業務調用的Binder對象,
* 通過這個對象,客戶端就可以獲取服務端提供的服務或者數據,
* 具體情況去下面的第二個參數mConnection中查看。
* */
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
~~~
(3)看完了是不是覺得很簡單呀,嘿嘿。
### 六、使用AIDL
#### 1、對比Messenger和AIDL:
上一節講的Messenger來進行進程間的通信,可以發現,Messenger是以串行的方式處理客戶端發來的消息的,
如果大量的消息同時發送到服務端,服務端仍然只能一個個處理,如果有大量的并發請求,那么用Messenger就不太合適了。
而且Messenger的主要作用是為了傳遞消息,很多時候我們可能需要跨進程調用服務端的方法,這種情形用Messenger就無法實現了。
所以我們用AIDL來實現跨進程的方法調用。
AIDL也是Messenger的底層實現,因此Messenger本質上也是AIDL,只不過系統為我們做了封裝,從而方便上層的調用而已。
#### 2、AIDL使用的基本思想:
(0)先來放一下我們的Book.java類,它實現了Parcelable接口:
~~~
package com.ryg.chapter_2.aidl;
import android.os.Parcel;
import android.os.Parcelable;
/*
* (1)它是一個表示圖示信息的類,
* 它實現了Parcelable接口,
* (2)Book.aidl是Book類在ADIL中的聲明。
* (3)IBookManager.aidl是我們定義的一個接口,里面有兩個方法:getBookList和addBook,
* 其中getBookList用于從遠程服務端獲取圖書列表,而addBook用于往圖書列表中添加一本書,
* 當然這兩個方法主要是示例用,不一定要有實際意義。
* (4)盡管Book類和IBookManager位于相同的包中,但是在IBookManager中仍然要導入Book類,
* 這就是AIDL的特殊之處。
* */
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book() {
}
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(bookId);
out.writeString(bookName);
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
public Book createFromParcel(Parcel in) {
return new Book(in);
}
public Book[] newArray(int size) {
return new Book[size];
}
};
private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
@Override
public String toString() {
return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
}
}
~~~
##### (1)服務端:
服務端首先要創建一個Service用來監聽客戶端的連接請求,然后創建一個AIDL文件,將暴露給客戶端的接口在這個AIDL文件中聲明,最后在Service中實現這個AIDL接口即可。
##### (2)客戶端:
客戶端所要做的事情就稍微簡單一些,首先需要綁定服務端的Service,在綁定成功后,將服務端返回的Binder對象轉成AIDL接口所屬的類型,接著就可以調用AIDL中的方法了。
##### (3)AIDL接口的創建:
首先看AIDL接口的創建,如下所示,我們創建一個后綴為AIDL的文件,在里面聲明了一個接口和兩個接口方法:
這個文件的名稱是:IBookManager.aidl。
~~~
package com.ryg.chapter_2.aidl;
import com.ryg.chapter_2.aidl.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
~~~
**在AIDL文件中,并不是所有的數據類型都是可以使用的,那么到底AIDL文件支持哪些類型的數據呢?**
基本數據類型(int、long、char、boolean、double等);
String和CharSequence;
List:只支持ArrayList,里面每個元素都必須能夠被AIDL支持;
Map:只支持HashMap,里面的每個元素都必須被AIDL支持,包括key和value;
Parcelable:所有實現了Parcelable接口的對象;
AIDL:所有的AIDL接口本身也可以在AIDL文件中使用。
(其中自定義的Parcelable對象和AIDL對象必須要顯示import進來,不管它們是否和當前的AIDL文件位于同一個包內)
所以上面的IBookManager.aidl文件,里面用到了Book這個類,這個類實現了Parcelable接口并且和IBookManager位于同一個包中,但是遵守AIDL的規范,我們仍然需要顯式的import進來:import com.ryg.chapter_2.aidl.Book;
* * * * *
還有一個**注意點:**
**如果AIDL文件中用到了自定義的Parcelable對象,那么必須新建一個和它同名的AIDL文件,并在其中聲明它為Parcelable類型。**
* * * * *
因為我們在IBookManager.aidl中用到了Book這個類,所以我們必須要創建Book.aidl,然后里面添加的內容如下:
~~~
package com.ryg.chapter_2.aidl;
parcelable Book;
~~~
事實上,AIDL中每個實現了Parcelable接口的類都需要按照上面那種方式去創建相應的AIDL文件并聲明那個類為Parcelable。
除此之外,AIDL中除了基本數據類型,其他類型的參數必須標上方向:in、out、inout。
我們需要根據實際需要去指定參數類型,不能一概使用out或者inout,因為這在底層實現是有開銷的。
最后,AIDL接口中只支持方法,不支持聲明靜態常量,這一點區別于傳統的接口。
為了方便AIDL的開發,建議把所有和AIDL相關的類和文件全部放入同一個包中,這樣方便我們直接復制整個包,不容易遺漏。
需要注意的是,AIDL的包結構在服務端和客戶端要保持一致,否則運行會出錯。因為客戶端需要反序列化服務端中和AIDL接口相關的所有類,如果類的完整路徑不一樣的話,就無法成功反序列化,程序也就無法正常運行。
##### (4)遠程服務端Service的實現:
上面講的是如何定義AIDL接口,下面講如何實現這個接口。
~~~
package com.ryg.chapter_2.aidl;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
/*
* 這是一個服務端Service的典型實現。
* 首先在onCreate中初始化添加兩本圖書,
* 然后創建了一個Binder對象并在onBind方法中返回它。
* */
public class BookManagerService extends Service {
private static final String TAG = "BMS";
/*
* 注意這里采用了CopyOnWriteArrayList,這個CopyOnWriteArrayList支持并發讀/寫。
* 因為AIDL方法是在服務端的Binder線程池中執行的,因此當多個客戶端同時連接的時候,
* 會存在多個線程同時訪問的情形,所以我們要在AIDL方法中處理線程同步,
* 而我們這里直接使用CopyOnWriteArrayList來進行自動的線程同步。
* */
/*
* 在AIDL中能夠使用的List只有ArrayList,但是我們這里卻使用了CopyOnWriteArrayList(它并不是繼承子ArrayList的),
* 但為什么還能工作呢?
* 因為AIDL中所支持的是抽象List,而List只是一個接口,
* 因此雖然服務端返回的是CopyOnWriteArrayList,
* 但是Binder中會按照List的規范去訪問數據并最終形成一個ArrayList傳遞給客戶端。
* 所以我們在服務端采用CopyOnWriteArrayList是完全可以的,
* 和此類似的還有ConcurrentHashMap。
* */
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
/*
* 創建一個Binder對象,并在onBind方法中返回它。
* 這個Binder對象繼承自IBookManager.Stub,并實現了它內部的AIDL方法,
* 這里主要看getBookList和addBook這兩個AIDL方法的實現,實現過程也比較簡單,
* */
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
~~~
還需要注意的是,我們需要注冊這個服務端,并讓它運行在獨立的進程中,這樣它和客戶端的Activity不在同一個進程中,這樣就構成了進程間通信的場景:
~~~
<service
android:name=".aidl.BookManagerService"
android:process=":remote" >
</service>
~~~
##### (5)客戶端的實現:
客戶端比較容易實現,首先需要綁定遠程服務,綁定成功后將服務端返回的Binder對象轉換成AIDL接口,然后就可以通過這個接口去調用服務端的遠程方法了。
~~~
package com.ryg.chapter_2.aidl;
import java.util.List;
import com.ryg.chapter_2.R;
import com.ryg.chapter_2.aidl.IBookManager;
import com.ryg.chapter_2.utils.MyConstants;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
public class BookManagerActivity extends Activity {
private static final String TAG = "BookManagerActivity";
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
/*
* 這里的實現方式和Messenger簡直一樣樣的,
* 都是在綁定服務端的過程中,服務端通過onBind方法將它的Binder傳遞過來,
* 然后在客戶端以這個傳遞來的Binder創建對應的對象
* */
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
/*
* 然后就可以調用相應的方法了。
* */
List<Book> list = bookManager.getBookList();
Log.i(TAG, "query book list, list type:"
+ list.getClass().getCanonicalName());
Log.i(TAG, "query book list:" + list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
Intent intent = new Intent(this, BookManagerService.class);
/*
* 綁定服務:
* */
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
~~~
以上就算是一個比較簡單的完整的AIDL進行IPC的過程。
#### 3、AIDL使用過程中的一些問題,應用觀察者模式
(1)我們用觀察者模式來實現當圖書館接收到新書后,就為申請過新書到來通知的用戶發送新書通知。我們需要提供一個AIDL接口,每個用戶都需要實現這個接口并且向圖書館申請新書的提醒功能,同時也可以取消這個功能。用AIDL接口而不用普通接口是因為AIDL中無法使用普通接口。
(2)首先我們創建一個IOnNewBookArrivedListener.aidl文件,當服務端有新書到來時,就會通知每一個已經申請提醒功能的用戶。從程序上來說就是調用所有IOnNewBookArrivedListener對象中的onNewBookArrived方法,并把新書的對象通過參數傳遞給客戶端。
~~~
package com.ryg.chapter_2.aidl;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
public class BookManagerService extends Service {
private static final String TAG = "BMS";
private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
// private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =
// new CopyOnWriteArrayList<IOnNewBookArrivedListener>();
/*
* 用來保存申請了新書通知的用戶。
* (1)這里有一個注意點,RemoteCallbackList是系統專門提供的用于刪除跨進程listener的接口。
* RemoteCallbackList是一個泛型,支持管理任意的AIDL接口。
* 它的內部有一個Map結構專門用來保存所有的AIDL回調,
* 這個Map的key是IBinder類型,value是Callback類型,如下所示:
* ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();
* 其中Callback中封裝了真正的遠程listener。當客戶端注冊listener的時候,它會把這個listener的信息存入mCallbacks中,
* 其中key和value分別通過下面的方式獲得:
* IBinder key = listener.asBinder();
* Callback value = new Callback(listener, cookie);
* (2)注意點二:也就是說,雖然多次跨進程傳輸客戶端的同一個對象會在服務端生成不同的對象,
* 但這些新生成的對象有一個共同點,那就是它們的底層Binder對象是同一個。也就是說key相同。
* (3)注意點三:RemoteCallbackList在客戶端進程終止后,能夠自動移除客戶端所注冊的listener。
* 另外RemoteCallbackList內部自動實現了線程同步的功能,
* 所以我們使用它來注冊和解注冊時,不需要做額外的線程同步工作。
* */
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();
/*
* 服務端的Binder對象,要傳給客戶端的,讓客戶端調用里面的方法:
* */
private Binder mBinder = new IBookManager.Stub() {
/*
* 具體的實現原來是在服務端的服務中實現的
* */
@Override
public List<Book> getBookList() throws RemoteException {
SystemClock.sleep(5000);
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
/*
* 第二種權限驗證功能方法:
* */
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
<span style="white-space:pre"> </span>// 首先查看自定義權限com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE
int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
Log.d(TAG, "check=" + check);
if (check == PackageManager.PERMISSION_DENIED) {
return false;
}
// 然后驗證包名:
String packageName = null;
String[] packages = getPackageManager().getPackagesForUid(
getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
Log.d(TAG, "onTransact: " + packageName);
if (!packageName.startsWith("com.ryg")) {
return false;
}
return super.onTransact(code, data, reply, flags);
}
/*
* 注冊申請新書提醒的用戶:
* */
@Override
public void registerListener(IOnNewBookArrivedListener listener)
throws RemoteException {
/*
* 用RemoteCallbackList,key和value都是通過listener來獲取的:
* IBinder key = listener.asBinder();
* Callback value = new Callback(listener, cookie);
* 這個Binder是IOnNewBookArrivedListener這個aidl的Binder,
* 和IBookManager這個aidl的binder不是同一個啦。
* */
mListenerList.register(listener);
/*
* RemoteCallbackList并不是一個List,
* 遍歷RemoteCallbackList必須要使用beginBroadcast和finishBroadcast來配對使用,
* 哪怕只是為了獲取RemoteCallbackList中的元素個數。
* */
final int N = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.d(TAG, "registerListener, current size:" + N);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener)
throws RemoteException {
boolean success = mListenerList.unregister(listener);
if (success) {
Log.d(TAG, "unregister success.");
} else {
Log.d(TAG, "not found, can not unregister.");
}
/*
* RemoteCallbackList并不是一個List,
* 遍歷RemoteCallbackList必須要使用beginBroadcast和finishBroadcast來配對使用,
* 哪怕只是為了獲取RemoteCallbackList中的元素個數。
* */
final int N = mListenerList.beginBroadcast();
mListenerList.finishBroadcast();
Log.d(TAG, "unregisterListener, current size:" + N);
};
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
new Thread(new ServiceWorker()).start();
}
@Override
public IBinder onBind(Intent intent) {
/*
* 我們可以在onBind方法中進行權限驗證,驗證不能通過就直接返回null。
* 這種方法需要服務端在AndroidManifest中聲明所需的權限:
* <permission
* android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE"
* android:protectionLevel="normal" />
* 在客戶端AndroidManifest中這樣聲明才可以:
* <uses-permission
* android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE" />
* */
int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
Log.d(TAG, "onbind check=" + check);
/*
* 如果客戶端沒有使用這個權限,就會綁定失敗。
* */
if (check == PackageManager.PERMISSION_DENIED) {
return null;
}
return mBinder;
}
@Override
public void onDestroy() {
mIsServiceDestoryed.set(true);
super.onDestroy();
}
/*
* 當有新書到來的時候,通知每一位用戶:
* 這里需要注意一下的是,當新書到達的時候,
* 服務端會回調客戶端的IOnNewBookArrivedListener對象中的onNewBookArrived方法,
* 這個方法是在客戶端的Binder線程池中執行的
* */
private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++) {
IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
if (l != null) {
try {
l.onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
}
/*
* 我們設定每隔5m添加一本新書:
* */
private class ServiceWorker implements Runnable {
@Override
public void run() {
// do background processing here.....
while (!mIsServiceDestoryed.get()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId, "new book#" + bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
~~~
##### (3)客戶端BookManagerActivity.java:
~~~
package com.ryg.chapter_2.aidl;
import java.util.List;
import com.ryg.chapter_2.R;
import com.ryg.chapter_2.aidl.IBookManager;
import com.ryg.chapter_2.utils.MyConstants;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
public class BookManagerActivity extends Activity {
private static final String TAG = "BookManagerActivity";
private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
private IBookManager mRemoteBookManager;
/*
* 當有新書到來的時候,服務端通知每一位用戶:
* 這里需要注意一下的是,當新書到達的時候,
* 服務端會回調客戶端的IOnNewBookArrivedListener對象中的onNewBookArrived方法,
* 這個方法是在客戶端的Binder線程池中執行的,
* 因此為了便于進行UI操作,我們需要一個Handler可以將其切換到客戶端的主線程中去執行。
* */
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
Log.d(TAG, "receive new book :" + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());
if (mRemoteBookManager == null)
return;
mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mRemoteBookManager = null;
// TODO:榪欓噷閲嶆柊緇戝畾榪滅▼Service
}
};
/*
* 連接服務器,這個IBinder service就是服務器返回給我們的Binder對象。
* */
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
/*
* 如果客戶端和服務端在同一進程,那么asInterface返回內部類Stub,
* 否則返回內部類Stub的內部代理類Proxy:
* */
IBookManager bookManager = IBookManager.Stub.asInterface(service);
mRemoteBookManager = bookManager;
try {
/*
* 給Binder設置死亡代理:
* */
mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);
List<Book> list = bookManager.getBookList();
Log.i(TAG, "query book list, list type:"
+ list.getClass().getCanonicalName());
Log.i(TAG, "query book list:" + list.toString());
Book newBook = new Book(3, "Android榪涢樁");
bookManager.addBook(newBook);
Log.i(TAG, "add book:" + newBook);
List<Book> newList = bookManager.getBookList();
Log.i(TAG, "query book list:" + newList.toString());
/*
* 申請新書提醒功能:
* */
bookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {
mRemoteBookManager = null;
Log.d(TAG, "onServiceDisconnected. tname:" + Thread.currentThread().getName());
}
};
/*
* 每個客戶端用戶內部都有這樣一個對象的,用來傳遞給服務端注冊新書提醒的。
* */
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
/*
* 當有新書到來的時候,服務端通知每一位用戶:
* 這里需要注意一下的是,當新書到達的時候,
* 服務端會回調客戶端的IOnNewBookArrivedListener對象中的onNewBookArrived方法,
* 這個方法是在客戶端的Binder線程池中執行的,
* 因此為了便于進行UI操作,我們需要一個Handler可以將其切換到客戶端的主線程中去執行。
* */
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook)
.sendToTarget();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book_manager);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
public void onButton1Click(View view) {
Toast.makeText(this, "click button1", Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
@Override
public void run() {
if (mRemoteBookManager != null) {
try {
List<Book> newList = mRemoteBookManager.getBookList();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}).start();
}
@Override
protected void onDestroy() {
if (mRemoteBookManager != null
&& mRemoteBookManager.asBinder().isBinderAlive()) {
try {
Log.i(TAG, "unregister listener:" + mOnNewBookArrivedListener);
mRemoteBookManager
.unregisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
}
~~~
**注意點一**:客戶端調用遠程服務的方法,被調用的方法運行在服務端的Binder線程池中,同時客戶端線程會被掛起。這個時候如果服務端方法執行比較耗時,就會導致客戶端線程長時間的阻塞在這里,而如果這個客戶端線程是UI線程的話,就會導致客戶端ANR。
**注意點二**:由于客戶端的onServiceConnected和onServiceDisconnected方法都運行在UI線程中,所以也不可以在他們里面直接調用服務端的耗時方法,這點要尤其注意。
**注意點三**:由于服務端的方法本身就運行在服務端的Binder線程中,所以服務端方法本身就可以執行大量耗時操作,這個時候切記不要在服務端方法中開線程去執行異步任務。懂?就是耗時操作在服務端方法中直接執行,不要再開啟其他的線程來執行耗時操作啦。
**注意點四**:同理,當遠程服務端需要調用客戶端的listener中的方法時,被調用的方法也運行在Binder線程池中,只不過是客戶端的線程池,所以,我們同樣不可以在服務端中調用客戶端的耗時方法。如果非要調用耗時方法,請確保這個方法運行在非UI線程中,否則將導致服務端無法響應。
**注意點五**:AIDL使用方法總結:
首先建一個Service和一個AIDL接口,接著創建一個類繼承自AIDL接口中的Stub類并實現Stub中的抽象方法,在Service的onBind方法中返回這個類的對象,然后客戶端就可以綁定服務端Service,建立連接后就可以訪問遠程服務端的方法了。
### 七、Binder連接池
#### 1、問題:隨著AIDL數量的增加,我們不能無限制的增加Service。
所以,我們需要減少Service的數量,將所有的AIDL放在同一個Service中去管理。
#### 2、工作機制
每個業務模塊創建自己的AIDL接口并實現此接口,這個時候不同業務模塊之間是不能有耦合的,所有實現細節我們要單獨開來,然后向服務端提供自己的唯一標識和其對應的Binder對象。對于服務端來說,只需要一個Service就可以了,服務端提供一個queryBind而接口,這個接口能夠根據業務模塊的特征來返回相應的Binder對象給它們,不同的業務模塊拿到所需的Binder對象后就可以進行遠程方法調用了。由此可見,Binder連接池的主要作用是將每個業務模塊的Binder請求統一轉發到遠程Service中去執行,從而避免了重復創建Service的過程。

#### 3、舉例說明吧
(1)我們有兩個AIDL接口(ISecurityCenter和ICompute)來模擬兩個業務模塊。然后系統會為它們兩個在gen目錄下分別生成ISecurityCenter.java和ICompute.java文件。
ISecurityCenter.aidl:(加密解密)
~~~
package com.ryg.chapter_2.binderpool;
interface ISecurityCenter {
String encrypt(String content);
String decrypt(String password);
}
~~~
ICompute.aidl:
~~~
package com.ryg.chapter_2.binderpool;
interface ICompute {
int add(int a, int b);
}
~~~
(2)這是上面兩個AIDL接口的實現:其中ISecurityCenter.Stub和ICompute.Stub是在系統在gen目錄下自動生成的ISecurityCenter.java和ICompute.java文件中的內部類Stub。在內部類中有它們方法的聲明,在這里我們繼承這個內部類并重寫實現這些方法。
SecurityCenterImpl.java:
~~~
package com.ryg.chapter_2.binderpool;
import android.os.RemoteException;
public class SecurityCenterImpl extends ISecurityCenter.Stub {
private static final char SECRET_CODE = '^';
@Override
public String encrypt(String content) throws RemoteException {
char[] chars = content.toCharArray();
for (int i = 0; i < chars.length; i++) {
chars[i] ^= SECRET_CODE;
}
return new String(chars);
}
@Override
public String decrypt(String password) throws RemoteException {
return encrypt(password);
}
}
~~~
ComputeImpl.java:
~~~
package com.ryg.chapter_2.binderpool;
import android.os.RemoteException;
public class ComputeImpl extends ICompute.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
~~~
(3)為Binder連接池創建AIDL接口IBinderPool.aidl:
~~~
package com.ryg.chapter_2.binderpool;
interface IBinderPool {
/**
* @param binderCode, the unique token of specific Binder<br/>
* @return specific Binder who's token is binderCode.
*/
IBinder queryBinder(int binderCode);
}
~~~
(4)為Binder連接池創建遠程Service并實現IBinderPool。下面是queryBinder的具體實現,當Binder連接池連接上遠程服務時,會根據不同模塊的標識即binderCode返回不同的Binder對象,通過這個Binder對象所執行的操作全部發生在遠程服務端:
~~~
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch (binderCode) {
case BINDER_SECURITY_CENTER: {
binder = new SecurityCenterImpl();
break;
}
case BINDER_COMPUTE: {
binder = new ComputeImpl();
break;
}
default:
break;
}
return binder;
}
~~~
(5)遠程Service的實現就比較簡單了:以前直接返回的是服務端的Binder對象,如今在onBind中返回的是BinderPool連接池。
~~~
package com.ryg.chapter_2.binderpool;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class BinderPoolService extends Service {
private static final String TAG = "BinderPoolService";
/*
* 在服務端創建一個連接池,BinderPoolImpl是BinderPool的內部類,
* 它繼承了IBinderPool.Stub,并實現了queryBinder方法。
* */
private Binder mBinderPool = new BinderPool.BinderPoolImpl();
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
/*
* 返回連接池對象:
* */
return mBinderPool;
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
~~~
(6)Binder連接池的具體實現,在它的內部首先它要去綁定遠程服務,綁定成功后,客戶端就可以通過它的queryBinder方法去獲取各自對應的Binder,拿到所需的Binder以后,不同業務模塊就可以進行各自的操作了:
~~~
package com.ryg.chapter_2.binderpool;
import java.util.concurrent.CountDownLatch;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
public class BinderPool {
private static final String TAG = "BinderPool";
public static final int BINDER_NONE = -1;
public static final int BINDER_COMPUTE = 0;
public static final int BINDER_SECURITY_CENTER = 1;
private Context mContext;
private IBinderPool mBinderPool;
private static volatile BinderPool sInstance;
private CountDownLatch mConnectBinderPoolCountDownLatch;
private BinderPool(Context context) {
mContext = context.getApplicationContext();
connectBinderPoolService();
}
/*
* 返回BinderPool的實例,如果沒有的話就創建,有的話就直接返回。
* */
public static BinderPool getInsance(Context context) {
if (sInstance == null) {
synchronized (BinderPool.class) {
if (sInstance == null) {
sInstance = new BinderPool(context);
}
}
}
return sInstance;
}
/*
* 連接BinderPoolService服務器。
* */
private synchronized void connectBinderPoolService() {
/*
* mConnectBinderPoolCountDownLatch這個東西是干嘛的?
* */
mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
Intent service = new Intent(mContext, BinderPoolService.class);
mContext.bindService(service, mBinderPoolConnection,
Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* query binder by binderCode from binder pool
*
* @param binderCode
* the unique token of binder
* @return binder who's token is binderCode<br>
* return null when not found or BinderPoolService died.
*/
/*
* queryBinder,
* */
public IBinder queryBinder(int binderCode) {
IBinder binder = null;
try {
/*
* 這個mBinderPool是一個BinderPool.BinderPoolImpl對象。
* 對于客戶端來說調用的是BinderPool的queryBinder方法,
* 而BinderPool的queryBinder方法又調用了BinderPool.BinderPoolImpl對象的queryBinder方法。
* mBinderPool這個對象是服務端返回給BinderPool的,對客戶端是隱藏的,
* 客戶端只知道BinderPool,
* mBinderPool是服務端和連接池的橋梁,
* BinderPool是客戶端和連接池的橋梁
* */
if (mBinderPool != null) {
binder = mBinderPool.queryBinder(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
/*
* 連接服務器的時候用的,里面有連接成功和連接斷開后的操作。
* */
private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// ignored.
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/*
* 將服務器端的Binder轉換成客戶端所需的AIDL接口對象:
* 服務端返回的是BinderPool連接池,而不是單純的一個Binder對象。
* */
mBinderPool = IBinderPool.Stub.asInterface(service);
try {
/*
* 設置死亡代理:
* */
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mConnectBinderPoolCountDownLatch.countDown();
}
};
/*
* 設置死亡代理:
* */
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.w(TAG, "binder died.");
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
/*
* (1)這個是我們的Binder連接池,它源于IBinderPool.aidl這個AIDL,它里面包含一個queryBinder方法,
* 我們的Binder連接池是放在服務端用,
* 所以在服務端需要有這樣一個BinderPoolImpl的實例,并且它是一個Binder:
* private Binder mBinderPool = new BinderPool.BinderPoolImpl();
* (2)那怎么用呢?
* 我們當前所在的類BinderPool.java就是用來綁定服務端的客戶端,
* 在BinderPool綁定服務端的時候,服務端會將mBinderPool返回給客戶端也就是我們這個類,
* 然后我們可以根據服務端返回的這個Binder來轉換成客戶端所需的AIDL接口對象,還是叫mBinderPool,
* 然后我們這個類中就可以調用mBinderPool中的方法:
* binder = mBinderPool.queryBinder(binderCode);
* (3)那另外的兩個AIDL呢?ICompute.aidl和ISecurityCenter.aidl呢?
* 由于另外的兩個AIDL的使用都是和服務端相關聯的,是服務端的queryBinder方法將它們的Binder返回給客戶端的,
* 客戶端接到這兩個AIDL的Binder以后,依舊是通過轉換成AIDL接口對象來使用這兩個AIDL中的方法的。
* */
public static class BinderPoolImpl extends IBinderPool.Stub {
public BinderPoolImpl() {
super();
}
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch (binderCode) {
case BINDER_SECURITY_CENTER: {
binder = new SecurityCenterImpl();
break;
}
case BINDER_COMPUTE: {
binder = new ComputeImpl();
break;
}
default:
break;
}
return binder;
}
}
}
~~~
**注意點一**:mBinderPool是BinderPool.BinderPoolImpl對象,這個BinderPool.BinderPoolImpl是BinderPool中的一個內部類,里面具體實現了queryBinder方法。服務端會創建一個mBinderPool對象然后在BinderPool對其綁定的過程中返回給BinderPool,這樣BinderPool和服務端就通過mBinderPool這個對象進行聯系。
**注意點二**:對于客戶端來說,首先他要獲取這個BinderPool連接池,然后根據BinderPool的queryBinder來獲取它對應的Binder對象,然后根據這個Binder對象可以去執行具體的方法。
**注意點三:首先需要搞清楚的是,哪里是在服務端運行的,哪里是在客戶端運行的。**
對于客戶端而言,僅僅是有一個ISecurityCenter和ICompute的對象,它們是AIDL,這兩個對象是沒有方法的具體實現的,具體實現是在服務端的。在服務端有SecurityCenterImpl和ComputeImpl來繼承ISecurityCenter.Stub和ICompute.Stub,因為Stub是Binder對象,所以它們兩個也是Binder,里面還給出了方法的具體實現。但服務端和客戶端并不在同一個進程中,那么客戶端為了調用服務端的方法,就必須使用Binder對象,所以客戶端要去綁定服務端,然后服務端返回Binder對象。但當我們使用了連接池BinderPool的時候,讓連接池BinderPool與服務端BinderPoolService綁定。在服務端BinderPoolService中有這樣一個對象:mBinderPool,它是BinderPool.BinderPoolImpl,BinderPool.BinderPoolImpl是BinderPool的一個內部類,里面有一個queryBinder方法,用來返回真正的對應客戶端的Binder對象,在連接池BinderPool與服務端綁定以后,服務端將這個mBinderPool對象返回給連接池,這樣連接池就可以通過這個mBinderPool對象為客戶端返回相應的Binder對象。這樣當多個種類的客戶端想要綁定服務端的時候,只需要直接調用連接池就可以了,因為連接池根據服務端給它的mBinderPool掌管了所有的Binder對象,不過要注意的是,連接池是通過服務端返回的連接池實現對象才能管理這些Binder,所以說,所有的Binder對象還是由服務端來掌管的。連接池會為對應的客戶端返回對應的Binder對象,這些Binder對象就是SecurityCenterImpl具體實現方法的Binder。
##### (7)下面就驗證一下Binder連接池的效果了:看客戶端。
~~~
package com.ryg.chapter_2.binderpool;
import com.ryg.chapter_2.R;
import android.app.Activity;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
/*
* 這里是客戶端
* */
public class BinderPoolActivity extends Activity {
private static final String TAG = "BinderPoolActivity";
private ISecurityCenter mSecurityCenter;
private ICompute mCompute;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_binder_pool);
/*
* 在線程中去執行:
* */
new Thread(new Runnable() {
@Override
public void run() {
doWork();
}
}).start();
}
private void doWork() {
// 首先獲取一個BinderPool的實例:這里是帶了上下文的,避免創建多個。
BinderPool binderPool = BinderPool.getInsance(BinderPoolActivity.this);
/*
* 然后根據客戶端編號bindercode查詢Binder,返回的是對應的客戶端的Binder。
* 在binderPool.queryBinder中,是根據在綁定服務端過程中返回的BinderPoolImpl的Binder,
* 這個BinderPoolImpl就是繼承了IBinderPool的,所以也實現了其中的queryBinder的。
* 這樣返回的才是真正對應的securityBinder。
* */
IBinder securityBinder = binderPool
.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
;
/*
* 查到對應的Binder以后,就可以根據這個Binder來轉換成客戶端所需的AIDL接口對象:
* */
mSecurityCenter = (ISecurityCenter) SecurityCenterImpl
.asInterface(securityBinder);
Log.d(TAG, "visit ISecurityCenter");
String msg = "helloworld-安卓";
System.out.println("content:" + msg);
try {
/*
* 有了接口對象,自然就可以調用對象中的方法了:
* */
String password = mSecurityCenter.encrypt(msg);
System.out.println("encrypt:" + password);
System.out.println("decrypt:" + mSecurityCenter.decrypt(password));
} catch (RemoteException e) {
e.printStackTrace();
}
/*
* 下面這是另一個AIDL模塊,使用方法和上面是一樣的。
* */
Log.d(TAG, "visit ICompute");
IBinder computeBinder = binderPool
.queryBinder(BinderPool.BINDER_COMPUTE);
;
mCompute = ComputeImpl.asInterface(computeBinder);
try {
System.out.println("3+5=" + mCompute.add(3, 5));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
~~~
#### 八、使用Bundle
1、四大組件中的三大組件(Activity、Service、Receiver)都是支持在Intent中傳遞Bundle數據的
由于Bundle實現了Parcelable接口,所以它可以方便的在不同的進程間傳輸。我們可以在Bundle中附加我們需要傳輸給遠程進程的信息并通過Intent發送出去。
##### 2、一個特殊的使用場景
比如A進程在進行一個計算,計算完成后它要啟動B進程的一個組件并把計算結果傳遞給B進程,可是遺憾的是這個計算結果并不支持放入Bundle中,因此無法通過Intent來傳輸,這個時候如果我們用其他IPC方式就會略顯復雜。可以考慮如下方式:我們通過Intent啟動進程B的一個Service組件(比如IntentService),讓Service在后臺進行計算,計算完畢后在啟動B進程中真正要啟動的目標組件,由于Service也運行在B進程中,所以目標組件就可以直接獲取計算結果,這樣一來就輕松解決了跨進程的問題。這種方式的核心思想在于將原本需要在A進程的計算任務轉移到B進程的后臺Service中去執行。
#### 九、使用ContentProvide
#### 十、使用Socket
#### 1、Socket套接字
(1)網絡通信,分為流式套接字和用戶數據報套接字兩種,分別對應于網絡的傳輸控制層中的TCP和UDP協議。
(2)Socket本身可以支持傳輸任意字節流。
#### 2、使用Socket進行通信,首先需要聲明權限
~~~
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
~~~
#### 3、使用Socket進行通信,不能再主線程中訪問網絡
因為這會導致我們的程序無法在Android4.0及以上的設備上運行,會拋出異常:android.os.NetworkOnMainThreadException。而且進行網絡操作很可能是耗時的。
#### 4、案例:跨進程的聊天程序
(1)首先在遠程Service建立一個TCP服務,然后在主界面中連接TCP服務,連接上了以后,就可以給服務端發消息,然后服務端隨機的回應我們一條信息。我們的服務端可以和多個客戶建立連接并響應。
(2)服務端。當Service啟動時,會在線程中建立TCP服務,這里監聽的是8688端口,然后就可以等待客戶端的連接請求。當有客戶端連接時,就會生成一個新的Socket,通過每次新創建的Socket就可以分別和不同的客戶端通信了。服務端每收到一次客戶端的消息就會隨機回復一句話給客戶端。當客戶端斷開連接時,服務端這邊也會相應的關閉對應的Socket并結束通話線程。
~~~
package com.ryg.chapter_2.socket;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
import com.ryg.chapter_2.utils.MyUtils;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class TCPServerService extends Service {
private boolean mIsServiceDestoryed = false;
private String[] mDefinedMessages = new String[] {
"你好啊,哈哈",
"請問你叫什么名字呀?",
"巴拉巴拉",
"巴拉巴拉小魔仙",
"艸"
};
@Override
public void onCreate() {
<span style="white-space:pre"> </span>/*
<span style="white-space:pre"> </span> * 開啟一個線程,TcpServer是實現了Runnable的,
<span style="white-space:pre"> </span> * 里面開啟了服務端這邊的Socket。
<span style="white-space:pre"> </span> * 這里的TcpServer是繼承自Runnable的。
<span style="white-space:pre"> </span> * */
new Thread(new TcpServer()).start();
super.onCreate();
}
/*
* 這次不需要綁定服務端。
* */
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
<span style="white-space:pre"> </span>/*
<span style="white-space:pre"> </span> * 用來告訴Socket線程,我們的服務結束了。
<span style="white-space:pre"> </span> * */
mIsServiceDestoryed = true;
super.onDestroy();
}
/*
* 在線程里面開啟Socket通信。
* 對于服務端,就是開啟一個Socket端口,等待客戶端來發起連接請求:
* */
private class TcpServer implements Runnable {
@SuppressWarnings("resource")
@Override
public void run() {
<span style="white-space:pre"> </span>/*
<span style="white-space:pre"> </span> * ServerSocket這種東西是系統自帶的啦,直接拿來用就好了,
<span style="white-space:pre"> </span> * 就這么容易服務端就開啟了Socket等待客戶端來連接:
<span style="white-space:pre"> </span> * */
ServerSocket serverSocket = null;
try {
<span style="white-space:pre"> </span>// 監聽本地8688端口:
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
System.err.println("establish tcp server failed, port:8688");
e.printStackTrace();
return;
}
/*
* 開啟Socket以后,服務端只需要一直等待就好了。
* */
while (!mIsServiceDestoryed) {
try {
// 接收客戶端請求,如果一直沒有客戶端,程序就會卡在這句,卡著!
final Socket client = serverSocket.accept();
System.out.println("accept");
/*
* 注意:不能在主線程中訪問網絡。
* 一來是不允許的,
* 二來放在主線程也會影響程序的響應效率。
* 每來一個客戶端連接就開啟一個線程。
* */
new Thread() {
@Override
public void run() {
try {
<span style="white-space:pre"> </span>// 這個響應方法在下面定義的。
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
};
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
* 用來相應客戶端:
* */
private void responseClient(Socket client) throws IOException {
// 用于接收客戶端消息:
<span style="white-space:pre"> </span>// BufferedReader用于接收:
BufferedReader in = new BufferedReader(new InputStreamReader(
client.getInputStream()));
// 用于向客戶端發送消息:
// PrintWriter用于發送:
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(client.getOutputStream())), true);
out.println("歡迎來到聊天室");
/*
* 當客戶端與服務端的連接沒有斷開時,服務器就一直監聽來自客戶端的Socket:
* */
while (!mIsServiceDestoryed) {
String str = in.readLine();
System.out.println("msg from client:" + str);
/*
* 當客戶端斷開連接后,服務端這邊的輸入流in會接收到null,
* 這個時候就要break退出了。
* */
if (str == null) {
break;
}
int i = new Random().nextInt(mDefinedMessages.length);
String msg = mDefinedMessages[i];
out.println(msg);
System.out.println("send :" + msg);
}
System.out.println("client quit.");
// 關閉流
MyUtils.close(out);
MyUtils.close(in);
client.close();
}
}
~~~
#### (3)客戶端:
~~~
package com.ryg.chapter_2.socket;
import java.io.*;
import java.net.Socket;
import java.sql.Date;
import java.text.SimpleDateFormat;
import com.ryg.chapter_2.R;
import com.ryg.chapter_2.utils.MyUtils;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class TCPClientActivity extends Activity implements OnClickListener {
private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
private static final int MESSAGE_SOCKET_CONNECTED = 2;
private Button mSendButton;
private TextView mMessageTextView;
private EditText mMessageEditText;
private PrintWriter mPrintWriter;
private Socket mClientSocket;
/*
*
* */
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_RECEIVE_NEW_MSG: {
mMessageTextView.setText(mMessageTextView.getText()
+ (String) msg.obj);
break;
}
case MESSAGE_SOCKET_CONNECTED: {
mSendButton.setEnabled(true);
break;
}
default:
break;
}
}
};
/*
*
* */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tcpclient);
mMessageTextView = (TextView) findViewById(R.id.msg_container);
mSendButton = (Button) findViewById(R.id.send);
mSendButton.setOnClickListener(this);
mMessageEditText = (EditText) findViewById(R.id.msg);
/*
* 先把服務端啟動了:startService(service);
* 然后在一個線程中去連接服務端的Socket:connectTCPServer();
* */
Intent service = new Intent(this, TCPServerService.class);
startService(service);
/*
* 開啟一個線程去連接服務端Socket:
* */
new Thread() {
@Override
public void run() {
connectTCPServer();
}
}.start();
}
/*
* 當Activity退出的時候,記得關閉和服務端的Socket連接:
* */
@Override
protected void onDestroy() {
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
/*
* 點擊Button發送消息給服務端:
* */
@Override
public void onClick(View v) {
if (v == mSendButton) {
final String msg = mMessageEditText.getText().toString();
if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
mPrintWriter.println(msg);
mMessageEditText.setText("");
String time = formatDateTime(System.currentTimeMillis());
final String showedMsg = "self " + time + ":" + msg + "\n";
mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
}
}
}
@SuppressLint("SimpleDateFormat")
private String formatDateTime(long time) {
return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
}
/*
* 連接服務端的Socket。
* */
private void connectTCPServer() {
Socket socket = null;
/*
* 為了確定能夠連接成功,這里采用了超時重連的策略,
* 每次連接失敗后都會重新建立嘗試連理連接。
* */
while (socket == null) {
try {
socket = new Socket("localhost", 8688);
mClientSocket = socket;
/*
* 這是客戶端用來發送消息的輸出流:
* */
mPrintWriter = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
/*
* 用一個Handler來進行和UI交互,因為我們的客戶端是在線程中與服務端進行連接的:
* */
mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
System.out.println("connect server success");
} catch (IOException e) {
/*
* 為了降低重試機制的開銷,我們加入了休眠機制,
* 即每次重試的時間間隔為1000毫秒。
* */
SystemClock.sleep(1000);
System.out.println("connect tcp server failed, retry...");
}
}
try {
// 用于接收服務器端的消息
BufferedReader br = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
// 不斷循環的接收消息,當Activity退出時,循環也退出并終止線程:
while (!TCPClientActivity.this.isFinishing()) {
// 如果沒有消息會卡住的:
String msg = br.readLine();
System.out.println("receive :" + msg);
if (msg != null) {
String time = formatDateTime(System.currentTimeMillis());
final String showedMsg = "server " + time + ":" + msg
+ "\n";
mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg)
.sendToTarget();
}
}
System.out.println("quit...");
// 關閉流:
MyUtils.close(mPrintWriter);
MyUtils.close(br);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
(4)其實Socket不僅僅能實現進程間的通信,還可以實現設備間的通信,前提是這些設備之間的IP地址是互相可見的。
- 前言
- 第一章Activity的生命周期和啟動模式
- 1.1 Activity生命周期全面分析
- 1.2 Activity的啟動模式
- 1.3 IntentFilter的匹配規則
- 第二章IPC
- 轉 chapter IPC
- 轉IPC1
- 轉IPC2
- Binder講解
- binder
- Messenger
- 一、Android IPC簡介
- 二、Android中的多進程模式
- 三、IPC基礎概念介紹
- 四、Android中的IPC方式
- 五、Binder連接池
- 第三章
- 第九章四大組件的工作過程
- 第十章
- 第13章 綜合技術
- 使用CrashHandler 來獲取應用的crash 信息
- 使用Multidex來解決方法數越界
- Android的動態加載技術