AIDL是一種跨進程通信的方式,通信基於Binder。
直接繼承Binder也可以實現跨進程通信。
Binder
Binder是一個類,它實現了IBinder接口,而IBinder接口定義了與遠程對象的交互協議。通常在進行跨進程通信時,不需要實現IBinder接口,直接從Binder派生即可。
除了實現IBinder接口外,Binder中還提供了兩個重要的接口。
- transact(),客戶端調用,用於發送調用請求
- onTransact(),服務端響應,用於接收調用請求
因爲以上的原因,Binder成爲了客戶端與服務端的通信媒介,其主要用在Service組件應用中。
AIDL
機制
AIDL的通信基於Binder。
下面寫一個AIDL的demo講解他的通信機制。
新建Book.aidl文件
package com.competition.pdking.ipcdemo;
parcelable Book;
然後創建一個IBookManager.aidl文件
// IBookManager.aidl
package com.competition.pdking.ipcdemo;
// Declare any non-default types here with import statements
import com.competition.pdking.ipcdemo.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
Android會生成一個java文件
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: C:\\Projects\\AndroidProjects\\AndroidDemoProject\\IPCDemo\\app\\src\\main
* \\aidl\\com\\competition\\pdking\\ipcdemo\\IBookManager.aidl
*/
package com.competition.pdking.ipcdemo;
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.competition.pdking.ipcdemo.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.competition.pdking.ipcdemo" +
".IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.competition.pdking.ipcdemo.IBookManager interface,
* generating a proxy if needed.
*/
public static com.competition.pdking.ipcdemo.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.competition.pdking.ipcdemo.IBookManager))) {
return ((com.competition.pdking.ipcdemo.IBookManager) iin);
}
return new com.competition.pdking.ipcdemo.IBookManager.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_getBookList: {
data.enforceInterface(descriptor);
java.util.List<com.competition.pdking.ipcdemo.Book> _result =
this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(descriptor);
com.competition.pdking.ipcdemo.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.competition.pdking.ipcdemo.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.competition.pdking.ipcdemo.IBookManager {
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 java.util.List<com.competition.pdking.ipcdemo.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.competition.pdking.ipcdemo.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.competition.pdking.ipcdemo.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(com.competition.pdking.ipcdemo.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.competition.pdking.ipcdemo.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.competition.pdking.ipcdemo.Book book) throws android.os.RemoteException;
}
-
IBookManager.java這個類他繼承IInterFace接口,同時他也是一個接口,所有可以在Binder中傳輸的接口都需要繼承IInterface接口。
-
首先他聲明瞭getBookList和addBook兩個方法。這顯然就是我們在aidl文件中聲明的方法。同時還聲明瞭兩個id用於標記這兩個方法。
-
然後,他聲明瞭一個內部類Stub,這個類就是一個Binder類,當位於一個進程時,方法調用不會走跨進程的transact過程;當位於不同的進程時,會走transact過程,這個邏輯由Stub的內部類Proxy完成。
然後逐個解釋作用:
- DESCRIPTOR——Binder的唯一標識,用來表示當前Binder的類名錶示。
- asInterface——將服務端的Binder對象轉換爲客戶端所需的AIDL接口類型,這個轉換過程是區分進程的。如果位於同一個進程那麼此方法返回的就是服務端的Stub對象本身,否則返回系統封裝的Stub.Proxy對象。
- asBinder——返回當前Binder對象。
- onTransact——這個方法運行在服務端的Binder線程池中,當客戶端發起遠程請求後,會經系統底層封裝後交此方法來處理。 public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) 服務端通過code可以確定客戶端請求的目標方法,接着從data中取出目標方法的參數,然後執行目標方法,目標方法執行完後,就向reply中寫入返回值。
- Proxy#getBookList——這個方法運行在客戶端,調用流程如下:創建Parcel對象_data和_reply,接着調用transact方法發起遠程調用請求,同時線程掛起,然後服務端的onTransact方法會調用,知道調用過程返回,當前線程繼續執行,並從_reply中取出返回的結果,最後返回結果。
- Proxy#addBook——和getBookList過程一樣,只不過沒有返回值。
使用
服務端
/**
* @author liupeidong
* Created on 2019/11/7 21:40
*/
public class MyService extends Service {
IBookManager.Stub mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return null;
}
@Override
public void addBook(Book book) throws RemoteException {
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
客戶端
public class MainActivity extends AppCompatActivity {
IBookManager aidl;
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
aidl = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MyService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
}
這樣就通過AIDL完成了進程間通信。
繼承Binder實現繼承間通信
服務端
服務端只要繼承一個Binder,並完成重寫onTransact方法,在onTransact方法裏面對不同的方法進行判斷即可。
private MyBinder mBinder = new MyBinder();
private class MyBinder extends Binder
{
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException
{
switch (code)
{
case 0x110:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = _arg0 * _arg1;
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case 0x111:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = _arg0 / _arg1;
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
};
客戶端
創建ServiceConnection 綁定IBinder
private IBinder mPlusBinder;
private ServiceConnection mServiceConnPlus = new ServiceConnection()
{
@Override
public void onServiceDisconnected(ComponentName name)
{
Log.e("client", "mServiceConnPlus onServiceDisconnected");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
Log.e("client", " mServiceConnPlus onServiceConnected");
mPlusBinder = service;
}
};
然後調用Binder的方法時需要注意,需要加上自己的方法標誌或ID來區分不同的方法,然後的流程就可AIDL的差不多了,創建Parcel _data 和_reply,接着調用transact方法進行遠程請求,最後從_reply讀取結果即可。
public void mulInvoked(View view)
{
if (mPlusBinder == null)
{
Toast.makeText(this, "未連接服務端或服務端被異常殺死", Toast.LENGTH_SHORT).show();
} else
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try
{
_data.writeInterfaceToken("CalcPlusService");
_data.writeInt(50);
_data.writeInt(12);
mPlusBinder.transact(0x110, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show();
} catch (RemoteException e)
{
e.printStackTrace();
} finally
{
_reply.recycle();
_data.recycle();
}
}
}
public void divInvoked(View view)
{
if (mPlusBinder == null)
{
Toast.makeText(this, "未連接服務端或服務端被異常殺死", Toast.LENGTH_SHORT).show();
} else
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try
{
_data.writeInterfaceToken("CalcPlusService");
_data.writeInt(36);
_data.writeInt(12);
mPlusBinder.transact(0x111, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show();
} catch (RemoteException e)
{
e.printStackTrace();
} finally
{
_reply.recycle();
_data.recycle();
}
}
}