什麼是aidl?Android AIDL詳解

什麼是aidl:
aidl是 Android Interface definition language的縮寫,一看就明白,它是一種android內部進程通訊接口的描述語言,經過它咱們能夠定義進程間的通訊接口
icp:interprocess communication :內部進程通訊。java

在 Android中, 每一個應用程序都有本身的進程,當須要在不一樣的進程之間傳遞對象時,該如何實現呢? 顯然, Java中是不支持跨進程內存共享的。所以要傳遞對象, 須要把對象解析成操做系統可以理解的數據格式, 以達到跨界對象訪問的目的。在JavaEE中,採用RMI經過序列化傳遞對象。在Android中, 則採用AIDL(Android Interface Definition Language:接口定義語言)方式實現。
android

(1)在Eclipse Android工程的Java包目錄中創建一個擴展名爲aidl的文件。該文件的語法相似於Java代碼,但會稍有不一樣。
(2)若是aidl文件的內容是正確的,ADT會自動生成一個Java接口文件(*.java)。
(3)創建一個服務類(Service的子類)。
(4)實現由aidl文件生成的Java接口。
(5)在AndroidManifest.xml文件中配置AIDL服務,尤爲要注意的是,<action>標籤中android:name的屬性值就是客戶端要引用該服務的ID,也就是Intent類的參數值。 dom

AIDL 是一種接口定義語言,用於約束兩個進程間的通信規則,供編譯器生成代碼,實現Android設備上的兩個進程間通訊(IPC)。AIDL的IPC機制和 EJB所採用的CORBA很相似,進程之間的通訊信息,首先會被轉換成AIDL協議消息,而後發送給對方,對方收到AIDL協議消息後再轉換成相應的對 象。因爲進程之間的通訊信息須要雙向轉換,因此android採用代理類在背後實現了信息的雙向轉換,代理類由android編譯器生成,對開發人員來講 是透明的。
eclipse

實現進程通訊,通常須要下面四個步驟:ide

假設A應用須要與B應用進行通訊,調用B應用中的download(String path)方法,B應用以Service方式向A應用提供服務。須要下面四個步驟:this

1> 在B應用中建立*.aidl文件,aidl文件的定義和接口的定義很相類,如:在cn.itcast.aidl包下建立IDownloadService.aidl文件,內容以下:操作系統

package cn.itcast.aidl;代理

interface IDownloadService {xml

   void download(String path);對象

}

當 完成aidl文件建立後,eclipse會自動在項目的gen目錄中同步生成IDownloadService.java接口文件。接口文件中生成一個 Stub的抽象類,裏面包括aidl定義的方法,還包括一些其它輔助方法。值得關注的是asInterface(IBinder iBinder),它返回接口類型的實例,對於遠程服務調用,遠程服務返回給客戶端的對象爲代理對象,客戶端在 onServiceConnected(ComponentName name, IBinder service)方法引用該對象時不能直接強轉成接口類型的實例,而應該使用asInterface(IBinder iBinder)進行類型轉換。

編寫Aidl文件時,須要注意下面幾點:

  1.接口名和aidl文件名相同。

  2.接口和方法前不用加訪問權限修飾符public,private,protected等,也不能用final,static。

  3.Aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、 CharSequence),使用這些類型時不須要import聲明。對於List和Map中的元素類型必須是Aidl支持的類型。若是使用自定義類型做 爲參數或返回值,自定義類型必須實現Parcelable接口。

  4.自定義類型和AIDL生成的其它接口類型在aidl描述文件中,應該顯式import,即使在該類和定義的包在同一個包中。

  5.在aidl文件中全部非Java基本類型參數必須加上in、out、inout標記,以指明參數是輸入參數、輸出參數仍是輸入輸出參數。

  6.Java原始類型默認的標記爲in,不能爲其它標記。

2> 在B應用中實現aidl文件生成的接口(本例是IDownloadService),但並不是直接實現接口,而是經過繼承接口的Stub來實現(Stub抽象類內部實現了aidl接口),而且實現接口方法的代碼。內容以下:

public class ServiceBinder extends IDownloadService.Stub {

   @Override

   public void download(String path) throws RemoteException {

      Log.i("DownloadService", path);

   }    

}

3> 在B應用中建立一個Service(服務),在服務的onBind(Intent intent)方法中返回實現了aidl接口的對象(本例是ServiceBinder)。內容以下:

public class DownloadService extends Service {

   private ServiceBinder serviceBinder = new ServiceBinder();

   @Override

   public IBinder onBind(Intent intent) {

      return serviceBinder;

   }

   public class ServiceBinder extends IDownloadService.Stub {

      @Override

      public void download(String path) throws RemoteException {

        Log.i("DownloadService", path);

      }    

   }

}

其餘應用能夠經過隱式意圖訪問服務,意圖的動做能夠自定義,AndroidManifest.xml配置代碼以下:

<service android:name=".DownloadService" >

   <intent-filter>

      <action android:name="cn.itcast.process.aidl.DownloadService" />

   </intent-filter>

</service>

4> 把B應用中aidl文件所在package連同aidl文件一塊兒拷貝到客戶端A應用,eclipse會自動在A應用的gen目錄中爲aidl文件同步生成IDownloadService.java接口文件,接下來就能夠在A應用中實現與B應用通訊,代碼以下:

public class ClientActivity extends Activity {

   private IDownloadService downloadService;

   @Override

