Android進程間通信機制Binder應用層分析

1. 概述

今天學姐復習了下Android進程間通信方式怖现,打算從基本概念琉预、使用原因基本使用姐直、原理分析幾個方面來講講倦淀。

想要對進程間通信方式有個相對全面的了解,就先從幾個比較重要的概念IPC声畏、AIDL撞叽、Binder說起吧。

(1)IPC:Inter-Process Communication插龄,即進程間通信
(2)AIDL:Android Interface Definition Language愿棋,即Android接口定義語言。Client和Service要實現
跨進程通信均牢,必須遵循的接口規(guī)范糠雨。需要創(chuàng)建.aidl文件,外在表現上和Java中的interface有點類似膨处。
(3)Binder:Android進程間通信是通過Binder來實現的见秤。遠程Service在Client綁定服務時,會在onBind()的回調中返回一個Binder真椿,當Client調用bindService()與遠程Service建立連接成功時,會拿到遠程Binder實例乎澄,從而使用遠程Service提供的服務突硝。

2. 為什么使用Binder?

下面內容學姐參考別人的博文,進行了總結置济。

Linux系統(tǒng)進程間通信方式:

(1)傳統(tǒng)機制解恰。如管道(Pipe)锋八、信號(Signal)和跟蹤(Trace),適用于父子進程或兄弟進程护盈,其中命名管道(Named Pipe)挟纱,支持多種類型進程

(2)System V IPC機制。如報文隊列(Message)腐宋、共享內存(Share Memory)和信號量(Semaphore)

(3)Socket通信

然而紊服,以上方式在性能和安全性方面存在不足:

(1)性能方面。管道和隊列采用存儲轉發(fā)方式胸竞,數據從發(fā)送方緩存區(qū)->內核緩存區(qū)->接收方緩存區(qū)欺嗤,會經歷兩次拷貝過程;共享內存無拷貝但控制復雜卫枝;Socket傳輸效率低下煎饼,且連接的建立與中斷有一定開銷。

(2)安全性方面校赤。傳統(tǒng)IPC方式無法獲得對方進程可靠的UID和PID吆玖,無法鑒別對方身份,若采用在數據包里填入UID/PID的方式马篮,容易被惡意程序利用衰伯;傳統(tǒng)IPC方式的接入點是開放性的,無法建立私有通道积蔚,容易被惡意程序猜測出接收方地址意鲸,獲得連接。

Binder是基于C/S通信模式尽爆,傳輸過程只需要一次拷貝怎顾,且為Client添加UID/PID身份,性能和安全性更好漱贱,因此Android進程間通信使用了Binder槐雾。

3. 基本使用

假設本地Client需要使用遠程Service的計算器功能。示例Demo: binderDemo

(1)新建Client和遠程Service兩個Android工程幅狮。

(2)在遠程Service工程中募强,創(chuàng)建ICalculator.aidl文件,并創(chuàng)建CalculatorService類崇摄。

// ICalculator.aidl
package me.wangxinghe.ipc;

// Declare any non-default types here with import statements

interface ICalculator {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    int add(int a, int b);
    int minus(int a, int b);
}

//給Client提供服務的Service文件
public class CalculatorService extends Service {
    public CalculatorService() {
    }

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

    @Override
    public IBinder onBind(Intent intent) {
        //當Client調用bindService()時擎值,遠程Service通過onBind()回調返給Client
        return mBinder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

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

    遠程Service向Client提供的服務,接口方法add()和minus()實現由Service決定
    private ICalculator.Stub mBinder = new ICalculator.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,         
        double aDouble, String aString) throws RemoteException {

        }

        @Override
        public int add(int a, int b) throws RemoteException {
            return a + b;
        }

        @Override
        public int minus(int a, int b) throws RemoteException {
            return a - b;
        }
    };

}

//AndroidManifest中Service組件聲明逐抑,由于是Client工程不能顯示調用遠程Service鸠儿,只能采用隱式調用的方  
  式,因此需要填寫category和action
<service
    android:name=".CalculatorService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT"/>
        <action android:name="com.wangxinghe.ipc_server.CalculatorService"/>
    </intent-filter>
</service>

(3)在Client工程中,拷貝以上.aidl文件及目錄进每,使用Service提供的服務汹粤。

