【Android】AIDL 總體介紹


概述:

  • AIDL,全稱 “Android Interface Defination Language”,中文 “Android 接口定義語言
  • 很多人看到這個詞,都不知道怎麼理解。首先需要明確的一點是,AIDL一種語言,後面再講它的作用
  • 我自己第一次接觸到 AIDL 是在寫 framework/base 下的服務 (Service) 的時候,根據我閱讀的相關資料,也要求對 Service 相關知識要有一定了解。所以這裏給出兩篇 Service 的介紹文章:
  • 研究 AIDL 還需要對 Android 中序列化有所瞭解,這部分我目前也沒有看過相關的資料,以後再補充,下文也會稍微提及

爲什麼設計這門語言?

  • 目的是爲了實現進程間通信
  • 每個進程都有自己的 Dalvik VM 實例,都有自己獨立的內存,都在自己狹小的空間過完一生,進程之間並不知道對方的存在。

    看資料說 Android 5.0 系統採用全新的 ART (Android Runtime),拋棄了 Dalvik 虛擬機

  • 但是有時候需要在進程間進行通信,這時候就需要用到 AIDL
  • 通過 AIDL 這門語言,我們可以在一個進程訪問另一個進程的數據,甚至調用其中特定的方法

    所謂特定的方法,按我目前的理解,是指我們在 AIDL 中指定的的方法,完全由編寫 AIDL 的我們來決定


AIDL 的語法:

  • AIDL 語法非常簡單,基本上和 Java 是一樣的,只是在一些細微處有差別

    畢竟它只是被創造出來簡化 Android 程序員工作的,太複雜不好

  • 文件類型:AIDL文件的後綴是 “.aidl”,而不是 “.java
  • 數據類型:AIDL 默認支持一些數據類型,在使用這些數據類型的時是不需要導包的。除此之外的數據類型,都必須導包,就算目標文件與當前 aidl 文件在同一個包(即同一個目錄),都需要導包

    舉個例子:
    假設有兩個文件:Book.javaBookMark.aidl,都放在 com/kenllf/demo 目錄下
    如果需要在 BookMark.aidl 文件中使用 Book 對象,就必須在 BookMark.aidl 文件寫上 import com.kenllf.demo;
    這種情況在 java 中是不需要導包的,可以直接使用,這個應該使用過 java 的都知道,不再贅述

  • 默認支持數據類型包括:
    • Java 中的八種基本數據類型,包括 :byte、short(不支持 short,編譯不通過,有說時因爲 Parcel 無法對 short 進行序列化)、int、long、float、double、boolean、char
    • String 類型
    • CharSequence 類型
    • List 類型 :List 中的所有元素必須是 AIDL 支持的類型之一,或者是一個其它 AIDL 生成的接口,或者是定義的 parcelable(這個下文會有詳解)。List 支持泛型
    • Map 類型 :Map 中的所有元素必須是 AIDL 支持的類型之一,或者是一個其它 AIDL 生成的接口,或者是定義的 parcelable。Map 是不支持泛型的
    • Parcelable :所有實現了 Parcelable 接口的對象
    • AILD :所有 AIDL 接口本身也可以在 AIDL 文件中使用
  • 定向 tag :這個極易被忽略——雖然大家都知道,但是很少人會正確地使用它。AIDL 中的定向 tag 表示了在跨進程通信數據的流向
    • in :表示數據只能由客戶端 (調用方) 流向服務端 (被調用的服務)
    • out :表示數據只能由服務端 (被調用的服務) 流向客戶端 (調用方)
    • inout :則表示數據可在服務端 (被調用的服務) 與客戶端 (調用方) 之間雙向流通
    • 其中,數據流向是針對在客戶端中的那個傳入方法對象而言的。
      • in 爲定向 tag 的話表現爲服務端將會接收到那個對象的完整數據,但是客戶端的那個對象不會因爲服務端對傳參的修改而發生變動(類似於傳值)
      • out 的話表現爲服務端將會接收到那個對象的的空對象,但是在服務端對接收到的空對象有任何修改之後客戶端將會同步變動;(類似於傳引用)
      • inout 爲定向 tag 的情況下,服務端將會接收到客戶端傳來對象的完整信息,並且客戶端將會同步服務端對該對象的任何變動
      • 更詳細的解釋可參考以下文章:
        你真的理解AIDL中的in,out,inout麼?
      • 給一個簡單的例子:
      package android.os;
      
      interface ISecurityService {
          int getTime(out byte[] outBuf, out int[] outLen);
          int setTime(in byte[] inBuf, int inLen);
      }
      
      • Java 中的基本類型StringCharSequence 的定向 tag 默認且只能是 in (這裏可以反推 byte[] 和 int[] 不屬於基本數據類型,其實這個我不是很懂 -_-,只能反推)。還有請注意,請不要濫用定向 tag ,而是要根據需要選取合適的——要是不管三七二十一,全都一上來就用 inout ,等工程大了系統的開銷就會大很多——因爲排列整理參數的開銷是很昂貴的(目前還未理解原理)
  • 兩種 AIDL 文件:
    • 一類用來定義 parcelable 對象,以供其它 AIDL 文件使用 AIDL 中非默認支持的數據類型
      • 關於 parcelabel,以後面的代碼爲例子說一下:

        假設有一個 Book.java 文件,而 IBookManager.aidl 的接口定義需要用到 Book 對象。這時候不能直接導入包,然後直接使用。需要先編寫一個 Book.aidl,把 Book 定義爲 parcelable 對象,然後在 IBookManager.aidl導入包,再使用 Book 的 parcelable 對象。
        其實比起普通的 java 文件調用 Book 對象,就多了編寫 Book.aidl 這一步。
        具體請參考後面的例子。

    • 一類用來定義方法接口,以供系統使用來完成跨進程通信
  • 兩類文件都是在 “定義” 些什麼,都不涉及具體的實現。這就是爲什麼它叫做 “Android接口定義語言
  • 舉兩個具體的使用例子:
