【Android - IPC】之AIDL簡介

來源:互聯網
上載者:User

標籤:hashmap   ddb   書籍   parcel   lin   java   處理   nts   存在   

參考資料:

1、《Android開發藝術探索》第二章2.4.4

2、Android AIDL Binder架構解析:http://blog.csdn.net/lmj623565791/article/details/38461079

3、你真的理解AIDL中的in、out、inoutm麼:http://www.open-open.com/lib/view/open1469494342021.html

4、慕課網《AIDL-小白成長記》

 

1、 AIDL簡介

  Android系統規定:每個應用程式獨自擁有一份虛擬機器,一個應用程式和另一個應用程式沒有辦法直接進行通訊,這樣保證了進程之內資料的安全性,也保證一個應用程式崩潰之後不會導致其他應用程式跟著崩潰。兩個進程如果想要通訊,只能通過Android系統底層進行通訊。

  AIDL(Android Interface Definition Language,Android介面定義語言)是Android中的一種IPC(Inter-Process Communication,跨進程通訊)的方式,它允許我們編寫一個介面,作為用戶端和服務端通訊的橋樑。AIDL是Android基於IDL自己定義的一種用戶端和服務端交流的語言規範。

 

2、 AIDL使用方法

  在這個章節中,我們通過一個執行個體學習AIDL的用法。

  在這個執行個體中有服務端和用戶端,服務端有操作Book的功能,用戶端調用服務端的功能。

  由於Book不是基礎資料型別 (Elementary Data Type),因此想要讓Book在多進程之間傳遞,需要對Book進行序列化,代碼如下:

package my.itgungnir.aidl;import android.os.Parcel;import android.os.Parcelable;/** * Book實體類 */public class Book implements Parcelable {    private String booName;    private int pirce;    public Book(String booName, int pirce) {        this.booName = booName;        this.pirce = pirce;    }    protected Book(Parcel in) {        booName = in.readString();        pirce = in.readInt();    }    public static final Creator<Book> CREATOR = new Creator<Book>() {        @Override        public Book createFromParcel(Parcel in) {            return new Book(in);        }        @Override        public Book[] newArray(int size) {            return new Book[size];        }    };    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeString(booName);        dest.writeInt(pirce);    }}

 

  我們的思路是:在服務端中定義操作Book列表的方法(添加書籍、查詢所有書籍),然後在用戶端中遠程調用服務端的這兩個方法操作書籍。

  下面提供這個執行個體的編寫代碼和詳細說明。

 

2.1、      AIDL介面的建立

  首先來介紹一下AIDL介面的定義文法:

  1、AIDL檔案支援的資料類型:

    (1)基礎資料型別 (Elementary Data Type)(int、long、char、boolean、double等);

    (2)String和CharSequence;

    (3)List:只支援ArrayList,裡面每個元素必須都能夠被AIDL支援;

    (4)Map:只支援HashMap,裡面的每個元素必須都能夠被AIDL支援,包括key和value;

    (5)Parcelable:所有實現了Parcelable介面的對象;

    (6)AIDL:所有的AIDL介面本身也可以在AIDL檔案中使用。

  2、自訂的Parcelable對象和AIDL對象必須要顯式import進來,不管它們是否和當前的AIDL檔案位於同一個包內。

  3、如果AIDL檔案中用到了自訂的Parcelable對象,那麼必須重建立一個和它同名的AIDL檔案,並在其中聲明它為Parcelable類型。

  4、AIDL介面的方法中,除了基本類型的參數,其他類型的參數都必須標上資料流向:in表示資料只能從用戶端流向服務端;out表示資料只能從服務端流向用戶端;inout表示資料可以在服務端和用戶端之間雙向流通。注意:基礎資料型別 (Elementary Data Type)預設且只能是in流向。

  5、AIDL介面中只支援方法,不支援聲明靜態常量。

  6、AIDL的包結構在服務端和用戶端要保持一致,否則運行會出錯,這是因為用戶端需要還原序列化服務端中和AIDL介面相關的所有類,如果類的完整路徑不一樣的話,就無法完成還原序列化,程式也就無法正常運行。

 

  接下來我們來建立一個AIDL介面IBookManager.aidl,其中用到了Book類,因此我們還需要建立一個Book.aidl檔案。

  Book.aidl檔案中的代碼如下:

package my.itgungnir.aidl;parcelable Book;

 

  IBookManager.aidl檔案中的代碼如下:

package my.itgungnir.aidl;import my.itgungnir.aidl.Book;interface IBookManager {    List<Book> getBookList();    void addBook(in Book book);}

 

  可以看到,這兩個AIDL檔案中的文法完全遵循上面說的AIDL文法。

 

