淺析AIDL的使用和工作原理


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

 


免責聲明!

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



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