IPC機(jī)制

一揍移、IPC簡(jiǎn)介

(1)IPC是Inter-Process Communication的縮寫(xiě)只损,含義為進(jìn)程間通信或者跨進(jìn)程通信滑肉,是指兩個(gè)進(jìn)程之間進(jìn)行數(shù)據(jù)交換的過(guò)程包各。

(2)ANR是Application Not Responding的縮寫(xiě),即應(yīng)用無(wú)響應(yīng)靶庙。主線(xiàn)程執(zhí)行大量的耗時(shí)操作容易導(dǎo)致ANR現(xiàn)象發(fā)生髓棋。

(3)在Android中最有特色的進(jìn)程間通信方式就是Binder了,通過(guò)Binder可以輕松地實(shí)現(xiàn)進(jìn)程間通信惶洲。

(4)Android還支持Socket按声,通過(guò)Socket也可以實(shí)現(xiàn)任意兩個(gè)終端或者兩個(gè)進(jìn)程之間的通信。

二恬吕、Android中的多進(jìn)程模式

1签则、在Android中使用多進(jìn)程只有一種方法:

就是給四大組件(Activity、Service铐料、Receiver渐裂、ContentProvider)在AndroidManifest中指定android:process屬性。

可以在Eclipse的DDMS視圖中查看進(jìn)程信息钠惩,還可以用shell來(lái)查看柒凉,命令為: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沒(méi)有指定process屬性膝捞,所以它運(yùn)行在默認(rèn)的進(jìn)程中,默認(rèn)進(jìn)程的進(jìn)程名是包名愧沟。

(2)SecondActivity會(huì)運(yùn)行在一個(gè)單獨(dú)的進(jìn)程中蔬咬,進(jìn)程名為“com.ryg.chapter_2:remote”鲤遥,其中com.ryg.chapter_2是包名。在程序中的冒號(hào)“:”的含義是指要在當(dāng)前的進(jìn)程名前面附加上當(dāng)前的包名林艘,是一種簡(jiǎn)寫(xiě)的方法盖奈。而且以“:”開(kāi)頭的進(jìn)程屬于當(dāng)前應(yīng)用的私有進(jìn)程,其他應(yīng)用的組件不可以和它跑在同一個(gè)進(jìn)程中狐援。

(3)ThirdActivity會(huì)運(yùn)行在另一個(gè)單獨(dú)的進(jìn)程中钢坦,進(jìn)程名為“com.ryg.chapter_2.remote”。這是一種完整的命名方式啥酱。屬于全局進(jìn)程场钉,其他應(yīng)用通過(guò)ShareUID方式可以和它跑在同一個(gè)進(jìn)程中。

注意點(diǎn)一:Android系統(tǒng)會(huì)為每一個(gè)應(yīng)用分配一個(gè)唯一的UID懈涛,具有相同UID的應(yīng)用才能共享數(shù)據(jù)逛万。要求兩個(gè)應(yīng)用具有相同的ShareUID并且簽名相同才可以跑在同一個(gè)進(jìn)程中。在這種情況下批钠,它們可以互相訪(fǎng)問(wèn)對(duì)方的私有數(shù)據(jù)宇植,比如data目錄、組件信息等埋心,不管它們是否跑在同一個(gè)進(jìn)程中指郁。當(dāng)然如果它們跑在同一個(gè)進(jìn)程中,那么除了能共享data目錄拷呆、組件信息闲坎,還可以共享內(nèi)存數(shù)據(jù),或者說(shuō)它們看起來(lái)就像是一個(gè)應(yīng)用的兩個(gè)部分茬斧。

2腰懂、多進(jìn)程模式的運(yùn)行機(jī)制

(1)多進(jìn)程會(huì)帶來(lái)很多意想不到的麻煩,因?yàn)锳ndroid為每一個(gè)應(yīng)用都分配了一個(gè)獨(dú)立的虛擬機(jī)项秉,或者說(shuō)為每個(gè)進(jìn)程都分配了一個(gè)獨(dú)立的虛擬機(jī)绣溜,不同的虛擬機(jī)在內(nèi)存分配上有不同的地址空間,這就導(dǎo)致在不同的虛擬機(jī)中訪(fǎng)問(wèn)同一個(gè)類(lèi)的對(duì)象會(huì)產(chǎn)生多份副本娄蔼。這樣很就容易導(dǎo)致數(shù)據(jù)不同步怖喻。

(2)所有運(yùn)行在不同進(jìn)程的四大組件,只要它們之間需要通過(guò)內(nèi)存在共享數(shù)據(jù)岁诉,都會(huì)共享失敗锚沸。

(3)主要有以下四方面的問(wèn)題:

1)靜態(tài)成員和單例模式完全失效。(由獨(dú)立虛擬機(jī)造成)

2)線(xiàn)程同步機(jī)制完全失效涕癣。(同上)

3)SharedPreferences的可靠性下降哗蜈。(存在并發(fā)讀寫(xiě)的問(wèn)題)

4)Application會(huì)多次創(chuàng)建。(新的進(jìn)程中又會(huì)導(dǎo)致進(jìn)程所在的Application在新的虛擬機(jī)中再次創(chuàng)建)

(4)運(yùn)行在同一個(gè)進(jìn)程中的組件是屬于同一個(gè)虛擬機(jī)和同一個(gè)Application的,同理運(yùn)行在不同進(jìn)程的組件是屬于兩個(gè)不同的虛擬機(jī)和Application的恬叹。

基于上面的這些問(wèn)題候生,因?yàn)槲覀冃枰獙W(xué)習(xí)進(jìn)程間通信機(jī)制M小U乐纭!P胛稀硅确!

三、IPC基礎(chǔ)概念介紹

當(dāng)我們需要通過(guò)Intent和Binder傳輸數(shù)據(jù)時(shí)就需要使用Parcelable或者Serializeble明肮。Serializable和Parcelable接口可以完成對(duì)象的序列化過(guò)程菱农。還有時(shí)候我們需要把對(duì)象持久化到存儲(chǔ) 設(shè)備上或者通過(guò)網(wǎng)絡(luò)傳輸給其他客戶(hù)端,這個(gè)時(shí)候也需要Serializable來(lái)完成對(duì)象的持久化柿估。

1循未、Serializable接口

(1)Serializable是Java所提供的一個(gè)序列化接口,它是一個(gè)空接口秫舌,為對(duì)象提供標(biāo)準(zhǔn)的序列化和反序列化操作的妖。使用Serializable來(lái)實(shí)現(xiàn)序列化非常簡(jiǎn)單,只需要在類(lèi)的聲明中指定一個(gè)類(lèi)似下面的標(biāo)識(shí)即可自動(dòng)實(shí)現(xiàn)默認(rèn)的序列化過(guò)程足陨。

public class User implements Serializable{  
    private static final long serialVersionUID = 8723148825838841922L;  
      
    public int userId;  
    public String userName;  
    public boolean isMale;  
}

(2)只需要采用ObjectOutputStream和ObjectInputStream即可輕松實(shí)現(xiàn)對(duì)象的序列化和反序列化過(guò)程:

// 序列化過(guò)程:  
User user = new User(0,"jake",true);  
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));  
out.writeObject(user);  
out.close();  
  
// 反序列化過(guò)程:  
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));  
User newUser = (User)in.readObject();  
in.close();

注意點(diǎn)一:序列化和反序列化的對(duì)象并不是同一個(gè)對(duì)象嫂粟!
(3)一般來(lái)說(shuō),我們應(yīng)該收到指定serialVersionUID的值墨缘,比如1L星虹,也可以讓Eclipse根據(jù)當(dāng)前類(lèi)的結(jié)構(gòu)自動(dòng)去生成它的hash值。當(dāng)我們手動(dòng)指定了它以后镊讼,就可以在很大程度上避免反序列化過(guò)程的失敗宽涌。

注意點(diǎn)二:靜態(tài)成員變量屬于類(lèi)不屬于對(duì)象,所以不會(huì)參與序列化過(guò)程蝶棋。

注意點(diǎn)三:用transient關(guān)鍵字標(biāo)記的成員變量不參與序列化過(guò)程护糖。

2、Parcelable接口

(1)Parcelable也是一個(gè)接口嚼松,只要實(shí)現(xiàn)這個(gè)接口嫡良,一個(gè)類(lèi)的對(duì)象就可以實(shí)現(xiàn)序列化并可以通過(guò)Intent和Binder傳遞。

public class User implements Parcelable {  
  
    public int userId;  
    public String userName;  
    public boolean isMale;  
      
    public Book book;  
  
    public User(int userId, String userName, boolean isMale) {  
        this.userId = userId;  
        this.userName = userName;  
        this.isMale = isMale;  
    }  
      
    /* 
     * 內(nèi)容描述功能幾乎都是直接返回0的献酗。 
     * */  
    public int describeContents() {  
        return 0;  
    }  
  
    /* 
     * 序列化由writeToParcel方法來(lái)完成寝受,最終是通過(guò)Parcel中一系列write方法來(lái)完成的侦副。 
     * 其中flags標(biāo)識(shí)有兩種值:0和1(PARCELABLE_WRITE_RETURN_VALUE)铣揉。 
     * 為1時(shí)標(biāo)識(shí)當(dāng)前對(duì)象需要作為返回值返回降淮,不能立即釋放資源经伙, 
     * 幾乎所有情況都為0帆喇。 
     * */  
    public void writeToParcel(Parcel out, int flags) {  
        out.writeInt(userId);  
        out.writeString(userName);  
        out.writeInt(isMale? 1:0);  
        out.writeParcelable(book, 0);  
    }  
  
    /* 
     * 反序列化功能是由CREATOR來(lái)完成,其內(nèi)部標(biāo)明了如何創(chuàng)建序列化對(duì)象和數(shù)組会宪, 
     * 并通過(guò)Parcel的一些了read方法來(lái)完成反序列化過(guò)程赏参。 
     * */  
    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {  
        // 從序列化后的對(duì)象中創(chuàng)建原始對(duì)象。  
        public User createFromParcel(Parcel in) {  
            return new User(in);  
        }  
  
        // 創(chuàng)建指定長(zhǎng)度的原始對(duì)象數(shù)組  
        public User[] newArray(int size) {  
            return new User[size];  
        }  
    };  
  
    /* 
     * Parcel內(nèi)部包裝了可序列化的數(shù)據(jù)讯蒲,可以在Binder中自由傳輸痊土。 
     * 從序列化后的對(duì)象中創(chuàng)建原始對(duì)象。 
     * */  
    private User(Parcel in) {  
        userId = in.readInt();  
        userName = in.readString();  
        isMale = in.readInt() == 1;  
        /* 
         * 由于book是另一個(gè)可序列化對(duì)象墨林,所以它的反序列化過(guò)程需要傳遞當(dāng)前線(xiàn)程的上下文類(lèi)加載器赁酝, 
         * 否則會(huì)報(bào)無(wú)法找到類(lèi)的錯(cuò)誤。 
         * */  
        book = in.readParcelable(Thread.currentThread().getContextClassLoader());  
    }  
}  

(2)系統(tǒng)已經(jīng)為我們提供了許多實(shí)現(xiàn)了Parcelable接口的類(lèi)旭等,它們都是可以直接序列化的酌呆,比如Intent、Bundle搔耕、Bitmap等隙袁,同時(shí)List和Map也可以序列化,前提是它們里面的每個(gè)元素都是可序列化的弃榨。

3菩收、Parcelable接口和Serializable接口的比較

(1)Serializable用起來(lái)簡(jiǎn)單,但開(kāi)銷(xiāo)很大惭墓,序列化和反序列化過(guò)程都需要大量的I/O操作坛梁。

(2)Parcelable是Android中的序列化方式,更適合在Android平臺(tái)上使用腊凶,用起來(lái)比較麻煩划咐,效率很高,首選钧萍。主要用在內(nèi)存序列化上褐缠。

四、Binder

1风瘦、Binder簡(jiǎn)介

(1)Binder實(shí)現(xiàn)了IBinder接口队魏。

(2)從IPC角度來(lái)說(shuō),Binder是Android中的一種跨進(jìn)程通信方式万搔。Binder還可以理解為一種虛擬的物理設(shè)備胡桨,它的設(shè)備驅(qū)動(dòng)是/dev/binder,這種通信方式在Linux中沒(méi)有瞬雹。

(3)從Android Framework角度來(lái)說(shuō)昧谊,Binder是ServiceManager連接各種Manager(ActivityManager、WindowManager酗捌,等等)和相應(yīng)ManagerService的橋梁呢诬。

(4)從Android應(yīng)用層來(lái)說(shuō)涌哲,Binder是客戶(hù)端和服務(wù)端進(jìn)行通信的媒介,當(dāng)bindService的時(shí)候尚镰,服務(wù)端會(huì)返回一個(gè)包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對(duì)象阀圾,通過(guò)這個(gè)對(duì)象,客戶(hù)端就可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù)狗唉,這里的服務(wù)包括普通服務(wù)和基于AIDL的服務(wù)初烘。

(5)AIDL即Android interface definition Language,即Android接口定義語(yǔ)言敞曹。

