Android : 跟我學Binder --- (2) AIDL分析及手動實現



目錄:

 

 

一、關於Android日常開發中進程間通信-AIDL

  通常Android應用開發中實現進程間通信用的最多的就是 AIDL,借助 AIDL 編譯以后的代碼能幫助我們進一步理解 Binder IPC 的通信原理。但是無論是從可讀性還是可理解性上來看,編譯器生成的代碼對開發者並不友好。比如一個 INanoMethod.aidl 文件對應會生成一個 INanoMethod.java 文件,這個 java 文件包含了一個 INanoMethod接口、一個 Stub 靜態的抽象類和一個 Proxy 靜態類。Proxy 是 Stub 的靜態內部類,Stub 又是 INanoMethod的靜態內部類,這就造成了可讀性和可理解性的問題。

  INanoMethod.aidl 內容:

package com.android.NanoServer;
import com.android.NanoServer.INanoMethodCallback;
// Declare any non-default types here with import statements

interface INanoMethod {
    /**
     * 注冊INanoVoiceCallback相關回調接口.
     *
     * @param INanoVoiceCallback 回調接口
     */
    void registerCallBack( INanoMethodCallback callback);
    /**
     * 解除INanoVoiceCallback相關回調接口.
     *
     * @param INanoVoiceCallback 回調接口
     */
    void unregisterCallBack(INanoMethodCallback callback);
    /**
     * 保存Activity對象.
     */
    void registerActivity();

}

  通過編譯器生產的 INanoMethod.java:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\work\\Android\\project\\androidStudio\\BLE-GATT-\\aidl\\src\\main\\aidl\\com\\android\\NanoServer\\INanoMethod.aidl
 */
package com.android.NanoServer;
// Declare any non-default types here with import statements

