android AIDL詳解


      在Android中, 每個應用程序都有自己的進程,當需要在不同的進程之間傳遞對象時,該如何實現呢? 顯然, Java中是不支持跨進程內存共享的。因此要傳遞對象, 需要把對象解析成操作系統能夠理解的數據格式, 以達到跨界對象訪問的目的。在JavaEE中,采用RMI通過序列化傳遞對象。在Android中, 則采用AIDL(Android Interface Definition Language:接口定義語言)方式實現。

 

     

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

 

實現進程通信,一般需要下面四個步驟:(請見頁面下方備注欄)

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

 

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

package cn.itcast.aidl;

interface IDownloadService {

   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;

      }

   };

}

 


免責聲明!

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



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