IPC機(jī)制——AIDL的簡單梳理

[toc]


在了解AIDL之前,我們要對Binder有簡單了解。

Binder

Binder是android的一個類,實現(xiàn)了IBinder接口。Binder主要用在Service中,包括AIDL和Messenger。Messenger底層是AIDL。

  • IPC角度
    Binder是Android中一種跨進(jìn)程的通信方式,還可以理解為一種虛擬的物理設(shè)備,設(shè)備驅(qū)動為/dev/binder,在Linux中沒有這種通信方式。

  • AndroidFramework角度
    Binder是ServiceManager連接各種Manager(ActivityManager、WindowManager...)和相應(yīng)ManagerService橋梁

  • Android應(yīng)用層
    Binder是客戶端和服務(wù)端進(jìn)行通信的媒介,當(dāng)bindService時,服務(wù)端會返回一個包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對象,通過這個Binder對象,客戶端可以獲取服務(wù)端提供的服務(wù)或數(shù)據(jù)。服務(wù)包括普通服務(wù)基于AIDL((Android Interface Definition Language)的服務(wù)

Binder意外死亡后(服務(wù)端進(jìn)程意外停止)重新連接服務(wù)方法

  • 1)給Binder設(shè)置DeathRecipient監(jiān)聽

    Binder提供了兩個配對方法,linkToDeathunlinkToDeathlinkToDeath為Binder設(shè)置一個死亡代理,Binder死亡時,我們收到通知。(isBinderAlive判斷是否死亡)

    Binder死亡時,系統(tǒng)會回調(diào)binderDied()方法,我們可以移除之前綁定的代理并重新綁定遠(yuǎn)程服務(wù)。

  • 2)在onServiceDisconnected中重連遠(yuǎn)程服務(wù)

兩種方法區(qū)別:

  • onServiceDisconnected在客戶端UI線程中被回調(diào)
  • binderDied方法中不能訪問UI

服務(wù)端——創(chuàng)建AIDL接口

AIDL文件支持的數(shù)據(jù)類型
  • 基本數(shù)據(jù)類型(除short,因為序列化不支持short類型)
  • String、CharSequence
  • ArrayList,里面元素必須能夠被AIDL支持
  • HashMap,里面元素必須能夠被AIDL支持
  • Parcelable:所有實現(xiàn)了Parcelable接口的對象
  • AIDL:AIDL接口本身也可在AIDL文件中使用

PS:

  • AIDL除基本數(shù)據(jù)類型,其他類型參數(shù)必須標(biāo)上方向
    • in:輸入型
    • out:輸出型
    • inout:輸入輸出型
  • AIDL接口中只支持方法,不支持聲明靜態(tài)常量!區(qū)別于傳統(tǒng)接口
  • 自定義parcelable對象和AIDL對象必須顯示import
  • AIDL文件用到了自定義parcelable對象,必須新建一個和他同名的AIDL,在其中該聲明他為parcelable類型

IBookManager .aidl

package com.emma.www.myapplication;
import com.emma.www.myapplication.Book;

interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
}

Book.java

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

    public Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public Book(int bookId, String bookName) {

        this.bookId = bookId;
        this.bookName = bookName;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
}

Book.aidl

package com.emma.www.myapplication;
parcelable Book;

服務(wù)端——實現(xiàn)service

Android SDK工具生成一個.java接口文件用你的.aidl文件命名生成的接口包含一個名字為Stub的子類,這是一個它父類的抽象實現(xiàn),并且聲明了.aidl中所有的方法。
  
Stub也定義了一些輔助的方法,最顯著的就是asInterface(),它是用來接收一個IBinder(通常IBinder傳遞給客戶端的onServiceConnected()回調(diào)方法)并且返回一個Stub接口的實例 。

// IService .aidl

interface IService {
    String hello(String name); 
}

繼承Service并且實現(xiàn)onBind()方法返回一個實現(xiàn)生成的Stub類

// AIDLService .java

public class AIDLService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Return the interface
        return new IService.Stub() {
            @Override
            public String hello(String name) throws RemoteException {
                // TODO Auto-generated method stub
                return "hello"+name;
            }
        };
    }

客戶端實現(xiàn)

public class MainActivity extends Activity {

    IService RemoteService; //監(jiān)聽服務(wù)
    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // TODO Auto-generated method stub
            Log.i("mConnection", service + "");
            RemoteService = IService.Stub.asInterface(service);

            try {
                String s = RemoteService.hello("finch");
                Toast.makeText(MainActivity.this, s, Toast.LENGTH_LONG).show();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // TODO Auto-generated method stub

        }

    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initService();
    }

    //連接服務(wù)
    private void initService() {
        Intent i = new Intent();
        i.setAction("android.intent.action.AIDLService");
        boolean ret = bindService(i, mConnection, Context.BIND_AUTO_CREATE);
    }

    //斷開服務(wù)
    private void releaseService() {
        unbindService(mConnection);
        mConnection = null;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseService();
    }
}
  • 客戶端
    遠(yuǎn)程方法比較耗時會導(dǎo)致客戶端線程阻塞,所以避免在客戶端UI線程中訪問遠(yuǎn)程方法。(onServiceConnected和onServiceDisconnected都運行在UI線程中,所以也不可以直接調(diào)用服務(wù)端耗時操作。)
  • 服務(wù)端
    服務(wù)端本身運行在服務(wù)端的Binder線程池中,所以服務(wù)端本身就可以執(zhí)行大量耗時操作,不要在服務(wù)端重開線程執(zhí)行異步任務(wù)