public interface INanoMethod extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.android.NanoServer.INanoMethod
{
private static final java.lang.String DESCRIPTOR = "com.android.NanoServer.INanoMethod";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.android.NanoServer.INanoMethod interface,
 * generating a proxy if needed.
 */
public static com.android.NanoServer.INanoMethod asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.android.NanoServer.INanoMethod))) {
return ((com.android.NanoServer.INanoMethod)iin);
}
return new com.android.NanoServer.INanoMethod.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_registerCallBack:
{
data.enforceInterface(descriptor);
com.android.NanoServer.INanoMethodCallback _arg0;
_arg0 = com.android.NanoServer.INanoMethodCallback.Stub.asInterface(data.readStrongBinder());
this.registerCallBack(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unregisterCallBack:
{
data.enforceInterface(descriptor);
com.android.NanoServer.INanoMethodCallback _arg0;
_arg0 = com.android.NanoServer.INanoMethodCallback.Stub.asInterface(data.readStrongBinder());
this.unregisterCallBack(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_registerActivity:
{
data.enforceInterface(descriptor);
this.registerActivity();
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.android.NanoServer.INanoMethod
{
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;
}
/**
     * 注冊INanoVoiceCallback相關回調接口.
     *
     * @param INanoVoiceCallback 回調接口
     */
@Override public void registerCallBack(com.android.NanoServer.INanoMethodCallback callback) 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.writeStrongBinder((((callback!=null))?(callback.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_registerCallBack, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
/**
     * 解除INanoVoiceCallback相關回調接口.
     *
     * @param INanoVoiceCallback 回調接口
     */
@Override public void unregisterCallBack(com.android.NanoServer.INanoMethodCallback callback) 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.writeStrongBinder((((callback!=null))?(callback.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_unregisterCallBack, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
/**
     * 保存Activity對象.
     */
@Override public void registerActivity() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_registerActivity, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_registerCallBack = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_unregisterCallBack = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_registerActivity = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
/**
     * 注冊INanoVoiceCallback相關回調接口.
     *
     * @param INanoVoiceCallback 回調接口
     */
public void registerCallBack(com.android.NanoServer.INanoMethodCallback callback) throws android.os.RemoteException;
/**
     * 解除INanoVoiceCallback相關回調接口.
     *
     * @param INanoVoiceCallback 回調接口
     */
public void unregisterCallBack(com.android.NanoServer.INanoMethodCallback callback) throws android.os.RemoteException;
/**
     * 保存Activity對象.
     */
public void registerActivity() throws android.os.RemoteException;
}
  • IBinder : IBinder 是一個接口,代表了一種跨進程通信的能力,只要實現了這個接口,這個對象就能跨進程傳輸;

  • IInterface : IInterface 代表的就是 Server 進程對象具備什么樣的能力,即能提供哪些方法,對應的就是 AIDL 文件中定義的接口;

  • Binder : Java 層的 Binder 類,代表的其實就是 Binder 本地對象。BinderProxy 類是 Binder 類的一個內部類,它代表遠程進程的 Binder 對象的本地代理。這兩個類都繼承自 IBinder, 因而都具有跨進程傳輸的能力,實際在跨越進程時 Binder 驅動會自動完成這兩個對象的轉換。

  • Stub : 編寫AIDL的時候,編譯工具會生成一個名為 Stub 的靜態內部類,這個類繼承了 Binder, 說明它是一個 Binder 本地對象,它實現了 IInterface 接口,表明它具有 Server 承諾給 Client 的能力,Stub 是一個抽象類,具體的 IInterface 的相關實現需要開發者自己實現。

編譯工具根據定義的.aidl文件生成的.java代碼雖然難於閱讀,但是Android之所以這樣設計並不無道理,因為當有多個 AIDL 文件的時候把 INanoMethod、Stub、Proxy 放在同一個文件里能有效避免 Stub 和 Proxy 重名的問題。

 

二、手動編寫代碼來實現跨進程調用

   一次跨進程通信必然會涉及到兩個進程,在這個例子中 BookManagerService 作為服務端進程,提供服務;ClientActivity 作為客戶端進程,使用 BookManagerService提供的服務。那么服務端進程具備什么樣的能力?能為客戶端提供什么樣的服務呢?前面介紹過的 IInterface 代表的就是服務端進程具體什么樣的能力,即提供了什么功能的接口。因此需要定義一個 BookManager 接口,BookManager 繼承自 IInterface ,表明服務端具備什么樣的能力:

/**
 * 這個類用來定義服務端 BookManagerService 具備什么樣的能力
 */
public interface BookManager extends IInterface {
  //提供添加書籍接口
void addBook(Book book) throws RemoteException; }

  只定義服務端具備什么樣的能力是不夠的,既然是跨進程調用,那需要實現一個跨進程調用對象 Stub。Stub 繼承自 Binder, 說明它是一個 Binder 本地對象;實現 IInterface 接口,表明具有 Server 承諾給 Client 的能力;Stub 是一個抽象類,具體的 IInterface 的相關實現需要調用方自己實現:

public abstract class Stub extends Binder implements BookManager {

    ...
    
    public static BookManager asInterface(IBinder binder) {
        if (binder == null)
            return null;
        IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
        if (iin != null && iin instanceof BookManager)
            return (BookManager) iin; //如果client和server在同一進程,則返回本地對象
        return new Proxy(binder);  //如果client和server不在同一進程,則創建並返回代理對象
    }

    ...

    @Override
  /**
   *  code:每個方法都有一個 int類型的數字(編號)用來區分
   *   data: 傳過來的數據,其中包含參數,以及類的描述。
   *   reply:傳回的數據,要寫入是否發生了 Exception,以及返回值。
   *   flags:該方法是否有返回值 , 0表示有返回值。
   */
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {

            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;

            case TRANSAVTION_addBook:
                data.enforceInterface(DESCRIPTOR);
                Book arg0 = null;
                if (data.readInt() != 0) {
                    arg0 = Book.CREATOR.createFromParcel(data); 
                }
                this.addBook(arg0); //調用本地.java中實現的方法
                reply.writeNoException();
                return true;

        }
        return super.onTransact(code, data, reply, flags);
    }
...
  static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); //轉換成對應編號
}

  接下來就要實現這個代理類 Proxy 了,既然是代理類自然需要實現 BookManager 接口:

public class Proxy implements BookManager {
    
    ...

    public Proxy(IBinder remote) {
        this.remote = remote;
    }

    @Override
    public void addBook(Book book) throws RemoteException {

        Parcel data = Parcel.obtain();
        Parcel replay = Parcel.obtain();
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            if (book != null) {
                data.writeInt(1);
                book.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            remote.transact(Stub.TRANSAVTION_addBook, data, replay, 0); //調用Stub的onTransact方法中對應函數
            replay.readException();
        } finally {
            replay.recycle();
            data.recycle();
        }
    }

    ...
}

 

  以上關於 Stub 類中重點介紹下 asInterface 和 onTransact

  • asInterface : 當 Client 端在創建和服務端的連接,調用 bindService 時需要創建一個 ServiceConnection 對象作為入參。在 ServiceConnection 的回調方法 onServiceConnected 中 會通過這個 asInterface(IBinder binder) 拿到 BookManager 對象,這個 IBinder 類型的入參 binder 是驅動傳上來的,正如上代碼中看到的一樣,方法中會去調用 binder.queryLocalInterface() 去查找 Binder 本地對象,如果找到了就說明 Client 和 Server 在同一進程,那么這個 binder 本身就是 Binder 本地對象,可以直接使用。否則說明是 binder 是個遠程對象,也就是 BinderProxy,因此需要創建一個代理對象 Proxy,通過這個代理對象來是實現遠程訪問。
  • onTransact : 在 Proxy 中的 addBook() 方法中首先通過 Parcel 將數據序列化,然后調用 remote.transact()。正如前文所述 Proxy 是在 Stub 的 asInterface 中創建,能走到創建 Proxy 這一步就說明 Proxy 構造函數的入參是 BinderProxy,即這里的 remote 是個 BinderProxy 對象。最終通過一系列的函數調用,Client 進程通過系統調用陷入內核態,Client 進程中執行 addBook() 的線程掛起等待返回,驅動完成一系列的操作之后喚醒 Server 進程,調用 Server 進程本地對象的 onTransact(),最終又走到了 Stub 中的 onTransact() 中,onTransact() 根據函數編號調用相關函數,這里要提及一下,在跨進程調用的時候不會傳遞函數名而是傳遞編號來指明要調用哪個函數,如果提供給client端的aidl文件接口數量或者順序和server端的聲明不一樣,則會導致函數調用錯位的問題。

 

    在client端綁定服務及使用接口:

  ......
  /**
* 嘗試與服務端建立連接 */ private void attemptToBindService() { Intent intent = new Intent(); intent.setAction("com.android.aidl.server"); intent.setPackage("com.android.NanoServer"); bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); } private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mBookManagerService = BookManager.Stub.asInterface(service); //獲得代理對象 if (BookManagerService != null) { mBound = true; } } @Override public void onServiceDisconnected(ComponentName name) { mBound = false; } }; ...... //成功綁定服務后便可調用服務端提供的接口 try { if (mBookManagerService != null) {     mBookManagerService.addBook(book); } } catch (RemoteException e) { e.printStackTrace(); }

注:

  • 如果 Client 和 Server 在同一個進程,那么直接就是調用這個方法。
  • 如果是遠程調用,Client 想要調用 Server 的方法就需要通過 Binder 代理來完成,也就是上面的 Proxy。

 

 

-end-

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM