經過AIDL手擼Binder實現IPC

1.什麼是Binder

  1. 直觀來講,Binder是Android中的一個類,它繼承了IBinder接口
  2. 從IPC角度來講,Binder是Android中的一種跨進程通訊方式,Binder還能夠理解爲一種虛擬的物理設備,它的設備驅動是/dev/binder,該通訊方式在linux中沒有
  3. 從Android Framework角度來講,Binder是ServiceManager鏈接各類Manager(ActivityManager、WindowManager...)和相應ManagerService的橋樑
  4. 從Android應用層來講,Binder是客戶端和服務端進行通訊的媒介,當你bindService的時候,服務端會返回一個包含了服務端業務調用的Binder對象,經過這個Binder對象,客戶端就能夠獲取服務端提供的服務或者數據,這裏的服務包括普通服務和基於AIDL的服務

這裏就不對Binder不作深刻的探究了,這篇文章主要是經過對AIDL分析知道AIDL是如何進行IPC的,那麼讓咱們如今透過現象看本質。java

2.AIDL的本質是什麼

關於Service你須要知道這些 一文中有提到過在編譯AIDL文件後會生成一個IxxxAIDL.java的編譯文件,真正實現IPC的實際上是這個文件,若是不相信你大能夠在服務端和客戶端只保留這個文件,同樣能夠實現IPC。那照這麼說咱們寫一大堆AIDL文件只是爲了生成這文件麼?沒錯,AIDL只是在簡化咱們寫這個.java 文件的工做而已,其實這個.java文件就是一個Binder類,爲何說是Binder類下面這個.java類的源碼一擺你就會明白的。因此AIDL說到底本質就是:系統提供的一個快速實現Binder的工具而已。因此不要對AIDL有什麼誤會,真正實現IPC的實際上是Binder。linux

3.透過現象看本質

我仍是拿 關於Service你須要知道這些 這篇文章裏生成的IMyAidl.java來講吧,下面是它的源碼,爲何說這個類是一個Binder類應該明白了吧,由於繼承了Binder類。其中有最重要的兩個方法是transact()和onTransact()。第一個方法使你能夠向遠端的IBinder對象發送發出的調用,第二個方法使你的遠程對象可以響應接收到的調用。android

package com.example.com.test;
// Declare any non-default types here with import statements

public interface IMyAidl extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
     public static abstract class Stub extends android.os.Binder implements com.example.com.test.IMyAidl {
        private static final java.lang.String DESCRIPTOR = "com.example.com.test.IMyAidl";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.example.com.test.IMyAidl interface,
         * generating a proxy if needed.
         */
        public static com.example.com.test.IMyAidl asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.com.test.IMyAidl))) {
                return ((com.example.com.test.IMyAidl) iin);
            }
            return new com.example.com.test.IMyAidl.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @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_getPerson: {
                    data.enforceInterface(descriptor);
                    String _result = this.getPerson();
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        reply.writeString(_result);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.example.com.test.IMyAidl {
            private android.os.IBinder mRemote;

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

            @Override
            public android.os.IBinder asBinder() {
                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 String getPerson() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = _reply.readString();
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public String getPerson() throws android.os.RemoteException;
}
複製代碼

咱們都知道客戶端的IMyAidl在下面的代碼中被賦值的。bash

public void onServiceConnected(ComponentName name, IBinder service) {
    mAidl = IMyAidl.Stub.asInterface(service);
}
複製代碼

那麼咱們點進去IMyAidl中看看IMyAidl.Stub.asInterface()這個方法究竟都作了什麼事。dom

public static com.example.com.test.IMyAidl asInterface(android.os.IBinder obj) {
    //判空
    if ((obj == null)) {
        return null;
    }
    //經過DESCRIPTOR = "com.example.com.test.IMyAidl",查詢在本地是否已經有可用的對象了,若是有就將其返回
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.example.com.test.IMyAidl))) {
        return ((com.example.com.test.IMyAidl) iin);
    }
    //若是本地沒有的話就新建一個返回
    return new com.example.com.test.IMyAidl.Stub.Proxy(obj);
}
複製代碼

經過上面源碼咱們知道起到關鍵性做用的是返回的什麼對象,在這裏就不作深究了,只須要知道:同進程時,返回的是Stub對象,其實就是在onBind中返回的mBinder。跨進程時,返回的是Stub.Proxy對象。因此咱們只須要關注Stub.Proxy這個對象幹了什麼。ide

private static class Proxy implements com.example.com.test.IMyAidl {
    private android.os.IBinder mRemote;

    Proxy(android.os.IBinder remote) {
        //此處的 remote 就是前面的 IBinder service                
        mRemote = remote;
    }

