一揍移、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ì)象之間起到中介的作用拢锹。
抽象對(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ò)程懂了嗎蜓斧?
這次應(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ò)程。
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)的圆凰。