Android的IPC機制(二)——AIDL實現原理簡析

綜述

  上篇說到AIDL的使用方法,我們不能僅僅只是滿足對AIDL的使用,那麼對於AIDL到底是如何實現的呢?爲什麼我們只是創建一個AIDL文件,系統就會爲我們自動生成一個Java文件,那麼這個Java文件裏面到底包含了哪些內容呢?我們今天就來研究一下。

AIDL實現原理

  在這裏我們首先看一下AIDL是怎麼實現的。當我們創建一個Service和一個AIDL接口的時候,然後創建一個Binder對象並在Service中的onBind方法中去返回這個Binder對象到客戶端,客戶端得到這個對象後就可以綁定服務端的Service,並且與服務端建立連接後就可以訪問服務端的方法了。所以在整個AIDL的實現過程中這個Binder是關鍵。那麼這個Binder究竟是什麼呢?在這裏簡要說明一下。
  Binder是一種進程間的通信方式。Binder在Linux 內核中是一個驅動程序(/dev/binder),ServiceManager通過這個Binder驅動連接各種Manager(AvtivityManager,WindowManager等)。在Android的應用層中通過Binder實現Android的RPC(Remote Procedure Call 遠程進程調用)過程。
  以上篇文章中的加法運算爲例分析來一下Binder在應用層的實現機制,我們可以看到在服務端通過new ICalculate.Stub()來創建一個Binder對象,那麼我們找到ICalculate的Java代碼。 也就是系統根據我們的AIDL接口自動生成Java文件。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: G:\\Android\\androidstudioProject\\aidl\\AIDLClient\\app\\src\\main\\aidl\\com\\ljd\\aidl\\ICalculate.aidl
 */
package com.ljd.aidl;
// Declare any non-default types here with import statements

public interface ICalculate extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.ljd.aidl.ICalculate {
        private static final java.lang.String DESCRIPTOR = "com.ljd.aidl.ICalculate";
         /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        /**
         * Cast an IBinder object into an com.ljd.aidl.ICalculate interface,
         * generating a proxy if needed.
         */
        public static com.ljd.aidl.ICalculate asInterface(android.os.IBinder obj) {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.ljd.aidl.ICalculate))) {
                return ((com.ljd.aidl.ICalculate)iin);
            }
            return new com.ljd.aidl.ICalculate.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 {
            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;
                }
            }
             return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.ljd.aidl.ICalculate {
            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 int add(int first, int second) 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(first);
                    _data.writeInt(second);
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    /**
         * Demonstrates some basic types that you can use as parameters
         * and return values in AIDL.
         */
    public int add(int first, int second) throws android.os.RemoteException;
}

  其實這段代碼很有規律可尋的。能夠看出ICalculate 是一個繼承IInterface的接口,IInterface中只聲明瞭一個asBinder接口,用於返回Binder對象。所有在Binder傳輸的接口都必須繼承這個IInterface接口。在ICalculate的java接口中再次聲明瞭AIDL中的方法並且創建了一個繼承Binder的內部抽象類Stub。也就是說這個Stub也就是一個Binder。
  在服務端我們通過new ICalculate.Stub()來創建一個Binder對象,我們來看一下這個Stub裏面的內容。Stub內定義了一個DESCRIPTOR作爲Binder的唯一標識,定義了一個TRANSACTION_add 作爲接口方法的id。下面看一下asInterface()方法。

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

  這個方法是在客戶端被調用,運行在主線程當中,是將服務端返回來的Binder對象轉爲客戶端所需要的接口對象。通過Binder對象的queryLocalInterface方法去查找客戶端進程中是否存在所需要的接口對象。存在的話就直接返回這個接口對象。也就是說這時候客戶端和服務端在同一個進程內。若是不存在,就創建一個Proxy對象。Proxy是Stub的一個內部代理類。我們看一下Proxy裏面到底做了什麼。

private static class Proxy implements com.ljd.aidl.ICalculate {
    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 int add(int first, int second) 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(first);
            _data.writeInt(second);
            mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readInt();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
}

  在跨進程通信中,由於是在asInterface方法中創建了一個Proxy對象,所以,Proxy也是運行在客戶端的主線程中。在Proxy類中實現了ICalculate接口,定義了IBinder接口對象。通過實現asBinder接口返回這個IBinder接口對象。而對於我們自己定義的add接口方法中創建兩個Parcel對象_data和_reply (Parcel可以說是一個存放讀取數據的容器,在Parcel內部包裝了可序列化的數據,並且可以在Binder中自由的傳輸)。我們可以很清晰看出_data對象寫入了我們傳入的參數,而_reply 則是用來存放方法的返回值。緊接着調用了Binder的transact方法。在transact方法方法中將方法id,_data,_reply作爲參數傳入進去。下面看一下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;
}

  我們可以很明顯的看到在transact方法中調用了onTransact方法並返回了一個boolean類型。當返回false的時候客戶端請求就會失敗。在Stub中我們重寫onTransact方法。下面我們看一下onTransact方法。
  

        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_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;
                }
            }
             return super.onTransact(code, data, reply, flags);
        }

  在onTransact方法中我們會根據code來判斷所需要執行的方法,通過data取出所需要的參數,然後執行該方法,若是有返回值則將將返回值寫入reply中。在這裏Parcel對象data和reply讀寫順序要嚴格保持一致。
  可以看出Binder運行的整個流程也就是:當客戶端綁定服務端發起遠程請求,客戶端進程處於休眠,當前線程處於掛起狀態。然後服務端開始執行,執行完畢後將結果返回給客戶端。然後客戶端繼續執行。
  這也說明了當遠程方法是一個很耗時的操作時,我們不應該在主線程中發起請求。而服務端的Binder方法在Binder線程池中運行,所以Binder不論是否耗時都不應該重新爲他在開啓一個線程。
  到這裏AIDL中Binder的工作機制已經分析完了。現在我們可以發現完全不用AIDL文件也能夠實現跨進程的方法調用。那麼我們自己來寫一個Binder去實現服務端與客戶端的跨進程的方法調用。

