android AIDL 01 雙向通訊
1、概述
AIDL是一個縮寫,全稱是Android Interface Definition Language,也就是Android介面定義語言。是的,首先我們知道的第一點就是:AIDL是一種語言。既然是一種語言,那麼相應的就很自然的衍生出了一些問題:
- 為什麼要設計出這麼一門語言?
- 它有哪些語法?
- 我們應該如何使用它?
- 再深入一點,我們可以思考,我們是如何通過它來達到我們的目的的?
- 更深入一點,為什麼要這麼設計這門語言?會不會有更好的方式來實現我們的目的
2、為什麼要設計這門語言?
設計這門語言的目的是為了實現程序間通訊,尤其是在涉及多程序併發情況下的程序間通訊。
每一個程序都有自己的Dalvik VM例項,都有自己的一塊獨立的記憶體,都在自己的記憶體上儲存自己的資料,執行著自己的操作,都在自己的那片狹小的空間裡過完自己的一生。每個程序之間都你不知我,我不知你,就像是隔江相望的兩座小島一樣,都在同一個世界裡,但又各自有著自己的世界。而AIDL,就是兩座小島之間溝通的橋樑。相對於它們而言,我們就好像造物主一樣,我們可以通過AIDL來制定一些規則,規定它們能進行哪些交流——比如,它們可以在我們制定的規則下傳輸一些特定規格的資料。
總之,通過這門語言,我們可以愉快的在一個程序訪問另一個程序的資料,甚至呼叫它的一些方法,當然,只能是特定的方法。
但是,如果僅僅是要進行跨程序通訊的話,其實我們還有其他的一些選擇,比如 BroadcastReceiver , Messenger 等,但是 BroadcastReceiver 佔用的系統資源比較多,如果是頻繁的跨程序通訊的話顯然是不可取的;Messenger 進行跨程序通訊時請求佇列是同步進行的,無法併發執行,在有些要求多程序的情況下不適用——這種時候就需要使用 AIDL 了。如果想要了解它們更詳細的區別的話,可以去另一篇博文看看:
3,它有哪些語法?
其實AIDL這門語言非常的簡單,基本上它的語法和 Java 是一樣的,只是在一些細微處有些許差別——畢竟它只是被創造出來簡化Android程式設計師工作的,太複雜不好——所以在這裡我就著重的說一下它和 Java 不一樣的地方。主要有下面這些點:
檔案型別:用AIDL書寫的檔案的字尾是 .aidl,而不是 .java。 資料型別:AIDL預設支援一些資料型別,在使用這些資料型別的時候是不需要導包的,但是除了這些型別之外的資料型別,在使用之前必須導包。比如,現在我們編寫了兩個檔案,一個叫做 Book.java ,另一個叫做 BookManager.aidl,它們都在 com.lypeer.aidldemo 包下 ,現在我們需要在 .aidl 檔案裡使用 Book 物件,那麼我們就必須在 .aidl 檔案裡面寫上 import com.aidldemo.Book; 哪怕 .java 檔案和 .aidl 檔案就在一個包下。 預設支援的資料型別包括:
- Java中的八種基本資料型別,包括 byte,short,int,long,float,double,boolean,char。
- String 型別。
- CharSequence型別。
- List型別:List中的所有元素必須是AIDL支援的型別之一,或者是一個其他AIDL生成的介面,或者是定義的parcelable(下文關於這個會有詳解)。List可以使用泛型。
- Map型別:Map中的所有元素必須是AIDL支援的型別之一,或者是一個其他AIDL生成的介面,或者是定義的parcelable。Map是不支援泛型的
- 定向tag:這是一個極易被忽略的點——這裡的“被忽略”指的不是大家都不知道,而是很少人會正確的使用它。在我的理解裡,定向 tag 是這樣的:AIDL中的定向 tag 表示了在跨程序通訊中資料的流向,其中 in 表示資料只能由客戶端流向服務端, out 表示資料只能由服務端流向客戶端,而 inout 則表示資料可在服務端與客戶端之間雙向流通。其中,資料流向是針對在客戶端中的那個傳入方法的物件而言的。in 為定向 tag 的話表現為服務端將會接收到一個那個物件的完整資料,但是客戶端的那個物件不會因為服務端對傳參的修改而發生變動;out 的話表現為服務端將會接收到那個物件的的空物件,但是在服務端對接收到的空物件有任何修改之後客戶端將會同步變動;inout 為定向 tag 的情況下,服務端將會接收到客戶端傳來物件的完整資訊,並且客戶端將會同步服務端對該物件的任何變動。具體的分析大家可以移步我的另一篇博文:你真的理解AIDL中的in,out,inout麼?
- 另外,Java 中的基本型別和 String ,CharSequence 的定向 tag 預設且只能是 in 。還有,請注意,請不要濫用定向 tag ,而是要根據需要選取合適的——要是不管三七二十一,全都一上來就用 inout ,等工程大了系統的開銷就會大很多——因為排列整理引數的開銷是很昂貴的。
- 兩種AIDL檔案:在我的理解裡,所有的AIDL檔案大致可以分為兩類。一類是用來定義parcelable物件,以供其他AIDL檔案使用AIDL中非預設支援的資料型別的。一類是用來定義方法介面,以供系統使用來完成跨程序通訊的。可以看到,兩類檔案都是在“定義”些什麼,而不涉及具體的實現,這就是為什麼它叫做“Android介面定義語言”。
- 注:所有的非預設支援資料型別必須通過第一類AIDL檔案定義才能被使用。
1、AIDL支援的簡單資料型別
- Java基本型別,即int、long、char等;
- String;
- CharSequence;
- List List中的所有元素都必須是AIDL支援的資料型別、其他AIDL介面或你之前宣告的Parcelable實現類。
- Map Map中的所有元素都必須是AIDL支援的資料型別、其他AIDL介面或你之前宣告的Parcelable實現類。定義AIDL
2、建立ServiceAIDL
import com.testmodelconnectionapp.MClientAIDL;
import com.testmodelconnectionapp.MsgBean;
interface MServiceAIDL {
/**
* 接收來自客戶端發來的資訊
* 模擬延時 6秒返回資訊
*/
String getClientInfo(int num1,int num2,in MsgBean msgBean);
/*
*接受客戶端AIDL
*/
MsgBean getServerInfo(in MClientAIDL clientAIDL);
}
3、client
import com.testmodelconnectionapp.MsgBean;
interface MClientAIDL {
//接受服務端的訊息
String getServerInfo (in MsgBean msgBean,String str);
}
4、宣告自定義型別
//宣告自定義型別
parcelable MsgBean;
4、activity繫結並通過aidl呼叫service
private ServiceConnection conn = new ServiceConnection() {
//繫結上服務的時候
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//拿到了遠端的服務
mServiceAIDL = MServiceAIDL.Stub.asInterface(service);
MClientAIDL.Stub stub = new MClientAIDL.Stub() {
@Override
public String getServerInfo(MsgBean msgBean, String str) throws RemoteException {
return null;
}
};
try {
//向服務端 傳遞binder
mServiceAIDL.getServerInfo(stub);
} catch (RemoteException e) {
e.printStackTrace();
}
}
//斷開服務
@Override
public void onServiceDisconnected(ComponentName name) {
//回收資源
mServiceAIDL = null;
}
};
5、server
private IBinder iBinder = new MServiceAIDL.Stub() {
@Override
public String getClientInfo(int num1, int num2, MsgBean msgBean) throws RemoteException {
Log.i(TAG, "接收到Client的資訊: ");
msgBean.setMsg1("接收到Client的資訊");
mClientAIDL.getServerInfo(msgBean,"msgg Form Server");
return String.valueOf(num1+num2);
}
@Override
public MsgBean getServerInfo(MClientAIDL clientAIDL) throws RemoteException {
//收到客戶端的binder
mClientAIDL = clientAIDL;
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
MsgBean msgBean = new MsgBean();
msgBean.setMsg1("我是來自Server的Msg1");
try {
mClientAIDL.getServerInfo(msgBean,"msgg Form Server");
} catch (RemoteException e) {
e.printStackTrace();
}
}
},1000);
return null;
}
};