AIDL是一種接口定義語言,用於生成可在Android設備上兩個進程之間進行進程間通信(IPC)的代碼。
AIDL的使用
- 新建一個aidl文件,定義進程間通信的接口
// IStudentManager.aidl
package com.tellh.androidart;
// Declare any non-default types here with import statements
import com.tellh.androidart.Student;
interface IStudentManager {
List<Student> getStudent();
void addStudent(in Student student);
}
注意點:
- aidl中支持的參數類型為:基本類型(int,long,char,boolean等),String,CharSequence,List,Map,其他類型必須使用import導入,即使它們可能在同一個包里。
- 接口中的參數除了aidl支持的類型,其他類型必須標識其方向:到底是輸入還是輸出抑或兩者兼之,用in,out或者inout來表示。
- 如果有自定義的數據類型,需要把自定義類實現Parcelable接口並新建aidl聲明文件。
// Student.aidl package tellh.com.androidart; parcelable Student; public class Student implements Parcelable {...}
點擊sycn project,對應的接口java類就被自動生成在gen目錄中。
- 創建服務端的Service,運行在服務端進程
public class AidlTestService extends Service {
private final static String TAG = "AidlTestService";
private static final String PACKAGE_CLIENT = "com.example.test.aidlclient&&com.tellh.androidart";
private final List<Student> mStudents = new ArrayList<>();
private IBinder mBinder = new IStudentManager.Stub() {
@Override
public List<Student> getStudent() throws RemoteException {
synchronized (mStudents) { return mStudents; }
}
@Override
public void addStudent(Student student) throws RemoteException {
synchronized (mStudents) {
if (!mStudents.contains(student)) mStudents.add(student);
}
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//權限控制
String packageName = null;
String[] packages = AidlTestService.this.getPackageManager(). getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
Log.d(TAG, "onTransact: " + packageName);
String[] clients = PACKAGE_CLIENT.split("&&");
for (String client : clients) {
if (client.equals(packageName)) {
return super.onTransact(code, data, reply, flags); }
}
return false;
}
};
@Override
public void onCreate() {
super.onCreate();
synchronized (mStudents) {
for (int i = 1; i < 6; i++) {
Student student = new Student();
student.sno = i;
student.name = "student#" + i;
student.age = i * 5;
mStudents.add(student);
} } }
public AidlTestService() { }
@Override
public IBinder onBind(Intent intent) { return mBinder; }
}
注冊Service
<service android:name=".AidlTestService" android:enabled="true" android:exported="true">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.tellh.androidart.AidlTestService" />
</intent-filter>
</service>
新建一個進程作為客戶端,隱式啟動服務端的Service,在綁定的回調函數中通過IStudentManager.Stub.asInterface(binder)
獲得通信接口的代理對象。
public class AidlClientActivity extends ActionBarActivity {
private String tag = "AidlClient";
private static final String ACTION_BIND_SERVICE = "com.tellh.androidart.AidlTestService";
private IStudentManager mStudentManager;
// Binder連接斷裂(死亡)代理
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mStudentManager == null) return;
mStudentManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mStudentManager = null;
bindService(); }
};
private ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mStudentManager = IStudentManager.Stub.asInterface(service);
try { service.linkToDeath(mDeathRecipient, 0); }
catch (RemoteException e) { e.printStackTrace(); }
}
@Override
public void onServiceDisconnected(ComponentName name) { mStudentManager = null; }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl_client);
bindService(); }
private void bindService() {
Intent intent = new Intent(ACTION_BIND_SERVICE);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setPackage("com.tellh.androidart");
bindService(intent, mServiceConn, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mStudentManager != null) unbindService(mServiceConn);
}
public void onClickGetStudent(View view) {
if (mStudentManager == null) return;
try { Log.d(tag, mStudentManager.getStudent().toString()); }
catch (RemoteException e) { e.printStackTrace(); } }
public void onClickAddStudent(View view) {
if (mStudentManager == null) return;
try { Student student = new Student();
student.sno = 7;
student.age = 10;
student.name = "TellH";
student.sex = Student.SEX_MALE;
mStudentManager.addStudent(student);
Log.d(tag, mStudentManager.getStudent().toString());
}
catch (RemoteException e) { e.printStackTrace(); } } }
注冊Activity,在新的進程中運行
<activity android:name=".AidlClientActivity" android:process=":client" />
如果在應用之間通信,客戶端應用需要把服務端的aidl文件全部copy過來,並且保證包名相同,
AIDL的工作原理
分析自動生成的java接口代碼:
pubblic interface IStudentManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements tellh.com.androidart.IStudentManager {...}
public java.util.List<tellh.com.androidart.Student> getStudent() throws android.os.RemoteException;
public void addStudent(tellh.com.androidart.Student student) throws android.os.RemoteException;
這個接口類繼承IInterface,所有在Binder中傳輸的接口都必須繼承IInterface。它除了保留了我們定義在aidl文件中的兩個方法外,還自動生成一個名為Stub的靜態類。這個Stub是抽象類,實現了這個接口類並繼承自Binder,將會在服務端的Service中被實例化,作為onBind的返回指返回給客戶端,客戶端通過IStudentManager.Stub.asInterface(binder)轉化為這個接口類的代理對象。
抽象類Stub的成員和屬性:
DESRIPTOR
Binder的唯一標識,一般用當前Binder的全類名表示。
public static IStudentManager asInterface(IBinder obj)
如果客戶端和服務端位於同一個進程,那么直接返回服務端的Stub對象本身,否則返回Stub.Proxy代理對象。
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
該方法運行在服務端中的Binder線程池中,服務端通過code確定客戶端所請求的目標方法是什么,從data中取出目標方法所需的參數,然后執行目標方法,並將目標方法的返回值寫入reply。如果該方法的返回false,那么客戶端的請求回失敗。因此,我們可以利用這個特性來做權限驗證。
Proxy#addStudent
Proxy是一個代理類,或者說是裝飾者類。
@Override public java.util.List<tellh.com.androidart.Student> getStudent() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<tellh.com.androidart.Student> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getStudent, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(tellh.com.androidart.Student.CREATOR); }
finally {
_reply.recycle();
_data.recycle();
}
return _result; }
每一個代理方法都是相同的套路,將方法的標識,參數和返回值對象傳入方法mRemote.transact中,發起遠程調用請求,同時掛起當前線程,然后服務端的Stub#onTransact被調用。當onTransact返回true,調用成功,從reply對象中取出返回值,返回給客戶端調用方。
總結
高度概括AIDL的用法,就是服務端里有一個Service,給與之綁定(bindService)的特定客戶端進程提供Binder對象。客戶端通過AIDL接口的靜態方法asInterface 將Binder對象轉化成AIDL接口的代理對象,通過這個代理對象就可以發起遠程調用請求了。
至於更深入地,遠程調用的過程可以概括如下圖
————————————————
版權聲明:本文為CSDN博主「TellH」的原創文章,遵循undefined版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/tellh/article/details/55100167