RemoteCallbackList

系統(tǒng)專門提供用于刪除跨進(jìn)程listener的接口,是一個泛型,支持管理任意的AIDL接口。

public class RemoteCallbackList<E extends IInterface> 
  • 工作原理
    內(nèi)部有一個Map結(jié)構(gòu)用來保存所有的AIDL回調(diào)(key:Ibinder,value:Callback)Callback中封裝了真正的遠(yuǎn)程listener,客戶端注冊listener時,將listener信息存入mCallbacks.

  • 功能

    • 多次跨進(jìn)程傳輸客戶端的同一個對象會在服務(wù)端生成不同對象,但這些對象底層的Binder對象是同一個。客戶端解注冊時,遍歷服務(wù)端所有l(wèi)istener,找到將其刪掉。
  • 客戶端進(jìn)程終止后,可以自動移除客戶端注冊的listener

  • 內(nèi)部實現(xiàn)了線程同步,注冊和解注冊時,不需要做額外的線程同步工作。

  • 遍歷RemoteCallbackList

RemoteCallbackList不是一個List

mListenerList.beginBroadcast()mListenerList.finishBroadcast必須配對使用


權(quán)限驗證

  • 在onBind中進(jìn)行驗證,失敗返回null
    permission驗證

每個權(quán)限通過 protectionLevel 來標(biāo)識保護(hù)級別:

  • normal : 低風(fēng)險權(quán)限,申請就可以使用,安裝時不需要用戶確認(rèn)

  • dangerous:高風(fēng)險權(quán)限,安裝時需要用戶的確認(rèn)才可使用

  • signature:只有當(dāng)申請權(quán)限的應(yīng)用程序的數(shù)字簽名與聲明此權(quán)限的應(yīng)用程序的數(shù)字簽名相同時(如果是申請系統(tǒng)權(quán)限,則需要與系統(tǒng)簽名相同)才能將權(quán)限授給它

  • signatureOrSystem:簽名相同,或者申請權(quán)限的應(yīng)用為系統(tǒng)應(yīng)用

    AndroidManifest中聲明權(quán)限

    Service的onBind中驗證

內(nèi)部應(yīng)用綁定服務(wù)進(jìn)行聲明權(quán)限即可~

   <uses-permission 
        android:name="com.emma.www.myapplication.permission.ACCESS_BOOK_SERVICE"/>
  • 通過服務(wù)端的onTransact方法中驗證,失敗返回false


AIDL oneway

oneway 主要有兩個特性:異步調(diào)用和串行化處理
異步調(diào)用指應(yīng)用向 binder 驅(qū)動發(fā)送數(shù)據(jù)后不需要掛起線程等待 binder 驅(qū)動的回復(fù),而是直接結(jié)束。像一些系統(tǒng)服務(wù)調(diào)用應(yīng)用進(jìn)程的時候就會使用 oneway,比如 AMS 調(diào)用應(yīng)用進(jìn)程啟動 Activity,這樣就算應(yīng)用進(jìn)程中做了耗時的任務(wù),也不會阻塞系統(tǒng)服務(wù)的運行。

binder 協(xié)議

非oneway

如果是 oneway 的話,客戶端就不需要掛起線程等待:


涉及到的 binder 命令也有規(guī)律,由外部發(fā)送給 binder 驅(qū)動的都是 BC_ 開頭,由 binder 驅(qū)動發(fā)往外部的都是 BR_開頭。

怎么理解客戶端線程掛起等待呢?有沒有實際占用 CPU 的調(diào)度?

這里的掛起相當(dāng)于 Thread 的 sleep,是真正的"休眠",底層調(diào)用的是 waiteventinterruptible() Linux 系統(tǒng)函數(shù)。

waiteventinterruptible函數(shù)

Handle 中最關(guān)鍵的地方就是 Looper 的阻塞與喚醒,阻塞是調(diào)用了 nativePollOnce() 方法,當(dāng)時對它的底層實現(xiàn)感興趣,就去了解了一下,也學(xué)習(xí)到 Linux 用來實現(xiàn)阻塞/喚醒的 select、poll 和 epoll 機(jī)制。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • Android開發(fā)藝術(shù)探索 第二章IPC機(jī)制 Linux中IPC通信方式?答:命名管道,共享內(nèi)存,信號量(具體再細(xì)...
    方木Rudy閱讀 1,117評論 0 2
  • 一、Android IPC簡介 IPC是Inter-Process Communication的縮寫,含義就是進(jìn)程...
    SeanMa閱讀 1,913評論 0 8
  • 2.1 Android IPC機(jī)制任何一個操作系統(tǒng)都需要IPC機(jī)制,Linux可以通過共享內(nèi)存,管道,信號量來進(jìn)行...
    shuixingge閱讀 1,714評論 0 3
  • 故事不知道怎么開頭 三個多月了,我們每次星期天的聊天我的小哥哥就只跟我講好的一面,過的...
    哈哈小哥哥閱讀 220評論 0 0
  • 夢里是張熟悉的臉 那溝壑被褐色填滿 那是一雙褐色的眼光 教科書被我的眼淚揉碎 快樂的人在唱永恒的歌 只有滿天的雪化...
    柳橙芝閱讀 679評論 27 12