2账月、在分析Binder的工作原理之前综膀,我們先補(bǔ)充一下Android設(shè)計(jì)模式之Proxy模式

(1)Proxy代理模式簡(jiǎn)介

代理模式是對(duì)象的結(jié)構(gòu)模式澳迫。代理模式給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)原對(duì)象的引用剧劝。

模式的使用場(chǎng)景:就是一個(gè)人或者機(jī)構(gòu)代表另一個(gè)人或者機(jī)構(gòu)采取行動(dòng)橄登。在一些情況下,一個(gè)客戶(hù)不想或者不能夠直接引用一個(gè)對(duì)象讥此,而代理對(duì)象可以在客戶(hù)端和目標(biāo)對(duì)象之間起到中介的作用拢锹。

image
  • 抽象對(duì)象角色AbstarctObject:聲明了目標(biāo)對(duì)象和代理對(duì)象的共同接口,這樣一來(lái)在任何可以使用目標(biāo)對(duì)象的地方都可以使用代理對(duì)象萄喳。

  • 目標(biāo)對(duì)象角色RealObject:定義了代理對(duì)象所代表的目標(biāo)對(duì)象卒稳。

  • 代理對(duì)象角色ProxyObject:代理對(duì)象內(nèi)部含有目標(biāo)對(duì)象的引用,從而可以在任何時(shí)候操作目標(biāo)對(duì)象他巨;代理對(duì)象提供一個(gè)與目標(biāo)對(duì)象相同的接口充坑,以便可以在任何時(shí)候替代目標(biāo)對(duì)象。代理對(duì)象通常在客戶(hù)端調(diào)用傳遞給目標(biāo)對(duì)象之前或之后染突,執(zhí)行某個(gè)操作捻爷,而不是單純地將調(diào)用傳遞給目標(biāo)對(duì)象。

(2)Proxy代理模式的簡(jiǎn)單實(shí)現(xiàn)

抽象對(duì)象角色:

public abstract class AbstractObject {  
    //操作  
    public abstract void operation();  
} 

目標(biāo)對(duì)象角色:

public class RealObject extends AbstractObject {  
    @Override  
    public void operation() {  
        //一些操作  
        System.out.println("一些操作");  
    }  
} 

代理對(duì)象角色:

public class ProxyObject extends AbstractObject{  
    RealObject realObject = new RealObject();//目標(biāo)對(duì)象角色  
    @Override  
    public void operation() {  
        //調(diào)用目標(biāo)對(duì)象之前可以做相關(guān)操作  
        System.out.println("before");          
        realObject.operation();        //目標(biāo)對(duì)象角色的操作函數(shù)  
        //調(diào)用目標(biāo)對(duì)象之后可以做相關(guān)操作  
        System.out.println("after");  
    }  
}

客戶(hù)端:

public class Client {  
    public static void main(String[] args) {  
        AbstractObject obj = new ProxyObject();  
        obj.operation();  
    }  
} 

(3)代理模式在Binder中的使用

直觀(guān)來(lái)說(shuō)份企,Binder是Android中的一個(gè)類(lèi)也榄,它繼承了IBinder接口。從IPC角度來(lái)說(shuō)司志,Binder是Android中的一種跨進(jìn)程通信方式甜紫,Binder還可以理解為一種虛擬的物理設(shè)備,它的設(shè)備驅(qū)動(dòng)是/dev/binder骂远,該通信方式在linux中沒(méi)有囚霸;從Android Framework角度來(lái)說(shuō),Binder是ServiceManager連接各種Manager(ActivityManager吧史、WindowManager邮辽,etc)和相應(yīng)ManagerService的橋梁唠雕;從Android應(yīng)用層來(lái)說(shuō),Binder是客戶(hù)端和服務(wù)端進(jìn)行通信的媒介吨述,當(dāng)你bindService的時(shí)候岩睁,服務(wù)端會(huì)返回一個(gè)包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對(duì)象,通過(guò)這個(gè)Binder對(duì)象揣云,客戶(hù)端就可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù)捕儒,這里的服務(wù)包括普通服務(wù)和基于AIDL的服務(wù)。

Binder一個(gè)很重要的作用是:將客戶(hù)端的請(qǐng)求參數(shù)通過(guò)Parcel包裝后傳到遠(yuǎn)程服務(wù)端邓夕,遠(yuǎn)程服務(wù)端解析數(shù)據(jù)并執(zhí)行對(duì)應(yīng)的操作刘莹,同時(shí)客戶(hù)端線(xiàn)程掛起,當(dāng)服務(wù)端方法執(zhí)行完畢后焚刚,再將返回結(jié)果寫(xiě)入到另外一個(gè)Parcel中并將其通過(guò)Binder傳回到客戶(hù)端点弯,客戶(hù)端接收到返回?cái)?shù)據(jù)的Parcel后,Binder會(huì)解析數(shù)據(jù)包中的內(nèi)容并將原始結(jié)果返回給客戶(hù)端矿咕,至此抢肛,整個(gè)Binder的工作過(guò)程就完成了。由此可見(jiàn)碳柱,Binder更像一個(gè)數(shù)據(jù)通道捡絮,Parcel對(duì)象就在這個(gè)通道中跨進(jìn)程傳輸,至于雙方如何通信莲镣,這并不負(fù)責(zé)福稳,只需要雙方按照約定好的規(guī)范去打包和解包數(shù)據(jù)即可。

為了更好地說(shuō)明Binder瑞侮,這里我們先手動(dòng)實(shí)現(xiàn)了一個(gè)Binder的圆。為了使得邏輯更清晰,這里簡(jiǎn)化一下区岗,我們來(lái)模擬一個(gè)銀行系統(tǒng)略板,這個(gè)銀行提供的功能只有一個(gè):即查詢(xún)余額,只有傳遞一個(gè)int的id過(guò)來(lái)慈缔,銀行就會(huì)將你的余額設(shè)置為id*10叮称,滿(mǎn)足下大家的發(fā)財(cái)夢(mèng)。

1)先定義一個(gè)Binder接口(抽象對(duì)象角色):

package com.ryg.design.manualbinder;  
  
  
import android.os.IBinder;  
import android.os.IInterface;  
import android.os.RemoteException;  
  
  
public interface IBank extends IInterface {  
  
  
/* 
 * Binder的唯一標(biāo)識(shí)符: 
 * */  
    static final String DESCRIPTOR = "com.ryg.design.manualbinder.IBank";  
  
  
    /* 
     * queryMoney方法的code標(biāo)識(shí): 
     * */  
    static final int TRANSACTION_queryMoney = (IBinder.FIRST_CALL_TRANSACTION + 0);  
  
  
    /* 
     * queryMoney方法聲明: 
     * */  
    public long queryMoney(int uid) throws RemoteException;  
}  

2)創(chuàng)建一個(gè)Binder并實(shí)現(xiàn)上述接口:

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);  
    }  
  
  
    /* 
     * 如果在同一進(jìn)程藐鹤,則返回目標(biāo)對(duì)象本身瓤檐, 
     * 如果在不同僅此,則返回代理類(lèi) 
     * */  
    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;  
    }  
  
  
    /* 
     * 這個(gè)onTransact方法是在目標(biāo)對(duì)象角色中重寫(xiě)的娱节, 
     * 在目標(biāo)對(duì)象角色調(diào)用Transact方法時(shí)回調(diào)的挠蛉! 
     * */  
    @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);  
    }  
  
  
    /* 
     * 這是正兒八經(jīng)的目標(biāo)對(duì)象角色的queryMoney函數(shù): 
     * */  
    @Override  
    public long queryMoney(int uid) throws RemoteException {  
        return uid * 10l;  
    }  
  
  
    /* 
     * 內(nèi)部代理類(lèi)(代理對(duì)象角色) 
     * */  
    private static class Proxy implements IBank {  
    /* 
     * 代表目標(biāo)對(duì)象角色: 
     * */  
        private IBinder mRemote;  
  
  
        /* 
         * 構(gòu)造函數(shù): 
         * */  
        Proxy(IBinder remote) {  
        >// 接收目標(biāo)對(duì)象角色:  
            mRemote = remote;  
        }  
  
  
        /* 
         * 返回目標(biāo)對(duì)象角色: 
         * */  
        @Override  
        public IBinder asBinder() {  
            return mRemote;  
        }  
  
  
        /* 
         * 返回Binder唯一標(biāo)識(shí)符: 
         * */  
        public java.lang.String getInterfaceDescriptor() {  
            return DESCRIPTOR;  
        }  
  
  
        /* 
         * 這是代理類(lèi)Proxy中的queryMoney方法: 
         * */  
        @Override  
        public long queryMoney(int uid) throws RemoteException {  
        /* 
         * 先創(chuàng)建兩個(gè)Parcel對(duì)象 
         * */  
            Parcel data = Parcel.obtain();  
            Parcel reply = Parcel.obtain();  
            long result;  
            try {  
            *  
             * 在操作前向data中寫(xiě)入一些數(shù)據(jù):  
             * */  
                data.writeInterfaceToken(DESCRIPTOR);  
                data.writeInt(uid);  
                /* 
                 * 這里執(zhí)行的mRemote.transact其實(shí)是目標(biāo)對(duì)象角色的transact函數(shù)。 
                 * 因?yàn)閙Remote是IBinder對(duì)象肄满,所以調(diào)用它的transact函數(shù)會(huì)回調(diào)它的onTransact方法谴古, 
                 * 這個(gè)onTransact方法是在mRemote這個(gè)目標(biāo)對(duì)象角色中重寫(xiě)了的质涛,哈 
                 * 然后要根據(jù)TRANSACTION_queryMoney的code代碼來(lái)執(zhí)行相應(yīng)的函數(shù)。 
                 * data負(fù)責(zé)傳遞信息掰担, 
                 * reply負(fù)責(zé)回收信息汇陆。 
                 * */  
                mRemote.transact(TRANSACTION_queryMoney, data, reply, 0);  
                /* 
                 * 這里是返回的數(shù)據(jù)。 
                 * */  
                reply.readException();  
                result = reply.readLong();  
            } finally {  
                reply.recycle();  
                data.recycle();  
            }  
            return result;  
        }  
    }  
}  

ok带饱,到此為止毡代,我們的Binder就完成了,這里只要?jiǎng)?chuàng)建服務(wù)端和客戶(hù)端勺疼,二者就能通過(guò)我們的Binder來(lái)通信了教寂。這里就不做這個(gè)示例了,我們的目的是分析代理模式在Binder中的使用执庐。

我們看上述Binder的實(shí)現(xiàn)中酪耕,有一個(gè)叫做“Proxy”的類(lèi),它的構(gòu)造方法如下:

Proxy(IBinder remote) {  
    mRemote = remote;  
} 

Proxy類(lèi)接收一個(gè)IBinder參數(shù)耕肩,這個(gè)參數(shù)實(shí)際上就是服務(wù)端Service中的onBind方法返回的Binder對(duì)象在客戶(hù)端重新打包后的結(jié)果因妇,因?yàn)榭蛻?hù)端無(wú)法直接通過(guò)這個(gè)打包的Binder和服務(wù)端通信问潭,因此客戶(hù)端必須借助Proxy類(lèi)來(lái)和服務(wù)端通信猿诸,這里Proxy的作用就是代理的作用,客戶(hù)端所有的請(qǐng)求全部通過(guò)Proxy來(lái)代理狡忙,具體工作流程為:Proxy接收到客戶(hù)端的請(qǐng)求后梳虽,會(huì)將客戶(hù)端的請(qǐng)求參數(shù)打包到Parcel對(duì)象中,然后將Parcel對(duì)象通過(guò)它內(nèi)部持有的Ibinder對(duì)象傳送到服務(wù)端灾茁,服務(wù)端接收數(shù)據(jù)窜觉、執(zhí)行方法后返回結(jié)果給客戶(hù)端的Proxy,Proxy解析數(shù)據(jù)后返回給客戶(hù)端的真正調(diào)用者北专。很顯然禀挫,上述所分析的就是典型的代理模式。至于Binder如何傳輸數(shù)據(jù)拓颓,這涉及到很底層的知識(shí)语婴,這個(gè)很難搞懂,但是數(shù)據(jù)傳輸?shù)暮诵乃枷胧枪蚕韮?nèi)存驶睦。

3砰左、我們通過(guò)一個(gè)案例來(lái)分析Binder工作原理

我們需要新建一個(gè)AIDL示例,SDK會(huì)自動(dòng)為我們生產(chǎn)AIDL所對(duì)應(yīng)的Binder類(lèi)场航。

(1)Book.java:這里面沒(méi)有什么特殊之處缠导,為了實(shí)現(xiàn)Parcelable,添加了幾個(gè)方法溉痢,上面在Parcelable部分已經(jīng)介紹過(guò)了僻造。

