Android Review - Binder機(jī)制(二)

前一篇我們從理論上來了解Android的Binder機(jī)制,本篇從實(shí)戰(zhàn)來深入了解Android的Binder機(jī)制。

Binder的使用

Binder依賴于Service,在組件(Activity)中通過bindService(),就可以獲取Service中的Binder對象,實(shí)現(xiàn)Service與Activity的通信。

服務(wù)分為本地服務(wù)和遠(yuǎn)程服務(wù),都可以使用Binder。

一:本地服務(wù)
在本地服務(wù)中使用Binder,只需要兩步:

  1. 聲明一個(gè)Service,重寫onBind(),返回一個(gè)繼承Binder的自定義對象;
  2. 聲明一個(gè)ServiceConnection,重寫onServiceConnected(),獲取IBinder對象,然后調(diào)用Activity的bindService();

聲明Service:

class ServiceWithBinder : Service() {

    private val TAG = "ServiceWithBinder"

    class InnerBinder : Binder() {

        @Throws(RemoteException::class)
        fun multiply(x: Int, y: Int): Int {
            return x * y
        }

        @Throws(RemoteException::class)
        fun divide(x: Int, y: Int): Int {
            return x / y
        }
    }

    override fun onBind(t: Intent): IBinder? {
        Log.e(TAG, "onBind")
        return InnerBinder()
    }
}

在Activity中綁定服務(wù):

fun myBindService() {
   val mServiceConnBinder = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName) {
            Log.e(TAG, "onServiceDisconnected Binder")
            binderInterface = null
        }

        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            Log.e(TAG, "onServiceConnected Binder")
            binderInterface = service as ServiceWithBinder.InnerBinder
            //調(diào)用服務(wù)的方法
            binderInterface?.multiply(3,2)
        }
    }
    val intentBinder = Intent(this, ServiceWithBinder::class.java)
    //此時(shí)使用bindService開啟服務(wù)
   bindService(intentBinder, mServiceConnBinder, Context.BIND_AUTO_CREATE)
    //銷毀服務(wù)
   unbindService(mServiceConnBinder)
}

二:遠(yuǎn)程服務(wù)
在遠(yuǎn)程服務(wù)中使用Binder,需要三步:創(chuàng)建aidl、聲明Service、調(diào)用bindService。

1.創(chuàng)建aidl
aidl是進(jìn)程間通信接口,需要在客戶端(聲明Service的應(yīng)用)和服務(wù)端(調(diào)用Service的應(yīng)用)同時(shí)聲明,并且要完全一致。

首先,在服務(wù)端端創(chuàng)建aidl:選中項(xiàng)目目錄->右鍵選中new->然后選中AIDL->填寫文件名(比如:ICalcAIDL)->修改aidl的代碼。

// ICalcAIDL.aidl
package com.pmm.demo.advanced;

// Declare any non-default types here with import statements
interface ICalcAIDL {
    int add(int x , int y);
    int min(int x , int y );
}

然后,在客戶端創(chuàng)建aidl,步驟同上??蛻舳说腶idl要與服務(wù)端的一模一樣,包括包名、類名、方法。

然后,選中Build->Make Project,編譯整個(gè)工程,就會在build/generated/source/aidl/debug目錄下生產(chǎn)一個(gè)名為ICalcAIDL的接口。

2.聲明Service
在服務(wù)端,聲明一個(gè)類(比如:MyStub)繼承ICalcAIDL.Stub(ICalcAIDL的代理類,系統(tǒng)幫我們生成的),然后聲明一個(gè)繼承Service的類,在onBind()中返回MyStub對象。

class ServiceWithAIDL : Service() {

    private val TAG = "ServiceWithAIDL"

    private val mBinder = object : ICalcAIDL.Stub() {

        @Throws(RemoteException::class)
        override fun add(x: Int, y: Int): Int {
            return x + y
        }

        @Throws(RemoteException::class)
        override fun min(x: Int, y: Int): Int {
            return x - y
        }
    }

    override fun onBind(t: Intent): IBinder? {
        Log.e(TAG, "onBind")
        return mBinder
    }
}

3.調(diào)用bindService
在客戶端,聲明一個(gè)匿名內(nèi)部類ServiceConnection,重寫onServiceConnected(),通過ICalcAIDL.Stub.asInterface(iBinder)獲取服務(wù)端的ICalcAIDL對象。

fun myBindService() {
   val mServiceConnAIDL = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName) {
            Log.e(TAG, "onServiceDisconnected AIDL")
            mCalcAidl = null
        }

        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            Log.e(TAG, "onServiceConnected AIDL")
            mCalcAidl = ICalcAIDL.Stub.asInterface(service)
           //調(diào)用服務(wù)的方法
            mCalcAidl?.add(3, 2)
        }
    }
    val intentBinder = Intent(this, ServiceWithAIDL::class.java)
    //此時(shí)使用bindService開啟服務(wù)
   bindService(intentBinder, mServiceConnAIDL, Context.BIND_AUTO_CREATE)
    //銷毀服務(wù)
    unbindService(mServiceConnAIDL)
}

AIDL機(jī)制

aidl是進(jìn)程間通信接口,它不是Java的類,更像一種協(xié)議。它的作用是讓AS自動生成Binder類,供客戶端調(diào)用。