自定義Binder的實現

實現過程

  在這裏我們創建一個繼承IInterfaceJava接口,在接口裏面我們去聲明算術的加減法運算。

package com.ljd.binder.custombinder;

import android.os.IInterface;

public interface ICalculate extends IInterface {

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

    public int add(int first, int second) throws android.os.RemoteException;

    public int sub(int first, int second) throws android.os.RemoteException;
}

  然後我們再去實現這個接口。

package com.ljd.binder.custombinder;

import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;

/**
 * Created by ljd-PC on 2016/2/21.
 */
public class Calculate extends Binder implements ICalculate{

    private final String TAG = "Calculate";
    private static final String DESCRIPTOR = "com.ljd.binder.ICalculate";

    @Override
    public void attachInterface(IInterface owner, String descriptor) {
        this.attachInterface(this, DESCRIPTOR);
    }
    public static ICalculate asInterface(IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof ICalculate))) {
            return (ICalculate) iin;
        }
        return new Proxy(obj);
    }

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        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;
            }
            case TRANSACTION_sub: {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.sub(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }

    @Override
    public int add(int first, int second) throws RemoteException {
        Log.e(TAG,String.valueOf(first+second));
        return first + second;
    }

    @Override
    public int sub(int first, int second) throws RemoteException {
        return first - second;
    }

    @Override
    public IBinder asBinder() {
        return null;
    }

    private static class Proxy implements ICalculate {
        private IBinder mRemote;

        Proxy(IBinder remote) {
            mRemote = remote;
        }

        @Override
        public IBinder asBinder() {
            return mRemote;
        }

        public String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }

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

        @Override
        public int sub(int first, int second) throws 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(first);
                _data.writeInt(second);
                mRemote.transact(TRANSACTION_sub, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readInt();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    }
}

  在這裏創建一個Service。

package com.ljd.binder.custombinder;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;


public class CalculateService extends Service {
    private Calculate mCalculate;
    public CalculateService() {
        mCalculate = new Calculate();
    }

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

  在上篇文章中說過我們可以通過android:process 屬性爲服務端開啓一個進程。

        <service
            android:name=".custombinder.CalculateService"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.ljd.binder.CUSTOM_BINDER"></action>
                <category android:name="android.intent.category.DEFAULT"></category>
            </intent-filter>
        </service>

  其中:remote是一種簡寫法,全稱爲com.ljd.binder.custombinder:remote。以:開頭的進程是屬於當前應用的私有進程,其他應用的組件不能和他運行在同一個進程中。
  下面是客戶端代碼。

package com.ljd.binder.custombinder;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import com.ljd.binder.*;

import butterknife.ButterKnife;
import butterknife.OnClick;

public class CustomBinderActivity extends AppCompatActivity {


    private final String TAG = "CustomBinderActivity";
    private ICalculate mCalculate;
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if(mCalculate != null){
                mCalculate.asBinder().unlinkToDeath(mDeathRecipient,0);
                mCalculate = null;
                bindService();
            }
        }
    };
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG,"Bind Success");
            mCalculate = Calculate.asInterface(service);
            try {
                mCalculate.asBinder().linkToDeath(mDeathRecipient,0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_binder);
        ButterKnife.bind(this);
        bindService();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ButterKnife.unbind(this);
        unbindService(mConnection);
    }

    @OnClick({R.id.custom_add_btn,R.id.custom_sub_btn})
    public void onClickButton(View v){
        if (mCalculate == null){
            return;
        }
        switch (v.getId()){
            case R.id.custom_add_btn:
                try {
                    Toast.makeText(this,String.valueOf(mCalculate.add(6,2)),Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.custom_sub_btn:
                try {
                    Toast.makeText(this,String.valueOf(mCalculate.sub(6,2)),Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            default:
                break;
        }
    }

    private void bindService(){
        Intent intent = new Intent("com.ljd.binder.CUSTOM_BINDER");
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

}

效果演示

這裏寫圖片描述

總結

  我們現在自己已經實現了跨進程間的調用。而且我們構建的這個Binder幾乎和系統生成的一模一樣。所以AIDL就是對Binder進行了一次封裝,並且能夠支持多線程併發訪問。通過AIDL的使用能夠大大簡化了我們開發過程,節約了我們的開發時間。

源碼下載

發佈了33 篇原創文章 · 獲贊 195 · 訪問量 36萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章