// Book.aidl
// 上面所說的第一類 AIDL 文件的例子。
// 這個文件的作用是引入一個序列化對象 Book 供其它的 AIDL 文件使用。
// 看到參考的文章裏說,Book.aidl 與 Book.java 的包名應當是一樣的,
// 我猜應該是 parcelable 這種用法的情況纔是這樣,因爲我接觸過定義 interface 的情況,
// 就有不是同一個包名的,這個有待後續查證。
package android.os;

// 注意哦,parcelable 是小寫
parcelable Book;
// IBookManager.aidl
// 第二類 AIDL 文件的例子
package android.os;
// 導入需要使用的非默認支持的數據類型的包(即使是處於同一個包內,看上文解釋)
import android.os.Book;

interface IBookManager {

    // 所有的返回值前都不需要加任何東西,不管是什麼數據類型
    // 這個有別於 java,java 需要在前面指明是 public 還是 private 還是其它的
    List<Book> getBooks();
    Book getBook();
    int getBookCount();

    // 傳參數時除了 Java 基本類型和 String、CharSequence 以外的類型(參考上文 默認支持的數據類型)
    // 都需要在前面加上定向 tag,具體加什麼根據需要
    void setBookPrice(in Book book, int price);
    void setBookName(in Book book, String name);
    void addBookIn(in Book book);
    void addBookOut(out Book book);
    void addBookInout(inout Book book);
}

如何使用 AIDL 文件來完成跨進程通信?

  • 在進行跨進程通信時,AIDL 文件中定義的方法包含非默認支持的數據類型與否,會影響具體的定義過程。
  • 如果包含默認支持的數據類型,那麼我們只需要編寫一個 AIDL 文件
  • 如果包含非默認支持的數據類型,那麼通常需要編寫 n+1 個 AIDL 文件( n非默認支持的數據類型的種類數

使用數據類實現 Parcelable 接口:

  • 不同進程擁有不同的內存區域,並且只能訪問自己的那一塊內存區域,所以傳一個句柄沒有用的 —— 句柄指向一個內存區域,但是目標進程沒有訪問源進程的內存的權限,傳遞過去也沒用。
  • 既然一個進程無法訪問另外一個進程的內存,那麼要使數據能夠在內存之間流通,就必須將要傳輸的數據轉化爲能夠在內存之間流通的形式。
    這個過程就叫序列化和反序列化。
  • 假設現在需要將一個對象的數據從客戶端傳遞到服務端,大概的過程是:
    • 首先在客戶端對這個對象進行序列化操作,將對象包含的數據轉化爲序列化流(以便傳輸),然後將這個序列化流傳遞到服務端的內存中去
    • 服務端接收到序列化流之後,對序列化流進行反序列化操作,還原其中的數據
    • 通過這種方式就實現了一個進程(服務端)訪問另外一個進程(客戶端)的數據的目的。
    • 其實這個過程有點像兩人之間通過 QQ 傳文件夾。直接傳文件夾不好傳,我先將文件夾壓縮(序列化),然後傳給你,你接收完之後將壓縮包解壓爲文件夾(反序列化),然後就可以隨便使用這個文件夾的內容了。
  • 我們通過 AIDL 進行進程間通信時,選用的序列化方式就是實現 Parcelable 接口

快速生成一個可用的可序列化類的例子:

參考文章:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章