    @Override
    public android.os.IBinder asBinder() {
        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 String getPerson() throws android.os.RemoteException {
    //_data用來存儲客戶端流向服務端的數據流,
    //_reply用來存儲服務端流回客戶端的數據流
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        String _result;
         try {
            _data.writeInterfaceToken(DESCRIPTOR);
            //調用 transact() 方法將方法id和兩個 Parcel 容器傳過去
            //這是一個比較重要的方法,經過這個transact()方法使你能夠向遠端的IBinder對象發送發出調用
            mRemote.transact(Stub.TRANSACTION_getPerson, _data, _reply, 0);
            _reply.readException();
            //從_reply中取出服務端執行方法的結果
            if ((0 != _reply.readInt())) {
                _result = _reply.readString();
            } else {
                _result = null;
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        //將結果返回
        return _result;
    }
}
複製代碼

上面這段源碼其實很好理解,這就是一個代理類,經過這個代理類生成 _data和 _reply兩個數據流對象,並將傳進來的參數寫入到 _data中,而後mRemote這個對象也就是IBinder service這個對象經過調用transact()方法將他們傳入到服務端中,並請求服務端調用相應的方法,調用這個transact()方法以後,客戶端將會掛起當前線程,等候服務端執行完相關任務後通知並接收返回的 _reply 數據流,並從中取出服務端傳回來的數據。工具

至此,關於客戶端分析就是這些了。下面來看看服務端的流程源碼分析

前面說了客戶端經過調用 transact() 方法將數據和請求發送過去,那麼理所固然的,服務端應當有一個方法來接收這些傳過來的東西,這個方法就是onTransact()。post

@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_getPerson: {
            data.enforceInterface(descriptor);
            //調用 this.getPerson() 方法,在這裏開始執行具體的事務邏輯
            //_result 列表爲調用 getPerson() 方法的返回值
            String _result = this.getPerson();
            reply.writeNoException();
            if ((_result != null)) {
                reply.writeInt(1);
                 //將_result寫入 reply中
                reply.writeString(_result);
            } else {
                reply.writeInt(0);
            }
            return true;
        }
        default: {
             return super.onTransact(code, data, reply, flags);
        }
  }
}
複製代碼

能夠看到,它在接收了客戶端的 transact() 方法傳過來的參數後,就直接進入了一個 switch 選擇:根據傳進來的方法 code 不一樣執行不一樣的操做。能夠看到,根據傳入的TRANSACTION_getPerson直接調用服務端這邊的具體方法getPerson()實現,而後獲取返回值並將其寫入 _reply 流,前面說到調用transact()後,客戶端的線程會被掛起等待服務端執行完相關任務後通知並接收返回的 _reply。這就很好解釋了爲何,客戶端和服務端是經過Binder進行交互的了。ui

4.手擼Binder實現IPC

通過上面的分析,相信對Binder實現IPC有了必定的瞭解了吧。那麼爲了更好的理解Binder如何實現IPC,咱們手擼一個Binder來實現IPC。

新建一個項目做爲客戶端

public class BinderActivity extends Activity {

    private boolean isBound;
    private int result;
    private int num1;
    private int num2;
    private IBinder mService;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            isBound = true;
            mService = service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
        }
    };


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_binder);
        ButterKnife.bind(this);

    }

    @OnClick(R.id.tv)
    public void onViewClicked() {
        if (isBound) {
            result = getTotal(mService);
            if (num1 + num2 == result) {
                Toast.makeText(this, num1 + " + " + num2 + " = " + result, Toast.LENGTH_SHORT).show();
            }else {
                Toast.makeText(this, "肯定你沒算錯?", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private int getTotal(IBinder service){
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        Random random = new Random();
        num1 = random.nextInt(10);
        num2 = random.nextInt(10);
        data.writeInt(num1);
        data.writeInt(num2);
        int result = 0;
        try {
            service.transact(1, data, reply, 0);
            result = reply.readInt();
        } catch (RemoteException e) {
            e.printStackTrace();
        } finally {
            data.recycle();
            reply.recycle();
        }
        return result;
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent();
        intent.setAction("com.example.com.test.service.ipc");
        intent.setPackage("com.example.com.test");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (isBound) {
            unbindService(mConnection);
            isBound = false;
        }
    }
}
複製代碼

能夠看到,上面的getTotal()是否是很熟悉了,沒錯就是上面源碼分析的客戶端部分,在這裏咱們能夠看到,我將num1和num2寫入到 _data中,而後調用transact()方法,將參數傳給服務端,等待服務端操做完後,再讀取 _reply。

新建一個項目做爲服務端

public class BinderService extends Service {

    private IBinder mIBinder = new Binder(){
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            if (code == 1){
                int num1 = data.readInt();
                int num2 = data.readInt();
                reply.writeInt(num1+num2);
                return true;
            }
            return super.onTransact(code, data, reply, flags);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mIBinder;
    }
}
複製代碼

能夠看到在服務端的onTransact()中,根據transact()方法傳入的code進行操做,讀取 _data的數據通過計算將值寫入 _reply中。

總結:客戶端經過調用transact()將相關參數傳給服務端,並將當前線程掛起,等待服務端操做。服務端根據客戶端傳進來的code調用服務端的具體方法實現,獲取返回值寫入_reply流中(服務端onTransact()其實就是讀取 _data流的數據,通過必定操做寫入 _reply流中),服務端操做完後,客戶端的線程收到通知,讀取 _reply流的數據並返回(客戶端transact()其實就是將參數寫入 _data流,通過必定操做讀取 _reply流)。 經過一張圖來幫助你理解

看到這裏相信你能理解Binder實現IPC了,話很少說上結果。

運行結果:

相關文章
相關標籤/搜索