//遠程Service連接回調
private ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //連接成功,獲取遠程Service的Binder
        mCalculator = ICalculator.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mCalculator = null;
    }
};

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.bind_service:
            //綁定服務
            Intent intent = new Intent(ACTION_CALCULATOR_SERVICE);
            bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
            break;
        case R.id.unbind_service:
            //斷開服務
            unbindService(mServiceConnection);
            break;
        case R.id.add:
            //調用遠程服務的add()
            int result = 0;
            try {
                if (mCalculator != null) {
                    result = mCalculator.add(2, 1);
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mResultTv.setText(result + "");
            break;
        case R.id.minus:
            //調用遠程服務的minus()
            int _result = 0;
            try {
                if (mCalculator != null) {
                    _result = mCalculator.minus(2, 1);
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mResultTv.setText(_result + "");
            break;
    }
}

(4)啟動Client和Service工程田晚,即可實現進程間通信嘱兼。

4. 原理分析

由于Binder機制涉及的東西很多。學姐本文并不打算深入到內核源碼贤徒,關于Client是怎樣獲取到遠程Binder的會在后續(xù)文章再講述芹壕。下面主要是從應用層角度分析。

我們先看看ICalculator.aidl編譯生成的ICalculator.java文件泞莉。

package me.wangxinghe.ipc;
// Declare any non-default types here with import statements

public interface ICalculator extends android.os.IInterface
{
    
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements     
    me.wangxinghe.ipc.ICalculator
    {
        //接口描述符
        private static final java.lang.String DESCRIPTOR = "me.wangxinghe.ipc.ICalculator";
        
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            //添加Binder和接口描述符到該Binder中份汗,便于后續(xù)本地查找Binder
            this.attachInterface(this, DESCRIPTOR);
        }
        
        /**
         * Cast an IBinder object into an me.wangxinghe.ipc.ICalculator interface,
         * generating a proxy if needed.
         */
        public static me.wangxinghe.ipc.ICalculator asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            
            //根據接口描述符邪财,從Binder中查找對應的接口,若有則直接返回
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof me.wangxinghe.ipc.ICalculator))) {
                return ((me.wangxinghe.ipc.ICalculator)iin);
            }
            
            //否則返回接口的代理對象,并將遠端Binder傳入代理
            return new me.wangxinghe.ipc.ICalculator.Stub.Proxy(obj);
        }
        
        @Override 
        public android.os.IBinder asBinder()
        {
            //返回Binder
            return this;
        }
        
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply,                                      
        int flags) throws android.os.RemoteException
        {
            switch (code) 
            {
                case INTERFACE_TRANSACTION:
                {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_basicTypes:
                {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0!=data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_add:
                {
                    //解析data Parcel參數锣光,執(zhí)行具體的加法操作光坝,并將計算結果寫進reply Parcel
                    //add()由Service實現
                    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;
                }
                case TRANSACTION_minus:
                {
                    //解析data Parcel參數罗捎,執(zhí)行具體的減法操作钥平,并將計算結果寫進reply Parcel
                    //minus()由Service實現
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.minus(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
        
        //代理類
        private static class Proxy implements me.wangxinghe.ipc.ICalculator
        {
            //遠程Binder
            private android.os.IBinder mRemote;
            
            Proxy(android.os.IBinder remote)
            {
                mRemote = remote;
            }
            
            @Override
            public android.os.IBinder asBinder()
            {
                //返回遠程Binder
                return mRemote;
            }
            
            public java.lang.String getInterfaceDescriptor()
            {
                //返回接口描述符
                return DESCRIPTOR;
            }
            
            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, 
            double aDouble, java.lang.String aString) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean)?(1):(0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
            
            @Override
            public int add(int a, int b) throws android.os.RemoteException
            {
                //執(zhí)行加法操作,將接口描述符和參數封裝進data Parcel疫剃,調用遠程Binder執(zhí)行加法操作钉疫,
                         //并從reply Parcel中解析出結果,返回結果巢价。該過程是同步操作
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
            
            @Override 
            public int minus(int a, int b) throws android.os.RemoteException
            {
                //執(zhí)行減法操作牲阁,將接口描述符和參數封裝進data Parcel,調用遠程Binder執(zhí)行減法操作壤躲,
                //并從reply Parcel中解析出結果城菊,返回結果。該過程是同步操作
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_minus, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
    
        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 
        0);
        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_minus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double 
    aDouble, java.lang.String aString) throws android.os.RemoteException;

    //接口方法
    public int add(int a, int b) throws android.os.RemoteException;

    //接口方法
    public int minus(int a, int b) throws android.os.RemoteException;
}

基本使用部分碉克,我們可以看到凌唬,Client在與Service建立連接成功后,會拿到遠程Binder實例(此處不能直接使用遠程Binder原因還不是很清楚)漏麦,并調用Stub的asInterface方法將其轉換成ICalculator接口客税。

mCalculator = ICalculator.Stub.asInterface(service);

這一步執(zhí)行的操作是:根據接口描述符,從Binder中本地查找對應的接口撕贞,若有則直接返回更耻;否則,將Binder對象傳給本地代理Stub.Proxy對象麻掸,并返回本地代理酥夭,由本地代理來接管相應的服務,Proxy也實現了ICalculator接口脊奋。

這一步之后熬北,Client就可以使用遠程Service提供的服務了。

再看看onClick()事件中的加法操作诚隙。

result = mCalculator.add(2, 1);

mCalculator即為上面得到的本地代理對象讶隐,其add()的實現是

@Override 
public int add(int a, int b) 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(a);
        _data.writeInt(b);
        mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readInt();
    }
    finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

可以看到實際上是調用連接建立成功后的遠程Binder的transact(add)方法。再看看Binder.transact實現

public final boolean transact(int code, Parcel data, Parcel reply,
        int flags) throws RemoteException {
    if (false) Log.v("Binder", "Transact: " + code + " to " + this);
    if (data != null) {
        data.setDataPosition(0);
    }
    boolean r = onTransact(code, data, reply, flags);
    if (reply != null) {
        reply.setDataPosition(0);
    }
    return r;
}

可以看到最后調用了遠程Binder的onTransact()方法久又,也就是走到了遠程Binder的Stub.onTransact()方法巫延。這個方法實現是

@Override 
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) 
throws android.os.RemoteException
{
    switch (code)
    {
        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;
        }
        ...
    }
}

這里最終調用到了遠程Service工程創(chuàng)建的ICalculator.Stub實例mBinder的add()方法即a+b,因此得到了最后的結果地消。

好了炉峰,我們再來梳理下,Client獲得并使用Service服務的過程:

(1)Client與Service建立連接脉执,得到遠程Binder(這個Binder就是Service的onBind返回的Binder疼阔,但是客戶端不能直接使用,具體原因還不是很明確)半夷。

(2)將遠程Binder傳給本地代理婆廊,得到本地代理Stub.Proxy實例。

(3)通過本地代理Stub.Proxy實例間接調用遠程Binder對象的add()方法巫橄。
具體實現是:由于遠程Binder已經傳給了本地代理Stub.Proxy淘邻。那么通過本地代理Stub.Proxy實例間接調用遠程Binder的transact(TRANSACTION_add)操作;調用遠程Binder的onTransact()方法湘换;調用遠程Binder的實現類ICalculator.Stub的add()方法

(4)得到結果

下面再總結下ICalculator宾舅、StubProxy這3個類的關系:

ICalculator:就是一個接口類彩倚。內部包括之前的接口方法和靜態(tài)Stub類筹我。
Stub:遠程Binder實現類。繼承類BinderICalculator接口署恍。
Proxy:遠程Binder代理類崎溃。實現ICalculator接口。

5. 總結

(1)Binder相當于不同進程間數據通信的通道
(2)核心是代理模式盯质,使用本地代理Proxy操作遠端Binder袁串,使用相應服務
(3)如理解有誤,歡迎指正

6. 參考鏈接

(1)http://blog.csdn.net/singwhatiwanna/article/details/19756201
(2)http://blog.csdn.net/luoshengyang/article/details/6618363

微信公眾號:學姐的IT專欄
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末呼巷,一起剝皮案震驚了整個濱河市囱修,隨后出現的幾起案子,更是在濱河造成了極大的恐慌王悍,老刑警劉巖破镰,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡鲜漩,警方通過查閱死者的電腦和手機源譬,發(fā)現死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孕似,“玉大人踩娘,你說我怎么就攤上這事『砑溃” “怎么了养渴?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長泛烙。 經常有香客問我理卑,道長,這世上最難降的妖魔是什么蔽氨? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任藐唠,我火速辦了婚禮,結果婚禮上孵滞,老公的妹妹穿的比我還像新娘中捆。我一直安慰自己,他們只是感情好坊饶,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布泄伪。 她就那樣靜靜地躺著,像睡著了一般匿级。 火紅的嫁衣襯著肌膚如雪蟋滴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天痘绎,我揣著相機與錄音津函,去河邊找鬼。 笑死孤页,一個胖子當著我的面吹牛尔苦,可吹牛的內容都是我干的。 我是一名探鬼主播行施,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼允坚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蛾号?” 一聲冷哼從身側響起稠项,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鲜结,沒想到半個月后展运,有當地人在樹林里發(fā)現了一具尸體活逆,經...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年拗胜,在試婚紗的時候發(fā)現自己被綠了蔗候。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡挤土,死狀恐怖琴庵,靈堂內的尸體忽然破棺而出误算,到底是詐尸還是另有隱情仰美,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布儿礼,位于F島的核電站咖杂,受9級特大地震影響,放射性物質發(fā)生泄漏蚊夫。R本人自食惡果不足惜诉字,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望知纷。 院中可真熱鬧壤圃,春花似錦、人聲如沸琅轧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乍桂。三九已至冲杀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間睹酌,已是汗流浹背权谁。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留憋沿,地道東北人旺芽。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像辐啄,于是被迫代替她去往敵國和親采章。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內容

  • 原文:http://weishu.me/2016/01/12/binder-index-for-newer/ 要點...
    指尖流逝的青春閱讀 2,608評論 0 13
  • 3.5 Android進程間通信 3.5.1 背景知識 傳統(tǒng)IPC Linux傳統(tǒng)的IPC機制分為如下幾種:管道则披、...
    jianhuih閱讀 5,539評論 1 5
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理共缕,服務發(fā)現,斷路器士复,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 毫不夸張地說图谷,Binder是Android系統(tǒng)中最重要的特性之一翩活;正如其名“粘合劑”所喻,它是系統(tǒng)間各個組件的橋梁...
    weishu閱讀 17,866評論 29 246
  • Android跨進程通信IPC整體內容如下 1便贵、Android跨進程通信IPC之1——Linux基礎2菠镇、Andro...
    隔壁老李頭閱讀 11,885評論 11 56