2.2、      服務端的實現

  定義了AIDL介面之後,我們就可以實現這個介面了。建立一個Service類BookManagerService,代碼如下:

import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.os.RemoteException;import android.support.annotation.Nullable;import java.util.ArrayList;import java.util.List;import my.itgungnir.aidl.Book;import my.itgungnir.aidl.IBookManager;/** * 定義服務端Service,實現AIDL介面 */public class BookManagerService extends Service {    private CopyOnWriteArrayList<Book> bookList = new CopyOnWriteArrayList<>();    private Binder binder = new IBookManager.Stub() {        @Override        public List<Book> getBookList() throws RemoteException {            return bookList;        }        @Override        public void addBook(Book book) throws RemoteException {            bookList.add(book);        }    };    @Override    public void onCreate() {        super.onCreate();        bookList.add(new Book("Android", 20));        bookList.add(new Book("iOS", 17));    }    @Nullable    @Override    public IBinder onBind(Intent intent) {        return binder;    }}

 

  在這個類中,我們建立了一個Stub的對象,在這個對象的兩個方法中進行資料的處理,最後在onBind()方法中返回這個Stub。

  編寫這個類的時候需要注意:這個類中我們並沒有使用ArrayList,而是一個CopyOnWriteArrayList類(這個類並不繼承自ArrayList)。這個CopyOnWriteArrayList支援是一種支援並發讀/寫的List。由於AIDL是在服務端的Binder線程池中執行的,因此當多個用戶端同時串連的時候,會存在多個線程同時訪問的情形,所以我們要在AIDL方法中處理線程同步,而CopyOnWriteArrayList可以自動進行線程同步。

  另外,前面說道,AIDL中只支援ArrayList,但CopyOnWriteArrayList並不繼承自ArrayList,為什麼AIDL還是會支援呢?這是因為AIDL中所支援的是抽象的List,而List只是一個介面,因此雖然服務端返回的是CopyOnWriteArrayList,但是在Binder中會按照List的規範去訪問資料並最終形成一個新的ArrayList傳遞給用戶端。和這個類類似的是Map中的ConcurrentHashMap。

  編寫完了Service類,我們需要在Manifest檔案中註冊這個Service,代碼如下:

<service android:name=".BookManagerService">    <intent-filter>        <action android:name="my.itgungnir.aidl.book_server" />        <category android:name="android.intent.category.DEFAULT" />    </intent-filter></service>

 

  至此,服務端的代碼就編寫完成了。

 

2.3、      用戶端的實現

  在用戶端,我們先要綁定服務端的服務,將服務端返回的Binder介面轉換成AIDL介面,然後就可以調用服務端的方法了。

  需要注意的是,AIDL檔案和AIDL中用到的Parcelable類,在服務端和用戶端都需要有一份,且包路徑必須一致。

  用戶端的MainActivity中的代碼如下:

import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import java.util.List;import my.itgungnir.aidl.Book;import my.itgungnir.aidl.IBookManager;public class MainActivity extends AppCompatActivity {    private static final String TAG = "MainActivity";    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            IBookManager manager = IBookManager.Stub.asInterface(service);            try {                // 查詢所有圖書                List<Book> bookList = manager.getBookList();                Log.i(TAG, "query book list: " + bookList.toString());                // 添加新書                Book newBook = new Book("Html", 40);                manager.addBook(newBook);                Log.i(TAG, "add book: " + newBook.toString());                // 查詢所有圖書                bookList = manager.getBookList();                Log.i(TAG, "query book list: " + bookList.toString());            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Intent intent = new Intent();        intent.setAction("my.itgungnir.aidl.book_server");        intent.setPackage("my.itgungnir.server");        bindService(intent, connection, Context.BIND_AUTO_CREATE);    }    @Override    protected void onDestroy() {        unbindService(connection);        super.onDestroy();    }}

 

  由於這裡把服務端和用戶端放在兩個不同的Module中,因此需要為Intent對象設定package屬性為服務端的包名。

  可以看到,在用戶端,我們調用了服務端的getBookList()方法,將儲存在服務端List中的Book對象擷取過來,並通過Log日誌列印到控制台。這樣,就可以在控制台中看到我們列印出來的資訊了,如下:

my.itgungnir.client I/MainActivity: query book list: [Book{booName=‘Android‘, pirce=20}, Book{booName=‘iOS‘, pirce=17}]my.itgungnir.client I/MainActivity: add book: Book{booName=‘Html‘, pirce=40}my.itgungnir.client I/MainActivity: query book list: [Book{booName=‘Android‘, pirce=20}, Book{booName=‘iOS‘, pirce=17}, Book{booName=‘Html‘, pirce=40}]

 

 

【Android - IPC】之AIDL簡介

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.