package com.ryg.chapter_2.aidl;  
  
  
import android.os.Parcel;  
import android.os.Parcelable;  
  
  
/* 
 * (1)它是一個(gè)表示圖示信息的類(lèi)憋他, 
 * 它實(shí)現(xiàn)了Parcelable接口,因?yàn)閷?shí)現(xiàn)了Parcelable接口便可以進(jìn)行序列化 
 * (2)Book.aidl是Book類(lèi)在ADIL中的聲明髓削。 
 * (3)IBookManager.aidl是我們定義的一個(gè)接口举瑰,里面有兩個(gè)方法:getBookList和addBook, 
 * 其中g(shù)etBookList用于從遠(yuǎn)程服務(wù)端獲取圖書(shū)列表蔬螟,而addBook用于往圖書(shū)列表中添加一本書(shū)此迅, 
 * 當(dāng)然這兩個(gè)方法主要是示例用,不一定要有實(shí)際意義旧巾。 
 * (4)盡管Book類(lèi)和IBookManager位于相同的包中耸序,但是在IBookManager中仍然要導(dǎo)入Book類(lèi), 
 * 這就是AIDL的特殊之處鲁猩。 
 * */  
public class Book implements Parcelable {      
<span style="white-space:pre">    </span>public int bookId;  
    public String bookName;      
      
    /* 
     * 普通構(gòu)造函數(shù): 
     * */  
    public Book() {      
    <span style="white-space:pre">  </span>  
    }      
      
    /* 
     * 普通構(gòu)造函數(shù): 
     * */  
    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);  
    }      
      
    /* 
     * 反序列化坎怪, 
     * 這個(gè)creator就是通過(guò)一個(gè)Parcle來(lái)創(chuàng)建一個(gè)book對(duì)象或者數(shù)組。 
     * */  
    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];  
        }  
    };      
      
    /* 
     * 用于反序列化的構(gòu)造函數(shù): 
     * */  
    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類(lèi)已經(jīng)和IBookManager位于相同的包中搅窿,但是這里依然需要導(dǎo)入Book類(lèi)。這是AIDL的特殊之處隙券。

它是一個(gè)接口男应,里面有四個(gè)方法。

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)下面我們要看一下系統(tǒng)為IBookManager.aidl生產(chǎn)的Binder類(lèi)娱仔,在gen目錄下有一個(gè)IBookManager.java的類(lèi)沐飘,這就是我們要找的類(lèi)。

/* 
 * This file is auto-generated.  DO NOT MODIFY. 
 */  
package com.ryg.chapter_2.aidl;  
/* 
 * IBookManager它繼承了IInterface這個(gè)接口牲迫,同時(shí)它自己也還是個(gè)接口耐朴, 
 * 所有可以在Binder中傳輸?shù)慕涌诙家^承IInterface接口。 
 * 首先盹憎,它聲明了兩個(gè)方法getBookList和addBook筛峭,顯然這就是我們?cè)贗BookManager.aidl中所聲明的方法, 
 * 同時(shí)它還聲明了兩個(gè)整型的id分別用于標(biāo)識(shí)這兩個(gè)方法陪每。 
 * 接著影晓,它聲明了一個(gè)內(nèi)部類(lèi)Stub,這個(gè)Stub就是一個(gè)Binder類(lèi)奶稠, 
 * 當(dāng)客戶(hù)端和服務(wù)端都位于同一個(gè)進(jìn)程時(shí)俯艰,方法調(diào)用不會(huì)走跨進(jìn)程的transact過(guò)程, 
 * 而當(dāng)兩者位于不同進(jìn)程時(shí)锌订,方法調(diào)用需要走transact過(guò)程竹握, 
 * 這個(gè)邏輯由Stub的內(nèi)部代理類(lèi)Proxy來(lái)完成。 
 * */  
public interface IBookManager extends android.os.IInterface  
{  
 /** Local-side IPC implementation stub class. */  
 /* 
  * 首先這個(gè)Stub辆飘,它是一個(gè)內(nèi)部類(lèi)啦辐,它繼承了Binder谓传,所以它是一個(gè)Binder, 
  * 同時(shí)Stub還實(shí)現(xiàn)了IBookManager中的方法芹关。 
  * */  
 public static abstract class Stub extends android.os.Binder implements com.ryg.chapter_2.aidl.IBookManager  
 {  
  /* 
   * Binder的唯一標(biāo)識(shí)符续挟。 
   * */  
  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. 
   */  
  /* 
   * 用于將服務(wù)端的Binder對(duì)象轉(zhuǎn)換成客戶(hù)端所需的AIDL接口類(lèi)型的對(duì)象, 
   * 這種轉(zhuǎn)換過(guò)程是區(qū)分進(jìn)程的侥衬, 
   * 如果客戶(hù)端和服務(wù)端位于同一進(jìn)程诗祸,那么此方法返回的就是服務(wù)端的Stub對(duì)象本身, 
   * 否則返回的是系統(tǒng)封裝后的Stub.proxy代理對(duì)象轴总。 
   * */  
  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);  
   // 同一進(jìn)程  
   if (((iin!=null)&&(iin instanceof com.ryg.chapter_2.aidl.IBookManager))) {  
    return ((com.ryg.chapter_2.aidl.IBookManager)iin);  
   }  
   // 不同進(jìn)程  
   return new com.ryg.chapter_2.aidl.IBookManager.Stub.Proxy(obj);  
  }  
    
  /* 
   * 此方法用于返回當(dāng)前Binder對(duì)象直颅,也就是內(nèi)部類(lèi)Stub。 
   * */  
  @Override public android.os.IBinder asBinder()  
  {  
   return this;  
  }  
   
  /* 
   * 這個(gè)方法運(yùn)行在服務(wù)端中的Binder線(xiàn)程池中怀樟, 
   * 當(dāng)客戶(hù)端發(fā)起跨進(jìn)程請(qǐng)求時(shí)功偿,遠(yuǎn)程請(qǐng)求會(huì)通過(guò)系統(tǒng)底層封裝后交由此方法來(lái)處理。 
   * 服務(wù)端通過(guò)code可以確定客戶(hù)端所請(qǐng)求的目標(biāo)方法是什么往堡, 
   * 接著從data中取出目標(biāo)方法所需的參數(shù)械荷, 
   * 然后執(zhí)行目標(biāo)方法。 
   * 當(dāng)目標(biāo)方法執(zhí)行完畢后虑灰,就向reply中寫(xiě)入返回值吨瞎。 
   * 如果此方法返回false,那么客戶(hù)端的請(qǐng)求會(huì)失敗瘩缆,因此我們可以利用這個(gè)特性來(lái)做權(quán)限驗(yàn)證码邻。 
   * */  
  @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);  
     /* 
      * 這句才是調(diào)用了真正的執(zhí)行過(guò)程呢 
      * */  
     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;  
     }  
     /* 
      * 這句才是調(diào)用了真正的執(zhí)行過(guò)程呢 
      * */  
     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);  
  }  
    
  /* 
   * 代理類(lèi)Proxy骑丸。 
   * */  
  private static class Proxy implements com.ryg.chapter_2.aidl.IBookManager  
  {  
   /* 
    * 這個(gè)mRemote代表的就是目標(biāo)對(duì)象角色, 
    * */  
   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;  
   }  
     
   /* 
    * 這個(gè)方法運(yùn)行在客戶(hù)端畦娄, 
    * 因?yàn)楫?dāng)客戶(hù)端和服務(wù)端不在同一進(jìn)程時(shí)谐算,服務(wù)端返回代理類(lèi)Proxy熟尉,所以客戶(hù)端會(huì)通過(guò)Proxy調(diào)用到代理類(lèi)的getBookList方法, 
    * 當(dāng)客戶(hù)端遠(yuǎn)程調(diào)用此方法時(shí)洲脂,它的內(nèi)部實(shí)現(xiàn)是這樣的: 
    * 首先創(chuàng)建該方法所需要的輸入型Parcel對(duì)象_data斤儿、輸出型Parcel對(duì)象_reply和返回值對(duì)象List, 
    * 然后把該方法的參數(shù)信息寫(xiě)入_data中恐锦, 
    * 接著調(diào)用transact方法來(lái)發(fā)起RPC(遠(yuǎn)程過(guò)程調(diào)用)請(qǐng)求往果,同時(shí)當(dāng)前線(xiàn)程掛起, 
    * 然后服務(wù)端的onTransact方法會(huì)被調(diào)用一铅,直到RPC過(guò)程返回后陕贮,當(dāng)前線(xiàn)程繼續(xù)執(zhí)行, 
    * 并從_reply中取出RPC過(guò)程的返回結(jié)果潘飘。 
    * 最后返回_reply中的數(shù)據(jù)肮之。 
    * */  
   @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();  
    }  
   }  
  }  
    
  /* 
   * 用于標(biāo)識(shí)方法的整型id掉缺。 
   * 它們用于在transact過(guò)程總客戶(hù)端所請(qǐng)求的到底是哪個(gè)方法。 
   * */  
  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中所聲明的方法戈擒。 
  * 這里才是真正的方法聲明眶明。具體實(shí)現(xiàn)我們?nèi)匀粵](méi)有看到呢。 
  * */  
 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;  
}  

注意點(diǎn)一:上面的Book類(lèi)筐高,就是一個(gè)可以Parcelable序列化的簡(jiǎn)單的Book類(lèi)搜囱,它里面沒(méi)有任何的方法,就是定義了一個(gè)簡(jiǎn)單的Book類(lèi)結(jié)構(gòu)柑土。

注意點(diǎn)二:Book.aidl的存在是因?yàn)樵贗BookManager.aidl中出現(xiàn)的對(duì)象也必須有aidl聲明犬辰。

注意點(diǎn)三:在IBookManager.aidl中,對(duì)于自動(dòng)生成的IBookManager.java文件冰单,它是服務(wù)器端的代碼幌缝。當(dāng)客戶(hù)端向服務(wù)端發(fā)送連接請(qǐng)求時(shí),如果客戶(hù)端和服務(wù)端在同一進(jìn)程中诫欠,那么服務(wù)端就向客戶(hù)端返回Stub這個(gè)Binder對(duì)象涵卵,如果客戶(hù)端和服務(wù)端在不同進(jìn)程中,那么服務(wù)端就向客戶(hù)端返回內(nèi)部類(lèi)Stub的內(nèi)部代理類(lèi)Proxy荒叼,然后客戶(hù)端根據(jù)這個(gè)Proxy來(lái)調(diào)用Proxy內(nèi)部的方法轿偎,這個(gè)Proxy內(nèi)部含有服務(wù)端真正的Binder對(duì)象也就是那個(gè)內(nèi)部類(lèi)Stub,在客戶(hù)端調(diào)用Proxy內(nèi)部的方法也就會(huì)導(dǎo)致調(diào)用Stub的transact方法被廓,而Stub的transact方法又會(huì)回調(diào)它自己的onTransact方法坏晦,onTransact方法是在服務(wù)端運(yùn)行的,而transact方法是在客戶(hù)端調(diào)用的嫁乘,這樣就實(shí)現(xiàn)了客戶(hù)端調(diào)用服務(wù)端的方法了昆婿。當(dāng)然這所有的傳遞過(guò)程也少不了Parcel這個(gè)數(shù)據(jù)包的協(xié)助。整個(gè)過(guò)程懂了嗎蜓斧?

image

這次應(yīng)該完全懂了吧仓蛆,再不懂去屎吧!

4挎春、linkToDeath和unlinkToDeath

Binder運(yùn)行在服務(wù)端進(jìn)程看疙,如果服務(wù)端進(jìn)程由于某些原因異常終止,這個(gè)時(shí)候我們到服務(wù)端的Binder連接斷裂直奋,會(huì)導(dǎo)致我們的遠(yuǎn)程調(diào)用失敗能庆。Binder提供了兩個(gè)配對(duì)的方法linkToDeath和unlinkToDeath,通過(guò)linkToDeath我們可以給Binder設(shè)置一個(gè)死亡代理脚线,當(dāng)Binder死亡時(shí)搁胆,我們會(huì)收到通知,這個(gè)時(shí)候我們就可以重新發(fā)起連接請(qǐng)求從而恢復(fù)連接。

/* 
 * 聲明這個(gè)接口就好: 
 * */  
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DearhRecipient(){  
    // 只需要重寫(xiě)這個(gè)方法就可以了丰涉。  
    @Override   
    public void binderDied(){  
        if(mBookManager == null)  
            return;  
        mBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);  
        mBookManager = null;  
        // TODO: 這里重新綁定遠(yuǎn)程Service拓巧。  
    }  
}

在客戶(hù)端綁定遠(yuǎn)程服務(wù)之后,給Binder設(shè)置死亡代理:

mService = IMessageBoxManager.Stub.asInterface(binder);  
binder.linkToDeath(mDeathRecipient, 0); 

五一死、使用Messenger

1肛度、特點(diǎn):

(1)Messenger對(duì)AIDL做了封裝,使得我們可以更簡(jiǎn)便地進(jìn)行進(jìn)程間通信投慈。由于它一次處理一個(gè)請(qǐng)求承耿,所以在服務(wù)端我們不考慮線(xiàn)程同步的問(wèn)題,因?yàn)榉?wù)端中不存在并發(fā)執(zhí)行的情形伪煤。
(2)通過(guò)它可以在不同進(jìn)程中傳遞Message對(duì)象加袋,在Message中仿佛我們需要傳遞的數(shù)據(jù),就可以輕松地實(shí)現(xiàn)數(shù)據(jù)的進(jìn)程間傳遞了抱既。
(3)有兩個(gè)構(gòu)造函數(shù)职烧,分別接收Handler對(duì)象和IBinder對(duì)象。

