Android之 從AIDL說說Binder

請(qǐng)尊重原創(chuàng),轉(zhuǎn)載請(qǐng)注明出處【tianyl】的博客

前言

說到Android,有一個(gè)繞不過去的知識(shí)點(diǎn)就是IPC,也叫進(jìn)程間通信,鑒于市面上已經(jīng)很多Binder的深入解析,這里我也不說重復(fù)的話了,感覺從AIDl的角度聊聊BInder(因?yàn)槲铱春孟窈苌儆胁┛蛷倪@個(gè)角度切入)

本文概要

1 什么是Binder
2 什么Android要使用Binder作為IPC的方式?Linux現(xiàn)有的IPC方式不能用嗎
3 什么是AIDL,AIDL和BInder又是什么關(guān)系
4 AIDL的基礎(chǔ)用法
5 什么是Stub和Proxy,它們又是如何通信的?

1 什么是Binder

Binder是Android系統(tǒng)進(jìn)程間通信(IPC)方式之一,說到IPC,就要介紹一些Linux中的IPC方式

2 Linux的IPC方式

  • 管道(Pipe)
    • 一個(gè)線性的內(nèi)存區(qū)域,存消息時(shí)將數(shù)據(jù)寫入管道,取消息時(shí)從管道拷貝數(shù)據(jù),因?yàn)橛袃纱慰截悾ㄐ实停?/li>
  • 插口(Socket)
    • 是一個(gè)通用接口,導(dǎo)致其傳輸效率低,開銷大,有兩次拷貝(效率低)
  • 報(bào)文隊(duì)列(Message)
    • 有兩次拷貝(效率低)
  • 共享內(nèi)存(Share Memory)
    • 機(jī)制復(fù)雜,管理內(nèi)存機(jī)制復(fù)雜
  • 信號(hào)量(Semaphore)
  • 信號(hào)(Signal)
  • 跟蹤(Trace)

3 Binder

3.1 Binder的優(yōu)點(diǎn)

  • 安全性
    • Linux的IPC機(jī)制在本身的實(shí)現(xiàn)中,并沒有安全措施,得依賴上層協(xié)議來進(jìn)行安全控制。而Binder機(jī)制的UID/PID是由Binder機(jī)制本身在內(nèi)核空間添加身份標(biāo)識(shí),安全性高
  • 私有管道
    • Binder可以建立私有通道,這是linux的通信機(jī)制所無法實(shí)現(xiàn)的(Linux訪問的接入點(diǎn)是開放的)
  • 效率性
    • LInux原有的IPC方式,要么是需要多次拷貝(2次或2次以上),要么則是機(jī)制復(fù)雜,而BInder只需要一次拷貝,在多線程時(shí)管理內(nèi)存也相對(duì)容易

所以從效率上和安全性上考慮,Google摒棄了LInux的IPC方式,重新構(gòu)建了一套屬于Android的IPC方式——BInder

4 Binder和AIDL

AIDL全稱:Android Interface Definition Language,Binder是IPC的機(jī)制,AIDL是Binder的具體規(guī)范

5 AIDL詳解

關(guān)于AIDL,它是Android Interface Definition Language,對(duì)于一個(gè).aild文件,它經(jīng)過IDE的處理后,會(huì)生成一個(gè)java文件

這個(gè)java類會(huì)實(shí)現(xiàn)android.os.IInterface接口,并且含有一個(gè)Stub的靜態(tài)抽象內(nèi)部類

public interface IAIDLTest extends android.os.IInterface

5.1 Stub

仔細(xì)查看抽象類Stub,它繼承了android.os.Binder,并實(shí)現(xiàn)了我們定義的AIDL接口

public static abstract class Stub extends android.os.Binder implements com.demo.tianyl.demo.IAIDLTest

5.2 Proxy

Proxy是抽象類Stub中的一個(gè)靜態(tài)內(nèi)部類,它也實(shí)現(xiàn)了我們定義的AIDL接口

private static class Proxy implements com.demo.tianyl.demo.IAIDLTest

5.3 Stub和Proxy

介紹完了Stub和Proxy的類的定義,下面來說說AIDL使用時(shí)它們之間的關(guān)系

1

