比較淺顯易懂的AIDL

Binder是Android中的一種跨進程通信方式,Android的四大組件、各種Manager 和其對應ManagerService等無不與Binder掛鉤。從Android Framework角度來說,Binder是ServiceManager連接ActivityManager、WindowManager等Manager和他們相應ManagerService的橋樑; 從Android 應用層來說,Binder是客戶端和服務端進行通信的媒介,正如我們在使用Service時bindService時, Service的onBind方法會返回一個包含了服務端業務調用的Binder對象,通過這個Binder對象,客戶端就可以獲取Service中提供的服務或數據,這裏的服務包括普通服務和基於AIDL的服務。

Binder單詞翻譯過來叫“粘合劑”,它的機制是將Client、Server、ServiceManager和Binder驅動粘合起來。其中Client、Server和Service Manager運行在用戶空間,Binder驅動運行在內核空間。Service Manager和Binder驅動已經在Android平臺中實現好,我們在開發過程中只要按照規範實現自己的Client和Server組件就可以了。

瞭解內核空間 和 用戶空間
Linux內核獨立於普通的應用程序,它可以訪問受保護的內存空間,也有訪問底層硬件設備的所有權限。內核和上層的應用程序抽像隔離開,分別稱之爲內核空間和用戶空間。

ServiceManager
ServiceManager是整個Binder IPC通信過程中的守護進程,本身也是一個Binder服務,它的主要就兩個工作就是查詢和註冊Service。

Binder驅動 內核模塊
linux進程間通信有很多種比如信號量、通道、socket等,但是android是用的binder。用戶空間可以通過系統提供的方法調用訪問內核空間,而一個用戶空間想與另外一個用戶空間進行通信的話,就得使用Linux的動態可加載內核模塊機制(Loadable Kernel Module,LKM),因爲Android系統可以通過添加一個內核模塊運行在內核空間,用戶進程之間通過這個模塊作爲橋樑,就可以完成通信了,而這個負責各個用戶進程通過Binder通信的內核模塊程序叫做Binder驅動。

通信過程
我們通過例子來講述Binder的通信過程,當你想給你的朋友打電話時,那麼你就是Client,而你的朋友就是Server,而電話公司就是ServiceManager,它系統裏註冊了很多人的電話號碼。當你在拔打你朋友電話時,若你朋友電話號碼是沒有在電話公司註冊過,就會返回空號的錯誤,也就是Server沒有在ServiceManager中註冊,否則就能拔打電話,在這個拔過電話過程中,是要通過基站來完成接線的,而基站就是Binder驅動。

劃重點 AIDL原理
AIDL是Binder的延伸,我們知道,當新建一個aidl文件後執行編譯後,便會在app\build\generated\source\aidl\debug\目錄下生成對應的同名的類文件。我們在日常開發中,大可不必理會此文件,也正因爲此文件是自動生成和格式錯亂使很多人對其望而止步。其實只要將該Java文件格式化後,就會清晰多了,這個類的結構其實很簡單的。只要讀懂該類,基本上就會明白AIDL的工作原理了。

首先創建一個IMyAidl.aidl文件,內容如下:

interface IMyAidl {
    int sum(int a, int b);
}

在執行編譯後,就會在app\build\generated\source\aidl\debug目錄下生成IMyAidl.java文件。在介紹IMyAidl.java文件前,先來創建 Client端和Server端的調用代碼。

Client端的MainActivity.Java代碼:

public class MainActivity extends Activity {
 
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
 
            IMyAidl myaidl = IMyAidl.Stub.asInterface(iBinder);
            try {
                myaidl.sum(1, 2);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        public void onServiceDisconnected(ComponentName className) {
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}

Server端的MyService.Java代碼:

public class MyService extends Service {
    private Binder mBinder = new IMyAidl.Stub() {
        @Override
        publicint sum(int a, int b) throws RemoteException {
            return a + b;
        }
    };
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

IMyAidl.java內容部分是這樣:

 
public interface IMyAidl extends android.os.IInterface {
    public static abstract class Stub extends android.os.Binder implements com.***.IMyAidl {
        ……
    }
    public void sum(int a, int b) throws android.os.RemoteException;
}

IMyAidl.java文件中,存在一個繼承自己的內部類Stub和我們在aidl中定義的sum方法。Stub就是一個Binder類。可以看出,它就是MyService中onBind方法return的mBinder對象的類。再來細看下Stub類的代碼:

public static abstract class Stub extends android.os.Binder implements com***.IMyAidl {
    private static final java.lang.String DESCRIPTOR = "com.***.IMyAidl";
 
    public Stub() {
        this.attachInterface(this, DESCRIPTOR);
    }
 
    public static ***.IMyAidl asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof ***.IMyAidl))) {
            return ((com.***.IMyAidl) iin);
        }
        return new com.***.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_sum: {
                data.enforceInterface(descriptor);
                int _arg0;
                _arg0 = data.readInt();
                int _arg1;
                _arg1 = data.readInt();
                int _result = this.sum(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                return true;
            }
            default: {
                return super.onTransact(code, data, reply, flags);
            }
        }
    }
 
    private static class Proxy implements com***.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;
        }
 
        @Override
        public int sum(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_sum, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readInt();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    }
 
    static final int TRANSACTION_sum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

下面我們來看看Stub類的變量和方法的含義:

DESCRIPTOR

DESCRIPTOR是Binder的唯一標識,一般用當前Binder的類名錶示

asInterface

asInterface方法用於將Server端的Binder對象轉換成Client端所需的AIDL接口類型的對象,正如我們可以看到asInterface方法就是我們在Client端中代碼:

IMyAidl myaidl = IMyAidl.Stub.asInterface(iBinder);
try {
    myaidl.sum(1, 2);
} catch (Exception e) {
    e.printStackTrace();
}

asInterface方法它的參數是接收由服務綁定成功後返回的一個IBinder對象,返回值一個AIDL接口類型的對象,從代碼中可以發現,該對象有三種情況返回值,第一種因爲參數輸入錯誤所以返回了null; 接着判斷了Client端和Server端是否位於同一進程,如果是同一進程,則是第二情況,返回就是Server端中的Stub對象本身,也就是本示例中就是MyService中的mBinder對象; 如果Client端和Server端不是同一進程,那麼就是第三種情況,返回的是Stub類的內部代理類Proxy對象。而Client端中代碼:myaidl.sum(1, 2);調用的就是Proxy類的sum方法。

Proxy

Proxy類是內部類Stub的內部代理類,同樣也是繼承於IMyAidl.java。它的sum方法會使用Parcelable來準備數據,把參數都寫入_data,讓_reply接收方法返回值。最後使用IBinder的transact方法把數據傳給Binder的Server端去。代碼中mRemote就是asInterface方法接收的參數obj。這時當前線程掛起,Server端的onTransact方法會被調用。

onTransact

onTransact方法是運行在Server中的Binder線程池中的,當Client端發起跨進程請求時,遠程請求會通過系統底層封裝後交由onTransact方法來處理。通過TRANSACTION_sum找到對應的方法sum,接着從data中取出從Client端進程傳遞過來的參數,然後執行目標方法sum,在執行完畢後就向reply中寫入返回值。此時,Proxy中的sum方法線程恢復執行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章