2防泵、 實(shí)現(xiàn)一個(gè)Messenger有如下步驟:

(1)服務(wù)端進(jìn)程:
首先需要在服務(wù)端創(chuàng)建一個(gè)Service來(lái)處理客戶(hù)端的連接請(qǐng)求蚀之,同時(shí)創(chuàng)建一個(gè)Handler并以它作為參數(shù)來(lái)創(chuàng)建一個(gè)Messenger對(duì)象,然后在Service的onBind中返回這個(gè)Messenger對(duì)象底層的Binder即可捷泞。關(guān)鍵點(diǎn)就在于它的返回是返回給了要綁定這個(gè)服務(wù)端的客戶(hù)端足删,然后客戶(hù)端拿到這個(gè)Binder再去創(chuàng)建Messenger,再去發(fā)送Message等等锁右。
(2)客戶(hù)端進(jìn)程:
客戶(hù)端進(jìn)程中失受,首先要綁定服務(wù)端的Service,綁定后服務(wù)端的onBind會(huì)返回一個(gè)Binder對(duì)象咏瑟,然后客戶(hù)端用服務(wù)端返回的這個(gè)Binder對(duì)象創(chuàng)建一個(gè)Messenger拂到,通過(guò)這個(gè)Messenger就可以向服務(wù)器端發(fā)送消息了,發(fā)送消息類(lèi)型為Message對(duì)象响蕴,如果需要服務(wù)端能夠回應(yīng)客戶(hù)端谆焊,就像和服務(wù)端一個(gè),我們還需要?jiǎng)?chuàng)建一個(gè)Handler并創(chuàng)建一個(gè)新的Messenger浦夷,并把這個(gè)Messenger對(duì)象在第一次客戶(hù)端像服務(wù)端發(fā)送消息時(shí)通過(guò)Message的replyTo參數(shù)傳遞給服務(wù)端,服務(wù)端通過(guò)讀取Message中的replyTo參數(shù)就是服務(wù)端給客戶(hù)端的的Messenger辜王,然后就可以回應(yīng)客戶(hù)端劈狐。
(3)注意點(diǎn):
客戶(hù)端給服務(wù)端發(fā)送消息的時(shí)候所用的Messenger是通過(guò)綁定服務(wù)端,然后依據(jù)onBind返回的Binder對(duì)象為參數(shù)來(lái)創(chuàng)建Messenger呐馆,而服務(wù)端在回應(yīng)客戶(hù)端的時(shí)候所用的Messenger是客戶(hù)端在剛剛發(fā)送消息的時(shí)候?qū)⒆陨韯?chuàng)建的Messenger作為剛剛發(fā)送消息的Message的replyTo參數(shù)傳遞給服務(wù)端的肥缔,所以在服務(wù)端直接讀取出這個(gè)Messenger。

3汹来、舉例:客戶(hù)端像服務(wù)端發(fā)送消息续膳,服務(wù)端回應(yīng)客戶(hù)端

(1)先看服務(wù)端代碼:

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;  
  
  
/* 
 * 首先改艇,這是一個(gè)服務(wù)。 
 * 其次坟岔,這個(gè)服務(wù)是需要注冊(cè)的谒兄,并且要給它另起一個(gè)進(jìn)程。 
 * <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用來(lái)處理客戶(hù)端發(fā)送的消息承疲, 
     * 并從消息中取出客戶(hù)端發(fā)來(lái)的文本信息。 
     * */  
    private static class MessengerHandler extends Handler {  
        @Override  
        public void handleMessage(Message msg) {  
            switch (msg.what) {  
            /* 
             * MyConstants是我們這個(gè)應(yīng)用中的一個(gè)類(lèi)鸥咖,其中包含了幾個(gè)變量的聲明: 
             *     public static final int MSG_FROM_CLIENT = 0; 
                   public static final int MSG_FROM_SERVICE = 1; 
             * */  
            case MyConstants.MSG_FROM_CLIENT:  
            /* 
             * 這一條語(yǔ)句是在處理從客戶(hù)端發(fā)來(lái)的消息燕鸽,用Log日志打印出來(lái): 
             * */  
                Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));  
                /* 
                 * 這下面的語(yǔ)句是用來(lái)響應(yīng)客戶(hù)端,給客戶(hù)端回饋消息的啼辣。 
                 * (1)第一步是通過(guò)replyTo來(lái)獲取客戶(hù)端的Messenger對(duì)象啊研。 
                 * (2)第二步是創(chuàng)建一個(gè)Message消息, 
                 * Message.obtain這個(gè)方法的第一個(gè)參數(shù)是Handler鸥拧,第二個(gè)參數(shù)是消息的what字段党远。 
                 * (3)第三步創(chuàng)建一個(gè)Bundle對(duì)象,然后向這個(gè)對(duì)象中添加String內(nèi)容住涉。 
                 * (4)第四步是將Bundle對(duì)象設(shè)置給Message麸锉。 
                 * (5)第五步是通過(guò)Messenger將Message發(fā)送出去, 
                 * 因?yàn)槲覀兊腗essenger是通過(guò)客戶(hù)端來(lái)獲取的舆声,而在客戶(hù)端那邊這個(gè)Messenger是以Handler為參數(shù)創(chuàng)建的花沉, 
                 * 所以在服務(wù)端通過(guò)客戶(hù)端的Messenger發(fā)送消息后,在客戶(hù)端的Handler就會(huì)處理這條消息媳握,嘻嘻碱屁,就達(dá)到了消息傳送的目的。 
                 * */  
                Messenger client = msg.replyTo;  
                Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);  
                Bundle bundle = new Bundle();  
                bundle.putString("reply", "嗯蛾找,你的消息我已經(jīng)收到娩脾,稍后會(huì)回復(fù)你。");  
                relpyMessage.setData(bundle);  
                try {  
                    client.send(relpyMessage);  
                } catch (RemoteException e) {  
                    e.printStackTrace();  
                }  
                break;  
            default:  
                super.handleMessage(msg);  
            }  
        }  
    }  
  
  
    /* 
     * 這是我們服務(wù)端自己的Messenger打毛,它是以上面的Handler對(duì)象為參數(shù)創(chuàng)建的柿赊, 
     * 這個(gè)Messenger是要通過(guò)綁定該服務(wù)器的時(shí)候onBind方法傳遞給客戶(hù)端, 
     * 然后客戶(hù)端獲取了該Messenger幻枉,再以該Messenger來(lái)發(fā)送消息碰声, 
     * 這樣服務(wù)端就可以接收到該消息并處理。 
     * */  
    private final Messenger mMessenger = new Messenger(new MessengerHandler());  
  
  
    /* 
     * 這個(gè)方法是在綁定服務(wù)的過(guò)程中調(diào)用的并將結(jié)果返回給客戶(hù)端的熬甫, 
     * 所以通過(guò)onBind方法客戶(hù)端就可以獲取我們Messenger的Binder對(duì)象了胰挑, 
     * 然后客戶(hù)端可以根據(jù)該Binder對(duì)象來(lái)創(chuàng)建一個(gè)Messenger, 
     * 這樣客戶(hù)端中用的Messenger和這里的Messenger就是向?qū)?yīng)的了。 
     * */  
    @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)再看看客戶(hù)端代碼:

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;  
  
  
/* 
 * 客戶(hù)端瞻颂,首先它是一個(gè)活動(dòng)豺谈。 
 * 其次它也需要注冊(cè)的。 
 * */  
public class MessengerActivity extends Activity {  
  
  
    private static final String TAG = "MessengerActivity";  
  
  
    // 用來(lái)獲取服務(wù)端的Messenger贡这,用來(lái)給服務(wù)端傳遞消息用的茬末。  
    private Messenger mService;  
    // 這是客戶(hù)端自己的Messenger,傳遞給服務(wù)端藕坯,讓服務(wù)端返回消息用的团南。  
    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());  
      
    /* 
     * 這個(gè)Handler是用來(lái)處理服務(wù)端返回的消息的, 
     * 這個(gè)Handler將作為一個(gè)參數(shù)來(lái)創(chuàng)建自己的Messenger炼彪, 
     * 然后將這個(gè)Messenger傳遞給服務(wù)端吐根,讓服務(wù)端根據(jù)它返回消息。 
     * */  
    private static class MessengerHandler extends Handler {  
        @Override  
        public void handleMessage(Message msg) {  
            switch (msg.what) {  
            case MyConstants.MSG_FROM_SERVICE:  
            // 處理消息辐马,以L(fǎng)og日志顯示出來(lái)拷橘。  
                Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));  
                break;  
            default:  
                super.handleMessage(msg);  
            }  
        }  
    }  
  
  
    /* 
     * 這個(gè)是客戶(hù)端用來(lái)綁定服務(wù)端用的, 
     * 在綁定過(guò)程中會(huì)調(diào)用onServiceConnected喜爷, 
     * 它的第二個(gè)參數(shù)IBinder service冗疮,就是在服務(wù)端中onBind方法返回的結(jié)果, 
     * 這個(gè)結(jié)果是服務(wù)端的Messenger對(duì)象的Binder對(duì)象檩帐, 
     * 然后客戶(hù)端通過(guò)這個(gè)Binder對(duì)象就可以創(chuàng)建一個(gè)Messenger术幔, 
     * 所以就是在綁定服務(wù)的過(guò)程中將服務(wù)端的Messenger傳遞給了客戶(hù)端,建立起了兩者之間的橋梁 
     * */  
    private ServiceConnection mConnection = new ServiceConnection() {  
        public void onServiceConnected(ComponentName className, IBinder service) {  
            /* 
             * (1)第一步是根據(jù)服務(wù)端的IBinder service對(duì)象為參數(shù)創(chuàng)建Messenger湃密。 
             * (2)第二步是創(chuàng)建一個(gè)Message消息诅挑,其中第二個(gè)參數(shù)是msg的what字段。 
             * 這里有個(gè)重要的點(diǎn)就是設(shè)置msg的replyTo字段泛源,這個(gè)字段保存了客戶(hù)端自己的Messenger拔妥, 
             * 客戶(hù)端將自己的Messenger傳遞給服務(wù)端,然后方便服務(wù)端根據(jù)這個(gè)Messenger將反饋消息用同樣的方法傳遞回來(lái)达箍。 
             * (3)第三步是創(chuàng)建一個(gè)Bundle對(duì)象没龙,這個(gè)對(duì)象中添加了要返回的消息內(nèi)容。 
             * (4)第四步將Bundle對(duì)象賦給Message缎玫。 
             * (5)第五步用Messenger的send方法將消息發(fā)送出去硬纤。 
             * */  
            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);  
        /* 
         * 這個(gè)Intent的跳轉(zhuǎn)是需要服務(wù)端設(shè)置的: 
         * <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的時(shí)候,服務(wù)端會(huì)通過(guò)onBind方法返回一個(gè)包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對(duì)象赃磨, 
         * 通過(guò)這個(gè)對(duì)象咬摇,客戶(hù)端就可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù), 
         * 具體情況去下面的第二個(gè)參數(shù)mConnection中查看煞躬。 
         * */  
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
    }  
      
    @Override  
    protected void onDestroy() {  
        unbindService(mConnection);  
        super.onDestroy();  
    }  
}  

(3)看完了是不是覺(jué)得很簡(jiǎn)單呀,嘿嘿。

六恩沛、使用AIDL

1在扰、對(duì)比Messenger和AIDL:

上一節(jié)講的Messenger來(lái)進(jìn)行進(jìn)程間的通信,可以發(fā)現(xiàn)雷客,Messenger是以串行的方式處理客戶(hù)端發(fā)來(lái)的消息的芒珠,

如果大量的消息同時(shí)發(fā)送到服務(wù)端,服務(wù)端仍然只能一個(gè)個(gè)處理搅裙,如果有大量的并發(fā)請(qǐng)求皱卓,那么用Messenger就不太合適了。

而且Messenger的主要作用是為了傳遞消息部逮,很多時(shí)候我們可能需要跨進(jìn)程調(diào)用服務(wù)端的方法娜汁,這種情形用Messenger就無(wú)法實(shí)現(xiàn)了。

所以我們用AIDL來(lái)實(shí)現(xiàn)跨進(jìn)程的方法調(diào)用兄朋。

AIDL也是Messenger的底層實(shí)現(xiàn)掐禁,因此Messenger本質(zhì)上也是AIDL,只不過(guò)系統(tǒng)為我們做了封裝颅和,從而方便上層的調(diào)用而已傅事。

2、AIDL使用的基本思想:

(0)先來(lái)放一下我們的Book.java類(lèi)峡扩,它實(shí)現(xiàn)了Parcelable接口:

package com.ryg.chapter_2.aidl;  
  
import android.os.Parcel;  
import android.os.Parcelable;  
  
/* 
 * (1)它是一個(gè)表示圖示信息的類(lèi)蹭越, 
 * 它實(shí)現(xiàn)了Parcelable接口, 
 * (2)Book.aidl是Book類(lèi)在ADIL中的聲明教届。 
 * (3)IBookManager.aidl是我們定義的一個(gè)接口响鹃,里面有兩個(gè)方法:getBookList和addBook, 
 * 其中g(shù)etBookList用于從遠(yuǎn)程服務(wù)端獲取圖書(shū)列表巍佑,而addBook用于往圖書(shū)列表中添加一本書(shū)茴迁, 
 * 當(dāng)然這兩個(gè)方法主要是示例用,不一定要有實(shí)際意義萤衰。 
 * (4)盡管Book類(lèi)和IBookManager位于相同的包中堕义,但是在IBookManager中仍然要導(dǎo)入Book類(lèi), 
 * 這就是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)服務(wù)端:

服務(wù)端首先要?jiǎng)?chuàng)建一個(gè)Service用來(lái)監(jiān)聽(tīng)客戶(hù)端的連接請(qǐng)求倦卖,然后創(chuàng)建一個(gè)AIDL文件,將暴露給客戶(hù)端的接口在這個(gè)AIDL文件中聲明椿争,最后在Service中實(shí)現(xiàn)這個(gè)AIDL接口即可怕膛。

(2)客戶(hù)端:

客戶(hù)端所要做的事情就稍微簡(jiǎn)單一些,首先需要綁定服務(wù)端的Service秦踪,在綁定成功后褐捻,將服務(wù)端返回的Binder對(duì)象轉(zhuǎn)成AIDL接口所屬的類(lèi)型掸茅,接著就可以調(diào)用AIDL中的方法了。

(3)AIDL接口的創(chuàng)建:

首先看AIDL接口的創(chuàng)建柠逞,如下所示昧狮,我們創(chuàng)建一個(gè)后綴為AIDL的文件,在里面聲明了一個(gè)接口和兩個(gè)接口方法:

這個(gè)文件的名稱(chēng)是: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文件中逗鸣,并不是所有的數(shù)據(jù)類(lèi)型都是可以使用的,那么到底AIDL文件支持哪些類(lèi)型的數(shù)據(jù)呢绰精?

基本數(shù)據(jù)類(lèi)型(int撒璧、long、char笨使、boolean卿樱、double等);

String和CharSequence阱表;

List:只支持ArrayList殿如,里面每個(gè)元素都必須能夠被AIDL支持;

Map:只支持HashMap最爬,里面的每個(gè)元素都必須被AIDL支持涉馁,包括key和value;

Parcelable:所有實(shí)現(xiàn)了Parcelable接口的對(duì)象爱致;

AIDL:所有的AIDL接口本身也可以在AIDL文件中使用烤送。
(其中自定義的Parcelable對(duì)象和AIDL對(duì)象必須要顯示import進(jìn)來(lái),不管它們是否和當(dāng)前的AIDL文件位于同一個(gè)包內(nèi))

所以上面的IBookManager.aidl文件糠悯,里面用到了Book這個(gè)類(lèi)帮坚,這個(gè)類(lèi)實(shí)現(xiàn)了Parcelable接口并且和IBookManager位于同一個(gè)包中,但是遵守AIDL的規(guī)范互艾,我們?nèi)匀恍枰@式的import進(jìn)來(lái):import com.ryg.chapter_2.aidl.Book;

還有一個(gè)注意點(diǎn):

如果AIDL文件中用到了自定義的Parcelable對(duì)象试和,那么必須新建一個(gè)和它同名的AIDL文件,并在其中聲明它為Parcelable類(lèi)型纫普。

因?yàn)槲覀冊(cè)贗BookManager.aidl中用到了Book這個(gè)類(lèi)阅悍,所以我們必須要?jiǎng)?chuàng)建Book.aidl,然后里面添加的內(nèi)容如下:

package com.ryg.chapter_2.aidl;  
  
parcelable Book;

事實(shí)上昨稼,AIDL中每個(gè)實(shí)現(xiàn)了Parcelable接口的類(lèi)都需要按照上面那種方式去創(chuàng)建相應(yīng)的AIDL文件并聲明那個(gè)類(lèi)為Parcelable节视。

除此之外,AIDL中除了基本數(shù)據(jù)類(lèi)型假栓,其他類(lèi)型的參數(shù)必須標(biāo)上方向:in寻行、out、inout匾荆。

我們需要根據(jù)實(shí)際需要去指定參數(shù)類(lèi)型拌蜘,不能一概使用out或者inout膝藕,因?yàn)檫@在底層實(shí)現(xiàn)是有開(kāi)銷(xiāo)的荠商。

最后妥箕,AIDL接口中只支持方法萨惑,不支持聲明靜態(tài)常量,這一點(diǎn)區(qū)別于傳統(tǒng)的接口贞滨。

為了方便AIDL的開(kāi)發(fā),建議把所有和AIDL相關(guān)的類(lèi)和文件全部放入同一個(gè)包中拍棕,這樣方便我們直接復(fù)制整個(gè)包晓铆,不容易遺漏。

需要注意的是绰播,AIDL的包結(jié)構(gòu)在服務(wù)端和客戶(hù)端要保持一致骄噪,否則運(yùn)行會(huì)出錯(cuò)。因?yàn)榭蛻?hù)端需要反序列化服務(wù)端中和AIDL接口相關(guān)的所有類(lèi)蠢箩,如果類(lèi)的完整路徑不一樣的話(huà)链蕊,就無(wú)法成功反序列化,程序也就無(wú)法正常運(yùn)行谬泌。

(4)遠(yuǎn)程服務(wù)端Service的實(shí)現(xiàn):

上面講的是如何定義AIDL接口滔韵,下面講如何實(shí)現(xiàn)這個(gè)接口。

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;  
  
/* 
 * 這是一個(gè)服務(wù)端Service的典型實(shí)現(xiàn)掌实。 
 * 首先在onCreate中初始化添加兩本圖書(shū)陪蜻, 
 * 然后創(chuàng)建了一個(gè)Binder對(duì)象并在onBind方法中返回它。 
 * */  
public class BookManagerService extends Service {  
  
    private static final String TAG = "BMS";  
  
    /* 
     * 注意這里采用了CopyOnWriteArrayList贱鼻,這個(gè)CopyOnWriteArrayList支持并發(fā)讀/寫(xiě)宴卖。 
     * 因?yàn)锳IDL方法是在服務(wù)端的Binder線(xiàn)程池中執(zhí)行的,因此當(dāng)多個(gè)客戶(hù)端同時(shí)連接的時(shí)候邻悬, 
     * 會(huì)存在多個(gè)線(xiàn)程同時(shí)訪(fǎng)問(wèn)的情形症昏,所以我們要在AIDL方法中處理線(xiàn)程同步, 
     * 而我們這里直接使用CopyOnWriteArrayList來(lái)進(jìn)行自動(dòng)的線(xiàn)程同步父丰。 
     * */  
    /* 
     * 在AIDL中能夠使用的List只有ArrayList肝谭,但是我們這里卻使用了CopyOnWriteArrayList(它并不是繼承子ArrayList的), 
     * 但為什么還能工作呢础米? 
     * 因?yàn)锳IDL中所支持的是抽象List分苇,而List只是一個(gè)接口, 
     * 因此雖然服務(wù)端返回的是CopyOnWriteArrayList屁桑, 
     * 但是Binder中會(huì)按照List的規(guī)范去訪(fǎng)問(wèn)數(shù)據(jù)并最終形成一個(gè)ArrayList傳遞給客戶(hù)端医寿。 
     * 所以我們?cè)诜?wù)端采用CopyOnWriteArrayList是完全可以的, 
     * 和此類(lèi)似的還有ConcurrentHashMap蘑斧。 
     * */  
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();  
  
    /* 
     * 創(chuàng)建一個(gè)Binder對(duì)象靖秩,并在onBind方法中返回它须眷。 
     * 這個(gè)Binder對(duì)象繼承自IBookManager.Stub,并實(shí)現(xiàn)了它內(nèi)部的AIDL方法沟突, 
     * 這里主要看getBookList和addBook這兩個(gè)AIDL方法的實(shí)現(xiàn)花颗,實(shí)現(xiàn)過(guò)程也比較簡(jiǎn)單, 
     * */  
    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;  
    }  
}  

還需要注意的是惠拭,我們需要注冊(cè)這個(gè)服務(wù)端扩劝,并讓它運(yùn)行在獨(dú)立的進(jìn)程中,這樣它和客戶(hù)端的Activity不在同一個(gè)進(jìn)程中职辅,這樣就構(gòu)成了進(jìn)程間通信的場(chǎng)景:

<service  
    android:name=".aidl.BookManagerService"  
    android:process=":remote" >  
</service>  

(5)客戶(hù)端的實(shí)現(xiàn):

客戶(hù)端比較容易實(shí)現(xiàn)棒呛,首先需要綁定遠(yuǎn)程服務(wù),綁定成功后將服務(wù)端返回的Binder對(duì)象轉(zhuǎn)換成AIDL接口域携,然后就可以通過(guò)這個(gè)接口去調(diào)用服務(wù)端的遠(yuǎn)程方法了簇秒。

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) {  
            /* 
             * 這里的實(shí)現(xiàn)方式和Messenger簡(jiǎn)直一樣樣的, 
             * 都是在綁定服務(wù)端的過(guò)程中秀鞭,服務(wù)端通過(guò)onBind方法將它的Binder傳遞過(guò)來(lái)趋观, 
             * 然后在客戶(hù)端以這個(gè)傳遞來(lái)的Binder創(chuàng)建對(duì)應(yīng)的對(duì)象 
             * */  
            IBookManager bookManager = IBookManager.Stub.asInterface(service);  
            try {  
              /* 
               * 然后就可以調(diào)用相應(yīng)的方法了。 
               * */  
                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);  
        /* 
         * 綁定服務(wù): 
         * */  
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);  
    }  
  
  
    @Override  
    protected void onDestroy() {  
        unbindService(mConnection);  
        super.onDestroy();  
    }  
  
  
}  

以上就算是一個(gè)比較簡(jiǎn)單的完整的AIDL進(jìn)行IPC的過(guò)程锋边。

3皱坛、AIDL使用過(guò)程中的一些問(wèn)題,應(yīng)用觀(guān)察者模式

(1)我們用觀(guān)察者模式來(lái)實(shí)現(xiàn)當(dāng)圖書(shū)館接收到新書(shū)后宠默,就為申請(qǐng)過(guò)新書(shū)到來(lái)通知的用戶(hù)發(fā)送新書(shū)通知麸恍。我們需要提供一個(gè)AIDL接口,每個(gè)用戶(hù)都需要實(shí)現(xiàn)這個(gè)接口并且向圖書(shū)館申請(qǐng)新書(shū)的提醒功能搀矫,同時(shí)也可以取消這個(gè)功能抹沪。用AIDL接口而不用普通接口是因?yàn)锳IDL中無(wú)法使用普通接口。

(2)首先我們創(chuàng)建一個(gè)IOnNewBookArrivedListener.aidl文件瓤球,當(dāng)服務(wù)端有新書(shū)到來(lái)時(shí)融欧,就會(huì)通知每一個(gè)已經(jīng)申請(qǐng)?zhí)嵝压δ艿挠脩?hù)。從程序上來(lái)說(shuō)就是調(diào)用所有IOnNewBookArrivedListener對(duì)象中的onNewBookArrived方法卦羡,并把新書(shū)的對(duì)象通過(guò)參數(shù)傳遞給客戶(hù)端噪馏。

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>();  
  
  
    /* 
     * 用來(lái)保存申請(qǐng)了新書(shū)通知的用戶(hù)。 
     * (1)這里有一個(gè)注意點(diǎn)绿饵,RemoteCallbackList是系統(tǒng)專(zhuān)門(mén)提供的用于刪除跨進(jìn)程listener的接口欠肾。 
     * RemoteCallbackList是一個(gè)泛型,支持管理任意的AIDL接口拟赊。 
     * 它的內(nèi)部有一個(gè)Map結(jié)構(gòu)專(zhuān)門(mén)用來(lái)保存所有的AIDL回調(diào)刺桃, 
     * 這個(gè)Map的key是IBinder類(lèi)型,value是Callback類(lèi)型吸祟,如下所示: 
     * ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>(); 
     * 其中Callback中封裝了真正的遠(yuǎn)程listener瑟慈。當(dāng)客戶(hù)端注冊(cè)listener的時(shí)候桃移,它會(huì)把這個(gè)listener的信息存入mCallbacks中, 
     * 其中key和value分別通過(guò)下面的方式獲得: 
     * IBinder key = listener.asBinder(); 
     * Callback value = new Callback(listener, cookie); 
     * (2)注意點(diǎn)二:也就是說(shuō)葛碧,雖然多次跨進(jìn)程傳輸客戶(hù)端的同一個(gè)對(duì)象會(huì)在服務(wù)端生成不同的對(duì)象借杰, 
     * 但這些新生成的對(duì)象有一個(gè)共同點(diǎn),那就是它們的底層Binder對(duì)象是同一個(gè)进泼。也就是說(shuō)key相同蔗衡。 
     * (3)注意點(diǎn)三:RemoteCallbackList在客戶(hù)端進(jìn)程終止后,能夠自動(dòng)移除客戶(hù)端所注冊(cè)的listener缘琅。 
     * 另外RemoteCallbackList內(nèi)部自動(dòng)實(shí)現(xiàn)了線(xiàn)程同步的功能粘都, 
     * 所以我們使用它來(lái)注冊(cè)和解注冊(cè)時(shí),不需要做額外的線(xiàn)程同步工作刷袍。 
     * */  
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();  
  
  
    /* 
     * 服務(wù)端的Binder對(duì)象,要傳給客戶(hù)端的樊展,讓客戶(hù)端調(diào)用里面的方法: 
     * */  
    private Binder mBinder = new IBookManager.Stub() {  
  
  
    /* 
     * 具體的實(shí)現(xiàn)原來(lái)是在服務(wù)端的服務(wù)中實(shí)現(xiàn)的 
     * */  
        @Override  
        public List<Book> getBookList() throws RemoteException {  
            SystemClock.sleep(5000);  
            return mBookList;  
        }  
  
  
        @Override  
        public void addBook(Book book) throws RemoteException {  
            mBookList.add(book);  
        }  
  
  
        /* 
         * 第二種權(quán)限驗(yàn)證功能方法: 
         * */  
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)  
                throws RemoteException {  
        <span style="white-space:pre">    </span>// 首先查看自定義權(quán)限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;  
            }  
  
  
            // 然后驗(yàn)證包名:  
            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);  
        }  
  
  
        /* 
         * 注冊(cè)申請(qǐng)新書(shū)提醒的用戶(hù): 
         * */  
        @Override  
        public void registerListener(IOnNewBookArrivedListener listener)  
                throws RemoteException {  
        /* 
         * 用RemoteCallbackList呻纹,key和value都是通過(guò)listener來(lái)獲取的: 
         * IBinder key = listener.asBinder(); 
         * Callback value = new Callback(listener, cookie); 
         * 這個(gè)Binder是IOnNewBookArrivedListener這個(gè)aidl的Binder, 
         * 和IBookManager這個(gè)aidl的binder不是同一個(gè)啦专缠。 
         * */  
            mListenerList.register(listener);  
  
  
            /* 
             * RemoteCallbackList并不是一個(gè)List雷酪, 
             * 遍歷RemoteCallbackList必須要使用beginBroadcast和finishBroadcast來(lái)配對(duì)使用, 
             * 哪怕只是為了獲取RemoteCallbackList中的元素個(gè)數(shù)涝婉。 
             * */  
            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并不是一個(gè)List哥力, 
             * 遍歷RemoteCallbackList必須要使用beginBroadcast和finishBroadcast來(lái)配對(duì)使用, 
             * 哪怕只是為了獲取RemoteCallbackList中的元素個(gè)數(shù)墩弯。 
             * */  
            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方法中進(jìn)行權(quán)限驗(yàn)證吩跋,驗(yàn)證不能通過(guò)就直接返回null。 
     * 這種方法需要服務(wù)端在AndroidManifest中聲明所需的權(quán)限: 
     * <permission 
     * android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE" 
    * android:protectionLevel="normal" /> 
     * 在客戶(hù)端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);  
        /* 
         * 如果客戶(hù)端沒(méi)有使用這個(gè)權(quán)限渔工,就會(huì)綁定失敗锌钮。 
         * */  
        if (check == PackageManager.PERMISSION_DENIED) {  
            return null;  
        }  
        return mBinder;  
    }  
  
  
    @Override  
    public void onDestroy() {  
        mIsServiceDestoryed.set(true);  
        super.onDestroy();  
    }  
  
  
    /* 
     * 當(dāng)有新書(shū)到來(lái)的時(shí)候,通知每一位用戶(hù): 
     * 這里需要注意一下的是引矩,當(dāng)新書(shū)到達(dá)的時(shí)候梁丘, 
     * 服務(wù)端會(huì)回調(diào)客戶(hù)端的IOnNewBookArrivedListener對(duì)象中的onNewBookArrived方法, 
     * 這個(gè)方法是在客戶(hù)端的Binder線(xiàn)程池中執(zhí)行的 
     * */  
    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();  
    }  
  
  
    /* 
     * 我們?cè)O(shè)定每隔5m添加一本新書(shū): 
     * */  
    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)客戶(hù)端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;  
  
    /* 
     * 當(dāng)有新書(shū)到來(lái)的時(shí)候旺韭,服務(wù)端通知每一位用戶(hù): 
     * 這里需要注意一下的是氛谜,當(dāng)新書(shū)到達(dá)的時(shí)候, 
     * 服務(wù)端會(huì)回調(diào)客戶(hù)端的IOnNewBookArrivedListener對(duì)象中的onNewBookArrived方法区端, 
     * 這個(gè)方法是在客戶(hù)端的Binder線(xiàn)程池中執(zhí)行的值漫, 
     * 因此為了便于進(jìn)行UI操作,我們需要一個(gè)Handler可以將其切換到客戶(hù)端的主線(xiàn)程中去執(zhí)行珊燎。 
     * */  
    @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  
        }  
    };  
  
    /* 
     * 連接服務(wù)器惭嚣,這個(gè)IBinder service就是服務(wù)器返回給我們的Binder對(duì)象遵湖。 
     * */  
    private ServiceConnection mConnection = new ServiceConnection() {  
        public void onServiceConnected(ComponentName className, IBinder service) {  
            /* 
             * 如果客戶(hù)端和服務(wù)端在同一進(jìn)程,那么asInterface返回內(nèi)部類(lèi)Stub晚吞, 
             * 否則返回內(nèi)部類(lèi)Stub的內(nèi)部代理類(lèi)Proxy: 
             * */  
            IBookManager bookManager = IBookManager.Stub.asInterface(service);  
            mRemoteBookManager = bookManager;  
            try {  
                /* 
                 * 給Binder設(shè)置死亡代理: 
                 * */  
                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());  
                /* 
                 * 申請(qǐng)新書(shū)提醒功能: 
                 * */  
                bookManager.registerListener(mOnNewBookArrivedListener);  
            } catch (RemoteException e) {  
                e.printStackTrace();  
            }  
        }  
  
        public void onServiceDisconnected(ComponentName className) {  
            mRemoteBookManager = null;  
            Log.d(TAG, "onServiceDisconnected. tname:" + Thread.currentThread().getName());  
        }  
    };  
  
    /* 
     * 每個(gè)客戶(hù)端用戶(hù)內(nèi)部都有這樣一個(gè)對(duì)象的延旧,用來(lái)傳遞給服務(wù)端注冊(cè)新書(shū)提醒的。 
     * */  
    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {  
  
        /* 
         * 當(dāng)有新書(shū)到來(lái)的時(shí)候槽地,服務(wù)端通知每一位用戶(hù): 
         * 這里需要注意一下的是迁沫,當(dāng)新書(shū)到達(dá)的時(shí)候, 
         * 服務(wù)端會(huì)回調(diào)客戶(hù)端的IOnNewBookArrivedListener對(duì)象中的onNewBookArrived方法捌蚊, 
         * 這個(gè)方法是在客戶(hù)端的Binder線(xiàn)程池中執(zhí)行的集畅, 
         * 因此為了便于進(jìn)行UI操作,我們需要一個(gè)Handler可以將其切換到客戶(hù)端的主線(xiàn)程中去執(zhí)行缅糟。 
         * */  
        @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();  
    }  
  
}  

注意點(diǎn)一:客戶(hù)端調(diào)用遠(yuǎn)程服務(wù)的方法挺智,被調(diào)用的方法運(yùn)行在服務(wù)端的Binder線(xiàn)程池中,同時(shí)客戶(hù)端線(xiàn)程會(huì)被掛起窗宦。這個(gè)時(shí)候如果服務(wù)端方法執(zhí)行比較耗時(shí)赦颇,就會(huì)導(dǎo)致客戶(hù)端線(xiàn)程長(zhǎng)時(shí)間的阻塞在這里,而如果這個(gè)客戶(hù)端線(xiàn)程是UI線(xiàn)程的話(huà)赴涵,就會(huì)導(dǎo)致客戶(hù)端ANR媒怯。

注意點(diǎn)二:由于客戶(hù)端的onServiceConnected和onServiceDisconnected方法都運(yùn)行在UI線(xiàn)程中,所以也不可以在他們里面直接調(diào)用服務(wù)端的耗時(shí)方法髓窜,這點(diǎn)要尤其注意扇苞。

注意點(diǎn)三:由于服務(wù)端的方法本身就運(yùn)行在服務(wù)端的Binder線(xiàn)程中,所以服務(wù)端方法本身就可以執(zhí)行大量耗時(shí)操作寄纵,這個(gè)時(shí)候切記不要在服務(wù)端方法中開(kāi)線(xiàn)程去執(zhí)行異步任務(wù)鳖敷。懂?就是耗時(shí)操作在服務(wù)端方法中直接執(zhí)行擂啥,不要再開(kāi)啟其他的線(xiàn)程來(lái)執(zhí)行耗時(shí)操作啦哄陶。

注意點(diǎn)四:同理,當(dāng)遠(yuǎn)程服務(wù)端需要調(diào)用客戶(hù)端的listener中的方法時(shí)哺壶,被調(diào)用的方法也運(yùn)行在Binder線(xiàn)程池中屋吨,只不過(guò)是客戶(hù)端的線(xiàn)程池,所以山宾,我們同樣不可以在服務(wù)端中調(diào)用客戶(hù)端的耗時(shí)方法至扰。如果非要調(diào)用耗時(shí)方法,請(qǐng)確保這個(gè)方法運(yùn)行在非UI線(xiàn)程中资锰,否則將導(dǎo)致服務(wù)端無(wú)法響應(yīng)敢课。

注意點(diǎn)五:AIDL使用方法總結(jié):

首先建一個(gè)Service和一個(gè)AIDL接口,接著創(chuàng)建一個(gè)類(lèi)繼承自AIDL接口中的Stub類(lèi)并實(shí)現(xiàn)Stub中的抽象方法,在Service的onBind方法中返回這個(gè)類(lèi)的對(duì)象直秆,然后客戶(hù)端就可以綁定服務(wù)端Service濒募,建立連接后就可以訪(fǎng)問(wèn)遠(yuǎn)程服務(wù)端的方法了。

七圾结、Binder連接池

1瑰剃、問(wèn)題:隨著AIDL數(shù)量的增加,我們不能無(wú)限制的增加Service筝野。

所以晌姚,我們需要減少Service的數(shù)量,將所有的AIDL放在同一個(gè)Service中去管理歇竟。

2挥唠、工作機(jī)制

每個(gè)業(yè)務(wù)模塊創(chuàng)建自己的AIDL接口并實(shí)現(xiàn)此接口,這個(gè)時(shí)候不同業(yè)務(wù)模塊之間是不能有耦合的焕议,所有實(shí)現(xiàn)細(xì)節(jié)我們要單獨(dú)開(kāi)來(lái)宝磨,然后向服務(wù)端提供自己的唯一標(biāo)識(shí)和其對(duì)應(yīng)的Binder對(duì)象。對(duì)于服務(wù)端來(lái)說(shuō)盅安,只需要一個(gè)Service就可以了懊烤,服務(wù)端提供一個(gè)queryBind而接口,這個(gè)接口能夠根據(jù)業(yè)務(wù)模塊的特征來(lái)返回相應(yīng)的Binder對(duì)象給它們宽堆,不同的業(yè)務(wù)模塊拿到所需的Binder對(duì)象后就可以進(jìn)行遠(yuǎn)程方法調(diào)用了。由此可見(jiàn)茸习,Binder連接池的主要作用是將每個(gè)業(yè)務(wù)模塊的Binder請(qǐng)求統(tǒng)一轉(zhuǎn)發(fā)到遠(yuǎn)程Service中去執(zhí)行畜隶,從而避免了重復(fù)創(chuàng)建Service的過(guò)程。

image

3号胚、舉例說(shuō)明吧

(1)我們有兩個(gè)AIDL接口(ISecurityCenter和ICompute)來(lái)模擬兩個(gè)業(yè)務(wù)模塊籽慢。然后系統(tǒng)會(huì)為它們兩個(gè)在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)這是上面兩個(gè)AIDL接口的實(shí)現(xiàn):其中ISecurityCenter.Stub和ICompute.Stub是在系統(tǒng)在gen目錄下自動(dòng)生成的ISecurityCenter.java和ICompute.java文件中的內(nèi)部類(lèi)Stub猫胁。在內(nèi)部類(lèi)中有它們方法的聲明箱亿,在這里我們繼承這個(gè)內(nèi)部類(lèi)并重寫(xiě)實(shí)現(xiàn)這些方法。

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連接池創(chuàng)建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連接池創(chuàng)建遠(yuǎn)程Service并實(shí)現(xiàn)IBinderPool弃秆。下面是queryBinder的具體實(shí)現(xiàn)届惋,當(dāng)Binder連接池連接上遠(yuǎn)程服務(wù)時(shí),會(huì)根據(jù)不同模塊的標(biāo)識(shí)即binderCode返回不同的Binder對(duì)象菠赚,通過(guò)這個(gè)Binder對(duì)象所執(zhí)行的操作全部發(fā)生在遠(yuǎn)程服務(wù)端:

                                @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)遠(yuǎn)程Service的實(shí)現(xiàn)就比較簡(jiǎn)單了:以前直接返回的是服務(wù)端的Binder對(duì)象脑豹,如今在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";  
  
  
    /* 
     * 在服務(wù)端創(chuàng)建一個(gè)連接池衡查,BinderPoolImpl是BinderPool的內(nèi)部類(lèi)瘩欺, 
     * 它繼承了IBinderPool.Stub,并實(shí)現(xiàn)了queryBinder方法。 
     * */  
    private Binder mBinderPool = new BinderPool.BinderPoolImpl();  
  
  
    @Override  
    public void onCreate() {  
        super.onCreate();  
    }  
  
  
    @Override  
    public IBinder onBind(Intent intent) {  
        Log.d(TAG, "onBind");  
        /* 
         * 返回連接池對(duì)象: 
         * */  
        return mBinderPool;  
    }  
  
  
    @Override  
    public void onDestroy() {  
        super.onDestroy();  
    }  
  
}  