aidl文件編譯后,會生成一個(gè)Java接口,分為3層:ICalcAIDL、Stub、Proxy。

package com.pmm.demo.advanced;

public interface ICalcAIDL extends android.os.IInterface {
   //具體的Binder類
    public static abstract class Stub extends android.os.Binder implements com.pmm.demo.advanced.ICalcAIDL {
        ·······
           //代理類
        private static class Proxy implements com.pmm.demo.advanced.ICalcAIDL {
            ·······
        }
    }

    //加
    public int add(int x, int y) throws android.os.RemoteException;
    //減
    public int min(int x, int y) throws android.os.RemoteException;
}

下面是生成類ICalcAIDL的結(jié)構(gòu)圖

ICalcAIDL的結(jié)構(gòu)圖

1.ICalcAIDL
ICalcAIDL就是aidl文件中聲明的接口,也就是我們要給客戶端調(diào)用的功能。

2.Sub
Stub是一個(gè)使用ICalcAIDL裝飾的Binder類,是我們實(shí)際傳遞給客戶端的對象。通過Stub,客戶端就可以調(diào)用ICalcAIDL的方法。

首先是DESCRIPTOR,默認(rèn)值是包名+類名,作用是在IBinder中查找ICalcAIDL接口。

private static final java.lang.String DESCRIPTOR = "com.pmm.demo.advanced.ICalcAIDL";

然后是asInterface(),作用是返回Proxy對象,也就是把ICalcAIDL對象返回給客戶端。我們注意到一般obj都是對應(yīng)服務(wù)的指引,然后傳遞給Proxy對象。

public static com.pmm.demo.advanced.ICalcAIDL asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.pmm.demo.advanced.ICalcAIDL))) {
        return ((com.pmm.demo.advanced.ICalcAIDL) iin);
    }
    //返回Proxy對象
    return new com.pmm.demo.advanced.ICalcAIDL.Stub.Proxy(obj);
}

然后是onTransact(),作用是處理客戶端的請求,并將處理結(jié)果返回給客戶端。

@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) {
        //用來指定DESCRIPTOR,從而確定需要通訊的接口
        case INTERFACE_TRANSACTION: {
            reply.writeString(descriptor);
            return true;
        }
        //用來處理ICalcAIDL的add()
        case TRANSACTION_add: {
            //說明data正準(zhǔn)備給指定DESCRIPTOR的接口使用
            data.enforceInterface(descriptor);
            int _arg0;
            _arg0 = data.readInt();
            int _arg1;
            _arg1 = data.readInt();
            int _result = this.add(_arg0, _arg1);
            //說明當(dāng)前操作沒有出現(xiàn)異常
            reply.writeNoException();
            reply.writeInt(_result);
            return true;
        }
         //用來處理ICalcAIDL的min()
        case TRANSACTION_min: {
            data.enforceInterface(descriptor);
            int _arg0;
            _arg0 = data.readInt();
            int _arg1;
            _arg1 = data.readInt();
            int _result = this.min(_arg0, _arg1);
            reply.writeNoException();
            reply.writeInt(_result);
            return true;
        }
        default: {
            return super.onTransact(code, data, reply, flags);
        }
    }
}

3.Proxy
Proxy,顧名思義,就是ICalcAIDL的代理類,用來實(shí)現(xiàn)ICalcAIDL的方法。Proxy的作用是將需要傳遞的參數(shù)轉(zhuǎn)化為Parcel,從而跨進(jìn)程傳輸。

我們來看下核心代碼,只顯示add(),不顯示min方法。

private static class Proxy implements com.pmm.demo.advanced.ICalcAIDL {
    private android.os.IBinder mRemote;

    Proxy(android.os.IBinder remote) {
        mRemote = remote;
    }

    @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 {
            //用來標(biāo)識_data是給含有DESCRIPTOR標(biāo)志的Binder接口的參數(shù)
            _data.writeInterfaceToken(DESCRIPTOR);
            _data.writeInt(x);
            _data.writeInt(y);
            //調(diào)用Stub的transact,處理add請求
            mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
            _reply.readException();
            //獲取Stub的處理結(jié)果
            _result = _reply.readInt();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
}

我們可以看到,前面asInterface(obj)傳遞進(jìn)來的對象,是我們代理里真正的執(zhí)行對象。比如:mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);

AIDL其實(shí)通過我們寫的aidl文件,幫助我們生成了一個(gè)接口,一個(gè)Stub類用于服務(wù)端,一個(gè)Proxy類用于客戶端調(diào)用。

客戶端使用AIDL接口的asInterface()獲取對應(yīng)的代理,代理調(diào)用對應(yīng)的方法,方法里都有mRemote.transact()進(jìn)行具體的調(diào)用發(fā)消息給服務(wù)器,最后服務(wù)端對應(yīng)Binder對象中的onTransact()來處理客戶端的請求,并將處理結(jié)果返回給客戶端。

PS:本文整理自以下博客
安卓移動架構(gòu)07-Binder核心機(jī)
Android aidl Binder框架淺析

若有發(fā)現(xiàn)問題請致郵 caoyanglee92@gmail.com

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

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