AIDL背景
在Android平台上,一個進程通常無法訪問另一個進程的內存,所以想要跨進程訪問的話,需要將要傳遞的數據分解為系統可以支持和識別的基本單元,有序的經過進程的邊界。因為這個操作十分繁瑣,所以Android使用AIDL來解決這個問題。AIDL就是用於生成兩個進程間進行進程間通信的(IPC)代碼,面向開發簡化這個過程。
注:只有允許不同應用的客户端用 IPC 方式訪問服務,並且想要在服務中處理多線程時,才有必要使用 AIDL。 如果您不需要執行跨越不同應用的併發 IPC,就應該通過實現一個 Binder 創建接口;或者,如果您想執行 IPC,但根本不需要處理多線程,則使用 Messenger 類來實現接口。無論如何,在實現 AIDL 之前,請您務必理解綁定服務。
定義AIDL接口
- 創建一個.aidl文件。
- 實現接口,此接口具有一個名為 stub 的內部抽象類,用於擴展 Binder 類並實現 aild 定義的接口。
- 向客户端公開接口,實現 Service 並重寫 onBind() 返回給 Stub 類的實現。
創建aidl文件
默認情況下,aidl支持的數據類型有:
- 基本數據類型( int, long, char, boolean, double等等)
- String 和 CharSequence
- List : 每個元素都必須被 aidl 支持,只支持ArrayList
- Map : 每個元素都必須被 aidl 支持,支持支HashMap
- Parcelable : 所有實現了 Parcelable 接口的對象
- aidl : 所有 aidl 接口本身也可以在 aidl 文件中使用
一個 aidl 文件例子 :
// IStudentManager.aidl
package com.seaicelin.aidl;
import com.seaicelin.aidl.Student;
interface IStudentManager {
List<Student> getStudentList();
void addStudent(in Student student);
}
Android Studio會自動生成一個 aidl 文件相對應的 java 接口文件,它包含一個名為 stub 的抽象類和他的代理類 proxy。
package com.seaicelin.aidl;
public interface IStudentManager extends android.os.IInterface {
//Local-side IPC implementation stub class.
public static abstract class Stub extends android.os.Binder implements com.seaicelin.aidl.IStudentManager {
private static final java.lang.String DESCRIPTOR = "com.seaicelin.aidl.IStudentManager";
//Construct the stub at attach it to the interface.
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
//Cast an IBinder object into an com.seaicelin.aidl.IStudentManager
//interface,generating a proxy if needed.
public static com.seaicelin.aidl.IStudentManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.seaicelin.aidl.IStudentManager))) {
return ((com.seaicelin.aidl.IStudentManager) iin);
}
return new com.seaicelin.aidl.IStudentManager.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_getStudentList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.seaicelin.aidl.Student> _result = this.getStudentList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addStudent: {
data.enforceInterface(DESCRIPTOR);
com.seaicelin.aidl.Student _arg0;
if ((0 != data.readInt())) {
_arg0 = com.seaicelin.aidl.Student.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addStudent(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.seaicelin.aidl.IStudentManager {
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.seaicelin.aidl.Student> getStudentList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.seaicelin.aidl.Student> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.seaicelin.aidl.Student.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addStudent(com.seaicelin.aidl.Student student) 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 ((student != null)) {
_data.writeInt(1);
student.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.seaicelin.aidl.Student> getStudentList() throws android.os.RemoteException;
public void addStudent(com.seaicelin.aidl.Student student) throws android.os.RemoteException;
}
遠程服務端service實現接口
服務端實現上文中提到的 stub 抽象類,實現 aidl 接口。這裏的 mBinder 是 Stub 的一個 實例,用於定義服務的 RPC 接口;在下一步中,將向客户端公開該實例,以便客户端能與服務端進行交互。
<service
android:name=".StudentManagerService"
android:enabled="true"
android:exported="true"
android:process=":remote">
</service>
public class StudentManagerService extends Service {
private static final String TAG = "[StudentManagerService]";
private CopyOnWriteArrayList<Student> mStudentList = new CopyOnWriteArrayList<>();
public StudentManagerService() {
}
@Override
public void onCreate(){
super.onCreate();
mStudentList.add(new Student(1, "seaicelin1"));
mStudentList.add(new Student(2, "seaicelin2"));
mStudentList.add(new Student(3, "seaicelin3"));
}
//實現aidl生成的java文件的Stub抽象類並實現aidl方法
//aidl方法在Binder線程池中執行,多個客户端同時連接時,需要處理線程同步
//CopyOnWriteArrayList支持併發讀寫,支持線程同步
private Binder mBinder = new IStudentManager.Stub(){
@Override
public List<Student> getStudentList() throws RemoteException {
return mStudentList;
}
@Override
public void addStudent(Student student) throws RemoteException {
if(mStudentList.contains(student))
return;
mStudentList.add(student);
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
客户端實現
在 service 內部實現接口後,需要向客户端公開該接口,以便客户端綁定。服務端需要創建一個 service 並實現 onBind() ,返回一個實例,這個實例實現了上文提到的 Stub 抽象類。
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private static final String TAG = "[MainActivity]";
private Button mBtnService;
private IStudentManager mStudentManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnService = (Button) findViewById(R.id.button);
mBtnService.setOnClickListener(this);
}
//創建 ServiceConnection 對象
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mStudentManager = IStudentManager.Stub.asInterface(service);
try {
List<Student> list = mStudentManager.getStudentList();
for(int i = 0; i < list.size(); i++){
Log.e(TAG, "query student list " + list.get(i).toString());
}
Student student = new Student(4, "seaicelin4");
mStudentManager.addStudent(student);
List<Student> newList = mStudentManager.getStudentList();
for(int i = 0; i < newList.size(); i++){
Log.e(TAG, "after add student, query student list " + newList.get(i).toString());
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
public void onClick(View v) {
Intent intent = new Intent(this, StudentManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
startService(intent);
Log.e(TAG, "onClick");
}
@Override
protected void onDestroy(){
unbindService(mConnection);
super.onDestroy();
}
}
由此,客户端創建完畢,查詢服務端的有多少個學生,然後增加一個學生,最後再次打印服務端的學生數。我們可以通過 log 來看看是否已經能夠正常使用。
03-25 09:17:09.117 31597-31597/com.seaicelin.aidl E/[MainActivity]: onClick
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 1 name = seaicelin1
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 2 name = seaicelin2
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 3 name = seaicelin3
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 1 name = seaicelin1
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 2 name = seaicelin2
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 3 name = seaicelin3
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 4 name = seaicelin4
到這裏,已經成功使用 aidl 進行跨進程通信,aidl 的基本使用介紹完畢。
總結調用 IPC 方法
用谷歌官方文檔作為總結吧,調用類必須執行以下步驟,才能調用使用 AIDL 定義的遠程接口:
- 在項目 src/ 目錄中加入 .aidl 文件。
- 聲明一個 IBinder 接口實例(基於 AIDL 生成)。
- 實現 ServiceConnection。
- 調用 Context.bindService(),以傳入您的 ServiceConnection 實現。
- 在您的 onServiceConnected() 實現中,您將收到一個 IBinder 實例(名為 service)。調用 YourInterfaceName.Stub.asInterface((IBinder)service),以將返回的參數轉換為 YourInterface 類型。
- 調用您在接口上定義的方法。您應該始終捕獲 DeadObjectException 異常,它們是在連接中斷時引發的;這將是遠程方法引發的唯一異常。
- 如需斷開連接,請使用您的接口實例調用 Context.unbindService()。