   public void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);

      setContentView(R.layout.main);

     this.bindService(new Intent("cn.itcast.process.aidl.DownloadService"), this.serviceConnection, BIND_AUTO_CREATE);//綁定到服務

   }

   @Override

   protected void onDestroy() {

      super.onDestroy();

      this.unbindService(serviceConnection);//解除服務

   } 

   private ServiceConnection serviceConnection = new ServiceConnection() {

      @Override

      public void onServiceConnected(ComponentName name, IBinder service) {

        downloadService = IDownloadService.Stub.asInterface(service);

        try {

           downloadService.download("http://www.itcast.cn");

        } catch (RemoteException e) {

           Log.e("ClientActivity", e.toString());

        }

      }

      @Override

      public void onServiceDisconnected(ComponentName name) {

        downloadService = null;

      }

   };

}

Aidl默認支持的類型包話java基本類型(int、long、boolean等)和(String、List、Map、CharSequence),若是要傳遞自定義的類型該如何實現呢?

進程間傳遞自定義類型的實現過程請參見頁面下方備註欄:

1> 建立自定義類型,並實現Parcelable接口,使其支持parcelable協議。如:在cn.itcast.domain包下建立Person.java:

package cn.itcast.domain;

import android.os.Parcel;

import android.os.Parcelable;

public class Person implements Parcelable

   private Integer id;

   private String name;

   public Person(){}

   public Person(Integer id, String name) {

      this.id = id;

      this.name = name;

   }

   public Integer getId() {

      return id;

   }

   public void setId(Integer id) {

      this.id = id;

   }

   public String getName() {

      return name;

   }

   public void setName(String name) {

      this.name = name;

   } 

   @Override

   public int describeContents() {

      return 0;

   }

   @Override

   public void writeToParcel(Parcel dest, int flags) {//把javanbean中的數據寫到Parcel

      dest.writeInt(this.id);

      dest.writeString(this.name);

   }

//添加一個靜態成員,名爲CREATOR,該對象實現了Parcelable.Creator接口

   public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>(){

      @Override

      public Person createFromParcel(Parcel source) {//從Parcel中讀取數據,返回person對象

        return new Person(source.readInt(), source.readString());

      }

      @Override

      public Person[] newArray(int size) {

        return new Person[size];

      }

   };

}

2> 在自定義類型所在包下建立一個aidl文件對自定義類型進行聲明,文件的名稱與自定義類型同名。

package cn.itcast.domain;

parcelable Person;

3> 在接口aidl文件中使用自定義類型,須要使用import顯式導入,本例在cn.itcast.aidl包下建立IPersonService.aidl文件,內容以下:

package cn.itcast.aidl;

import cn.itcast.domain.Person;

interface IPersonService {

      void save(in Person person);

}

4> 在實現aidl文件生成的接口(本例是IPersonService),但並不是直接實現接口,而是經過繼承接口的Stub來實現(Stub抽象類內部實現了aidl接口),而且實現接口方法的代碼。內容以下:

public class ServiceBinder extends IPersonService.Stub {

       @Override

       public void save(Person person) throws RemoteException {

   Log.i("PersonService", person.getId()+"="+ person.getName());

       }   

}

5> 建立一個Service(服務),在服務的onBind(Intent intent)方法中返回實現了aidl接口的對象(本例是ServiceBinder)。內容以下:

public class PersonService extends Service {

   private ServiceBinder serviceBinder = new ServiceBinder();

   @Override

   public IBinder onBind(Intent intent) {

      return serviceBinder;

   }

public class ServiceBinder extends IPersonService.Stub {

       @Override

       public void save(Person person) throws RemoteException {

   Log.i("PersonService", person.getId()+"="+ person.getName());

       }

}

}

其餘應用能夠經過隱式意圖訪問服務,意圖的動做能夠自定義,AndroidManifest.xml配置代碼以下:

<service android:name=".PersonService" >

   <intent-filter>

      <action android:name="cn.itcast.process.aidl.PersonService " />

   </intent-filter>

</service>

6> 把應用中的aidl文件和所在package一塊兒拷貝到客戶端應用的src目錄下,eclipse會自動在客戶端應用的gen目錄中爲aidl文件同步生 成IPersonService.java接口文件,接下來再把自定義類型文件和類型聲明aidl文件及所在package一塊兒拷貝到客戶端應用的src 目錄下。

最後就能夠在客戶端應用中實現與遠程服務的通訊,代碼以下:

public class ClientActivity extends Activity {

   private IPersonService personService;

   @Override

   public void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);

      setContentView(R.layout.main);

      this.bindService(new Intent("cn.itcast.process.aidl.PersonService"), this.serviceConnection, BIND_AUTO_CREATE);//綁定到服務

   }

@Override

   protected void onDestroy() {

      super.onDestroy();

      this.unbindService(serviceConnection);//解除服務

   } 

   private ServiceConnection serviceConnection = new ServiceConnection() {

      @Override

      public void onServiceConnected(ComponentName name, IBinder service) {

        personService = IPersonService.Stub.asInterface(service);

        try {

           personService.save(new Person(56,"liming"));

        } catch (RemoteException e) {

           Log.e("ClientActivity", e.toString());

        }

      }

      @Override

      public void onServiceDisconnected(ComponentName name) {

        personService = null;

      }

   };

}

最後咱們在總結一下android中 本地服務和 AIDL服務的區別:     本地服務不支持onBind(),它從onBind()返回null,這種類型的服務只能由承載服務的應用程序組件訪問。能夠調用 startService()來調用本地服務。AIDL服務能夠同時供 同一進程內的組件和其餘應用程序的組件使用。這種類型的服務在AIDL 文件中爲自身與其客戶端定義一個契約。服務實現 AIDL契約,而客戶端綁定到 AIDL定義。服務經過從 onBind()方法 返回AIDL接口的實現,來實現契約。客戶端經過調用 bindService()來綁定到AIDL服務,並調用 unBindService()來從服務斷開。