(6)Binder連接池的具體實(shí)現(xiàn)俱饿,在它的內(nèi)部首先它要去綁定遠(yuǎn)程服務(wù)歌粥,綁定成功后,客戶(hù)端就可以通過(guò)它的queryBinder方法去獲取各自對(duì)應(yīng)的Binder拍埠,拿到所需的Binder以后失驶,不同業(yè)務(wù)模塊就可以進(jìn)行各自的操作了:

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的實(shí)例,如果沒(méi)有的話(huà)就創(chuàng)建械拍,有的話(huà)就直接返回突勇。 
     * */  
    public static BinderPool getInsance(Context context) {  
        if (sInstance == null) {  
            synchronized (BinderPool.class) {  
                if (sInstance == null) {  
                    sInstance = new BinderPool(context);  
                }  
            }  
        }  
        return sInstance;  
    }  
  
    /* 
     * 連接BinderPoolService服務(wù)器。 
     * */  
    private synchronized void connectBinderPoolService() {  
        /* 
         * mConnectBinderPoolCountDownLatch這個(gè)東西是干嘛的坷虑? 
         * */  
        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 {  
            /* 
             * 這個(gè)mBinderPool是一個(gè)BinderPool.BinderPoolImpl對(duì)象。 
             * 對(duì)于客戶(hù)端來(lái)說(shuō)調(diào)用的是BinderPool的queryBinder方法迄损, 
             * 而B(niǎo)inderPool的queryBinder方法又調(diào)用了BinderPool.BinderPoolImpl對(duì)象的queryBinder方法定躏。 
             * mBinderPool這個(gè)對(duì)象是服務(wù)端返回給BinderPool的,對(duì)客戶(hù)端是隱藏的芹敌, 
             * 客戶(hù)端只知道BinderPool痊远, 
             * mBinderPool是服務(wù)端和連接池的橋梁, 
             * BinderPool是客戶(hù)端和連接池的橋梁 
             * */  
            if (mBinderPool != null) {  
                binder = mBinderPool.queryBinder(binderCode);  
            }  
        } catch (RemoteException e) {  
            e.printStackTrace();  
        }  
        return binder;  
    }  
  
    /* 
     * 連接服務(wù)器的時(shí)候用的氏捞,里面有連接成功和連接斷開(kāi)后的操作碧聪。 
     * */  
    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {  
  
        @Override  
        public void onServiceDisconnected(ComponentName name) {  
            // ignored.  
        }  
  
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            /* 
             * 將服務(wù)器端的Binder轉(zhuǎn)換成客戶(hù)端所需的AIDL接口對(duì)象: 
             * 服務(wù)端返回的是BinderPool連接池,而不是單純的一個(gè)Binder對(duì)象液茎。 
             * */  
            mBinderPool = IBinderPool.Stub.asInterface(service);  
            try {  
                /* 
                 * 設(shè)置死亡代理: 
                 * */  
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);  
            } catch (RemoteException e) {  
                e.printStackTrace();  
            }  
            mConnectBinderPoolCountDownLatch.countDown();  
        }  
    };  
  
    /* 
     * 設(shè)置死亡代理: 
     * */  
    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)這個(gè)是我們的Binder連接池逞姿,它源于IBinderPool.aidl這個(gè)AIDL,它里面包含一個(gè)queryBinder方法捆等, 
     * 我們的Binder連接池是放在服務(wù)端用滞造, 
     * 所以在服務(wù)端需要有這樣一個(gè)BinderPoolImpl的實(shí)例,并且它是一個(gè)Binder: 
     * private Binder mBinderPool = new BinderPool.BinderPoolImpl(); 
     * (2)那怎么用呢栋烤? 
     * 我們當(dāng)前所在的類(lèi)BinderPool.java就是用來(lái)綁定服務(wù)端的客戶(hù)端谒养, 
     * 在BinderPool綁定服務(wù)端的時(shí)候,服務(wù)端會(huì)將mBinderPool返回給客戶(hù)端也就是我們這個(gè)類(lèi)明郭, 
     * 然后我們可以根據(jù)服務(wù)端返回的這個(gè)Binder來(lái)轉(zhuǎn)換成客戶(hù)端所需的AIDL接口對(duì)象买窟,還是叫mBinderPool, 
     * 然后我們這個(gè)類(lèi)中就可以調(diào)用mBinderPool中的方法: 
     * binder = mBinderPool.queryBinder(binderCode); 
     * (3)那另外的兩個(gè)AIDL呢达址?ICompute.aidl和ISecurityCenter.aidl呢蔑祟? 
     * 由于另外的兩個(gè)AIDL的使用都是和服務(wù)端相關(guān)聯(lián)的,是服務(wù)端的queryBinder方法將它們的Binder返回給客戶(hù)端的沉唠, 
     * 客戶(hù)端接到這兩個(gè)AIDL的Binder以后疆虚,依舊是通過(guò)轉(zhuǎn)換成AIDL接口對(duì)象來(lái)使用這兩個(gè)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;  
        }  
    }  
  
}  

注意點(diǎn)一:mBinderPool是BinderPool.BinderPoolImpl對(duì)象,這個(gè)BinderPool.BinderPoolImpl是BinderPool中的一個(gè)內(nèi)部類(lèi)径簿,里面具體實(shí)現(xiàn)了queryBinder方法罢屈。服務(wù)端會(huì)創(chuàng)建一個(gè)mBinderPool對(duì)象然后在BinderPool對(duì)其綁定的過(guò)程中返回給BinderPool,這樣BinderPool和服務(wù)端就通過(guò)mBinderPool這個(gè)對(duì)象進(jìn)行聯(lián)系篇亭。

注意點(diǎn)二:對(duì)于客戶(hù)端來(lái)說(shuō)缠捌,首先他要獲取這個(gè)BinderPool連接池,然后根據(jù)BinderPool的queryBinder來(lái)獲取它對(duì)應(yīng)的Binder對(duì)象译蒂,然后根據(jù)這個(gè)Binder對(duì)象可以去執(zhí)行具體的方法曼月。

注意點(diǎn)三:首先需要搞清楚的是,哪里是在服務(wù)端運(yùn)行的柔昼,哪里是在客戶(hù)端運(yùn)行的哑芹。

對(duì)于客戶(hù)端而言,僅僅是有一個(gè)ISecurityCenter和ICompute的對(duì)象捕透,它們是AIDL聪姿,這兩個(gè)對(duì)象是沒(méi)有方法的具體實(shí)現(xiàn)的,具體實(shí)現(xiàn)是在服務(wù)端的乙嘀。在服務(wù)端有SecurityCenterImpl和ComputeImpl來(lái)繼承ISecurityCenter.Stub和ICompute.Stub末购,因?yàn)镾tub是Binder對(duì)象,所以它們兩個(gè)也是Binder虎谢,里面還給出了方法的具體實(shí)現(xiàn)盟榴。但服務(wù)端和客戶(hù)端并不在同一個(gè)進(jìn)程中,那么客戶(hù)端為了調(diào)用服務(wù)端的方法婴噩,就必須使用Binder對(duì)象曹货,所以客戶(hù)端要去綁定服務(wù)端,然后服務(wù)端返回Binder對(duì)象讳推。但當(dāng)我們使用了連接池BinderPool的時(shí)候,讓連接池BinderPool與服務(wù)端BinderPoolService綁定玩般。在服務(wù)端BinderPoolService中有這樣一個(gè)對(duì)象:mBinderPool银觅,它是BinderPool.BinderPoolImpl,BinderPool.BinderPoolImpl是BinderPool的一個(gè)內(nèi)部類(lèi)坏为,里面有一個(gè)queryBinder方法究驴,用來(lái)返回真正的對(duì)應(yīng)客戶(hù)端的Binder對(duì)象,在連接池BinderPool與服務(wù)端綁定以后匀伏,服務(wù)端將這個(gè)mBinderPool對(duì)象返回給連接池洒忧,這樣連接池就可以通過(guò)這個(gè)mBinderPool對(duì)象為客戶(hù)端返回相應(yīng)的Binder對(duì)象。這樣當(dāng)多個(gè)種類(lèi)的客戶(hù)端想要綁定服務(wù)端的時(shí)候够颠,只需要直接調(diào)用連接池就可以了熙侍,因?yàn)檫B接池根據(jù)服務(wù)端給它的mBinderPool掌管了所有的Binder對(duì)象,不過(guò)要注意的是,連接池是通過(guò)服務(wù)端返回的連接池實(shí)現(xiàn)對(duì)象才能管理這些Binder蛉抓,所以說(shuō)庆尘,所有的Binder對(duì)象還是由服務(wù)端來(lái)掌管的。連接池會(huì)為對(duì)應(yīng)的客戶(hù)端返回對(duì)應(yīng)的Binder對(duì)象巷送,這些Binder對(duì)象就是SecurityCenterImpl具體實(shí)現(xiàn)方法的Binder驶忌。

(7)下面就驗(yàn)證一下Binder連接池的效果了:看客戶(hù)端。

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;  
  