首先要從我們定義的AIDL說起了,如果我們要使用AIDL,那么我們首先需要定義一個(gè)AIDL接口文件(IAidlTest.aidl),根據(jù)這個(gè).aidl文件自動(dòng)生成一個(gè).java文件,并且構(gòu)建一個(gè)Service實(shí)現(xiàn)我們定義的AIDL接口(AidlTestService.java),并且在manifest中注冊(cè)這個(gè)服務(wù)

2

在其他的進(jìn)程,需要使用AIDL的地方,調(diào)用bindService開啟這個(gè)服務(wù),然后在ServiceConnection中處理返回的IBinder對(duì)象

//開啟服務(wù)
bindService(service, mConnection, Context.BIND_AUTO_CREATE);

//處理IBinder對(duì)象
IAIDLTest mService = null
private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mService = IAIDLTest.Stub.asInterface(service)
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
        mService = null
    }
};

3

說完了用法,下面來解析

首先在bindService中,獲得我們?cè)贏idlTestService.java(aidl實(shí)現(xiàn)類)中的onBind方法中返回的IBinder

實(shí)現(xiàn)類一般寫法如下

public class AidlTestService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    IAIDLTest.Stub mBinder = new IAIDLTest.Stub() {
        //do something
    };
}

4

然后在ServiceConnection中,處理上面返回的IBinder

mService = IAIDLTest.Stub.asInterface(service)

看內(nèi)部類Stub中的方法asInterface

public static com.demo.tianyl.demo.IAIDLTest asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.demo.tianyl.demo.IAIDLTest))) {
        return ((com.demo.tianyl.demo.IAIDLTest) iin);
    }
    return new com.demo.tianyl.demo.IAIDLTest.Stub.Proxy(obj);
}

asInterface方法會(huì)根據(jù)當(dāng)前的進(jìn)程還決定返回Stub或者Proxy

  • 如果當(dāng)前進(jìn)程和AIDL定義的進(jìn)程相同,就返回Stub的實(shí)現(xiàn)類(AidlTestService.java)
  • 如果當(dāng)前進(jìn)程和AIDL定義的進(jìn)程不同,就返回Proxy

所以我們可以猜到,AidlTestService.java是Stub的真實(shí)實(shí)現(xiàn)類,Proxy是Stub在其他進(jìn)程的代理實(shí)現(xiàn)類

5.4 Stub和Proxy的通信

說完了Stub和Proxy的關(guān)系,再說說它們是如何通信的

既然Proxy是Stub在其他進(jìn)程的實(shí)現(xiàn)類,那么其他進(jìn)程在調(diào)用AIDL接口中的方法時(shí),肯定是通過Proxy進(jìn)行的,例如一個(gè)求和方法add

1

Proxy中的方法如下

@Override
public int add(int x, int y) throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    int _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        _data.writeInt(x);
        _data.writeInt(y);
        mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readInt();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

這個(gè)方法主要做了2件事

  1. 序列化參數(shù)x和y
  2. 調(diào)用mRemote.transact方法

其中Stub.TRANSACTION_add是定義的一個(gè)標(biāo)準(zhǔn)常量,用于確定調(diào)用的是哪個(gè)方法

static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

2

從mRemote的transact,會(huì)回調(diào)到Stub的onTransact,在這個(gè)方法中,會(huì)有

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
    java.lang.String descriptor = DESCRIPTOR;
    switch (code) {
        case INTERFACE_TRANSACTION: {
            reply.writeString(descriptor);
            return true;
        }
        case TRANSACTION_add: {
            data.enforceInterface(descriptor);
            int _arg0;
            _arg0 = data.readInt();
            int _arg1;
            _arg1 = data.readInt();
            int _result = this.add(_arg0, _arg1);
            reply.writeNoException();
            reply.writeInt(_result);
            return true;
        }
        default: {
            return super.onTransact(code, data, reply, flags);
        }
    }
}

在TRANSACTION_add分支中,會(huì)將參數(shù)反序列化,然后調(diào)用this.add(_arg0, _arg1),就是它的實(shí)現(xiàn)類,然后將結(jié)果回傳回去

6 總結(jié)

關(guān)于AIDL和Binder的相關(guān)描述,到此就結(jié)束了,因?yàn)榭紤]到已經(jīng)有不少源碼分析的博客,所以這里就較少的涉及源碼方面,想必開篇的問題,大家心中已經(jīng)有答案了吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容