/* 
 * 這里是客戶(hù)端 
 * */  
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);  
        /* 
         * 在線(xiàn)程中去執(zhí)行: 
         * */  
        new Thread(new Runnable() {  
  
            @Override  
            public void run() {  
                doWork();  
            }  
        }).start();  
    }  
  
    private void doWork() {  
        // 首先獲取一個(gè)BinderPool的實(shí)例:這里是帶了上下文的笑跛,避免創(chuàng)建多個(gè)付魔。  
        BinderPool binderPool = BinderPool.getInsance(BinderPoolActivity.this);  
        /* 
         * 然后根據(jù)客戶(hù)端編號(hào)bindercode查詢(xún)Binder,返回的是對(duì)應(yīng)的客戶(hù)端的Binder飞蹂。 
         * 在binderPool.queryBinder中几苍,是根據(jù)在綁定服務(wù)端過(guò)程中返回的BinderPoolImpl的Binder, 
         * 這個(gè)BinderPoolImpl就是繼承了IBinderPool的晤柄,所以也實(shí)現(xiàn)了其中的queryBinder的擦剑。 
         * 這樣返回的才是真正對(duì)應(yīng)的securityBinder。 
         * */   
        IBinder securityBinder = binderPool  
                .queryBinder(BinderPool.BINDER_SECURITY_CENTER);  
        ;  
        /* 
         * 查到對(duì)應(yīng)的Binder以后芥颈,就可以根據(jù)這個(gè)Binder來(lái)轉(zhuǎn)換成客戶(hù)端所需的AIDL接口對(duì)象: 
         * */  
        mSecurityCenter = (ISecurityCenter) SecurityCenterImpl  
                .asInterface(securityBinder);  
        Log.d(TAG, "visit ISecurityCenter");  
        String msg = "helloworld-安卓";  
        System.out.println("content:" + msg);  
        try {  
            /* 
             * 有了接口對(duì)象惠勒,自然就可以調(diào)用對(duì)象中的方法了: 
             * */  
            String password = mSecurityCenter.encrypt(msg);  
            System.out.println("encrypt:" + password);  
            System.out.println("decrypt:" + mSecurityCenter.decrypt(password));  
        } catch (RemoteException e) {  
            e.printStackTrace();  
        }  
  
          
        /* 
         * 下面這是另一個(gè)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數(shù)據(jù)的

由于Bundle實(shí)現(xiàn)了Parcelable接口嘶摊,所以它可以方便的在不同的進(jìn)程間傳輸。我們可以在Bundle中附加我們需要傳輸給遠(yuǎn)程進(jìn)程的信息并通過(guò)Intent發(fā)送出去。

2族铆、一個(gè)特殊的使用場(chǎng)景

比如A進(jìn)程在進(jìn)行一個(gè)計(jì)算,計(jì)算完成后它要啟動(dòng)B進(jìn)程的一個(gè)組件并把計(jì)算結(jié)果傳遞給B進(jìn)程哭尝,可是遺憾的是這個(gè)計(jì)算結(jié)果并不支持放入Bundle中哥攘,因此無(wú)法通過(guò)Intent來(lái)傳輸,這個(gè)時(shí)候如果我們用其他IPC方式就會(huì)略顯復(fù)雜材鹦∈叛停可以考慮如下方式:我們通過(guò)Intent啟動(dòng)進(jìn)程B的一個(gè)Service組件(比如IntentService),讓Service在后臺(tái)進(jìn)行計(jì)算桶唐,計(jì)算完畢后在啟動(dòng)B進(jìn)程中真正要啟動(dòng)的目標(biāo)組件栅葡,由于Service也運(yùn)行在B進(jìn)程中,所以目標(biāo)組件就可以直接獲取計(jì)算結(jié)果尤泽,這樣一來(lái)就輕松解決了跨進(jìn)程的問(wèn)題欣簇。這種方式的核心思想在于將原本需要在A進(jìn)程的計(jì)算任務(wù)轉(zhuǎn)移到B進(jìn)程的后臺(tái)Service中去執(zhí)行规脸。

九、使用ContentProvider

十醉蚁、使用Socket

1燃辖、Socket套接字

(1)網(wǎng)絡(luò)通信,分為流式套接字和用戶(hù)數(shù)據(jù)報(bào)套接字兩種网棍,分別對(duì)應(yīng)于網(wǎng)絡(luò)的傳輸控制層中的TCP和UDP協(xié)議黔龟。

(2)Socket本身可以支持傳輸任意字節(jié)流。

2滥玷、使用Socket進(jìn)行通信氏身,首先需要聲明權(quán)限

<uses-permission android:name="android.permission.INTERNET" />  
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />  

3、使用Socket進(jìn)行通信惑畴,不能再主線(xiàn)程中訪(fǎng)問(wèn)網(wǎng)絡(luò)

因?yàn)檫@會(huì)導(dǎo)致我們的程序無(wú)法在Android4.0及以上的設(shè)備上運(yùn)行蛋欣,會(huì)拋出異常:android.os.NetworkOnMainThreadException。而且進(jìn)行網(wǎng)絡(luò)操作很可能是耗時(shí)的如贷。

4陷虎、案例:跨進(jìn)程的聊天程序

(1)首先在遠(yuǎn)程Service建立一個(gè)TCP服務(wù),然后在主界面中連接TCP服務(wù)杠袱,連接上了以后尚猿,就可以給服務(wù)端發(fā)消息,然后服務(wù)端隨機(jī)的回應(yīng)我們一條信息楣富。我們的服務(wù)端可以和多個(gè)客戶(hù)建立連接并響應(yīng)凿掂。

(2)服務(wù)端。當(dāng)Service啟動(dòng)時(shí)纹蝴,會(huì)在線(xiàn)程中建立TCP服務(wù)庄萎,這里監(jiān)聽(tīng)的是8688端口,然后就可以等待客戶(hù)端的連接請(qǐng)求塘安。當(dāng)有客戶(hù)端連接時(shí)糠涛,就會(huì)生成一個(gè)新的Socket,通過(guò)每次新創(chuàng)建的Socket就可以分別和不同的客戶(hù)端通信了兼犯。服務(wù)端每收到一次客戶(hù)端的消息就會(huì)隨機(jī)回復(fù)一句話(huà)給客戶(hù)端脱羡。當(dāng)客戶(hù)端斷開(kāi)連接時(shí),服務(wù)端這邊也會(huì)相應(yīng)的關(guān)閉對(duì)應(yīng)的Socket并結(jié)束通話(huà)線(xiàn)程免都。

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[] {  
            "你好啊,哈哈",  
            "請(qǐng)問(wèn)你叫什么名字呀帆竹?",  
            "巴拉巴拉",  
            "巴拉巴拉小魔仙",  
            "艸"  
    };  
  
  
    @Override  
    public void onCreate() {  
    <span style="white-space:pre">  </span>/* 
    <span style="white-space:pre">  </span> * 開(kāi)啟一個(gè)線(xiàn)程绕娘,TcpServer是實(shí)現(xiàn)了Runnable的, 
    <span style="white-space:pre">  </span> * 里面開(kāi)啟了服務(wù)端這邊的Socket栽连。 
    <span style="white-space:pre">  </span> * 這里的TcpServer是繼承自Runnable的险领。 
    <span style="white-space:pre">  </span> * */  
        new Thread(new TcpServer()).start();  
        super.onCreate();  
    }  
  
  
    /* 
     * 這次不需要綁定服務(wù)端侨舆。 
     * */  
    @Override  
    public IBinder onBind(Intent intent) {  
        return null;  
    }  
  
  
    @Override  
    public void onDestroy() {  
    <span style="white-space:pre">  </span>/* 
    <span style="white-space:pre">  </span> * 用來(lái)告訴Socket線(xiàn)程,我們的服務(wù)結(jié)束了绢陌。 
    <span style="white-space:pre">  </span> * */  
        mIsServiceDestoryed = true;  
        super.onDestroy();  
    }  
  
  
    /* 
     * 在線(xiàn)程里面開(kāi)啟Socket通信挨下。 
     * 對(duì)于服務(wù)端,就是開(kāi)啟一個(gè)Socket端口脐湾,等待客戶(hù)端來(lái)發(fā)起連接請(qǐng)求: 
     * */  
    private class TcpServer implements Runnable {  
  
  
        @SuppressWarnings("resource")  
        @Override  
        public void run() {  
        <span style="white-space:pre">    </span>/* 
        <span style="white-space:pre">    </span> * ServerSocket這種東西是系統(tǒng)自帶的啦臭笆,直接拿來(lái)用就好了, 
        <span style="white-space:pre">    </span> * 就這么容易服務(wù)端就開(kāi)啟了Socket等待客戶(hù)端來(lái)連接: 
        <span style="white-space:pre">    </span> * */  
            ServerSocket serverSocket = null;  
            try {  
            <span style="white-space:pre">  </span>// 監(jiān)聽(tīng)本地8688端口:  
                serverSocket = new ServerSocket(8688);  
            } catch (IOException e) {  
                System.err.println("establish tcp server failed, port:8688");  
                e.printStackTrace();  
                return;  
            }  
  
  
            /* 
             * 開(kāi)啟Socket以后秤掌,服務(wù)端只需要一直等待就好了愁铺。 
             * */  
            while (!mIsServiceDestoryed) {  
                try {  
                    // 接收客戶(hù)端請(qǐng)求,如果一直沒(méi)有客戶(hù)端闻鉴,程序就會(huì)卡在這句茵乱,卡著!  
                    final Socket client = serverSocket.accept();  
                    System.out.println("accept");  
                    /* 
                     * 注意:不能在主線(xiàn)程中訪(fǎng)問(wèn)網(wǎng)絡(luò)孟岛。 
                     * 一來(lái)是不允許的瓶竭, 
                     * 二來(lái)放在主線(xiàn)程也會(huì)影響程序的響應(yīng)效率。 
                     * 每來(lái)一個(gè)客戶(hù)端連接就開(kāi)啟一個(gè)線(xiàn)程渠羞。 
                     * */  
                    new Thread() {  
                        @Override  
                        public void run() {  
                            try {  
                            <span style="white-space:pre">  </span>// 這個(gè)響應(yīng)方法在下面定義的斤贰。  
                                responseClient(client);  
                            } catch (IOException e) {  
                                e.printStackTrace();  
                            }  
                        };  
                    }.start();  
  
  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
  
  
    /* 
     * 用來(lái)相應(yīng)客戶(hù)端: 
     * */  
    private void responseClient(Socket client) throws IOException {  
        // 用于接收客戶(hù)端消息:  
    <span style="white-space:pre">  </span>// BufferedReader用于接收:  
        BufferedReader in = new BufferedReader(new InputStreamReader(  
                client.getInputStream()));  
        // 用于向客戶(hù)端發(fā)送消息:  
        // PrintWriter用于發(fā)送:  
        PrintWriter out = new PrintWriter(new BufferedWriter(  
                new OutputStreamWriter(client.getOutputStream())), true);  
        out.println("歡迎來(lái)到聊天室");  
        /* 
         * 當(dāng)客戶(hù)端與服務(wù)端的連接沒(méi)有斷開(kāi)時(shí),服務(wù)器就一直監(jiān)聽(tīng)來(lái)自客戶(hù)端的Socket: 
         * */  
        while (!mIsServiceDestoryed) {  
            String str = in.readLine();  
            System.out.println("msg from client:" + str);  
            /* 
             * 當(dāng)客戶(hù)端斷開(kāi)連接后堵未,服務(wù)端這邊的輸入流in會(huì)接收到null腋舌, 
             * 這個(gè)時(shí)候就要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.");  
        // 關(guān)閉流  
        MyUtils.close(out);  
        MyUtils.close(in);  
        client.close();  
    }  
  
  
}  

(3)客戶(hù)端:

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);  
          
        /* 
         * 先把服務(wù)端啟動(dòng)了:startService(service); 
         * 然后在一個(gè)線(xiàn)程中去連接服務(wù)端的Socket:connectTCPServer(); 
         * */  
        Intent service = new Intent(this, TCPServerService.class);  
        startService(service);  
        /* 
         * 開(kāi)啟一個(gè)線(xiàn)程去連接服務(wù)端Socket: 
         * */  
        new Thread() {  
            @Override  
            public void run() {  
                connectTCPServer();  
            }  
        }.start();  
    }  
  
    /* 
     * 當(dāng)Activity退出的時(shí)候渗蟹,記得關(guān)閉和服務(wù)端的Socket連接: 
     * */  
    @Override  
    protected void onDestroy() {  
        if (mClientSocket != null) {  
            try {  
                mClientSocket.shutdownInput();  
                mClientSocket.close();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
        super.onDestroy();  
    }  
  
    /* 
     * 點(diǎn)擊Button發(fā)送消息給服務(wù)端: 
     * */  
    @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));  
    }  
  
    /* 
     * 連接服務(wù)端的Socket块饺。 
     * */  
    private void connectTCPServer() {  
        Socket socket = null;  
        /* 
         * 為了確定能夠連接成功,這里采用了超時(shí)重連的策略雌芽, 
         * 每次連接失敗后都會(huì)重新建立嘗試連理連接授艰。 
         * */  
        while (socket == null) {  
            try {  
                socket = new Socket("localhost", 8688);  
                mClientSocket = socket;  
                /* 
                 * 這是客戶(hù)端用來(lái)發(fā)送消息的輸出流: 
                 * */   
                mPrintWriter = new PrintWriter(new BufferedWriter(  
                        new OutputStreamWriter(socket.getOutputStream())), true);  
                /* 
                 * 用一個(gè)Handler來(lái)進(jìn)行和UI交互,因?yàn)槲覀兊目蛻?hù)端是在線(xiàn)程中與服務(wù)端進(jìn)行連接的: 
                 * */  
                mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);  
                System.out.println("connect server success");  
            } catch (IOException e) {  
                /* 
                 * 為了降低重試機(jī)制的開(kāi)銷(xiāo)世落,我們加入了休眠機(jī)制淮腾, 
                 * 即每次重試的時(shí)間間隔為1000毫秒。 
                 * */  
                SystemClock.sleep(1000);  
                System.out.println("connect tcp server failed, retry...");  
            }  
        }  
  
        try {  
            // 用于接收服務(wù)器端的消息  
            BufferedReader br = new BufferedReader(new InputStreamReader(  
                    socket.getInputStream()));  
            // 不斷循環(huán)的接收消息屉佳,當(dāng)Activity退出時(shí)谷朝,循環(huán)也退出并終止線(xiàn)程:  
            while (!TCPClientActivity.this.isFinishing()) {  
                // 如果沒(méi)有消息會(huì)卡住的:  
                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...");  
            // 關(guān)閉流:  
            MyUtils.close(mPrintWriter);  
            MyUtils.close(br);  
            socket.close();  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
}  

(4)其實(shí)Socket不僅僅能實(shí)現(xiàn)進(jìn)程間的通信,還可以實(shí)現(xiàn)設(shè)備間的通信武花,前提是這些設(shè)備之間的IP地址是互相可見(jiàn)的圆凰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市体箕,隨后出現(xiàn)的幾起案子专钉,更是在濱河造成了極大的恐慌挑童,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跃须,死亡現(xiàn)場(chǎng)離奇詭異站叼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)菇民,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)尽楔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人玉雾,你說(shuō)我怎么就攤上這事翔试。” “怎么了复旬?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵垦缅,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我驹碍,道長(zhǎng)壁涎,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任志秃,我火速辦了婚禮怔球,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浮还。我一直安慰自己竟坛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布钧舌。 她就那樣靜靜地躺著担汤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪洼冻。 梳的紋絲不亂的頭發(fā)上崭歧,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音撞牢,去河邊找鬼率碾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛屋彪,可吹牛的內(nèi)容都是我干的所宰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼畜挥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼仔粥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起砰嘁,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤件炉,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后矮湘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體斟冕,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年缅阳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了磕蛇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡十办,死狀恐怖秀撇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情向族,我是刑警寧澤呵燕,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站件相,受9級(jí)特大地震影響再扭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜夜矗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一泛范、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧紊撕,春花似錦罢荡、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至辩稽,卻和暖如春惧笛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背逞泄。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工患整, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人喷众。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓各谚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親到千。 傳聞我的和親對(duì)象是個(gè)殘疾皇子昌渤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容