由於每個應用程式都運行在自己的進程空間,並且可以從應用程式UI運行另一個服務進程,而且經常會在不同的進程間傳遞對象。在Android平台,一個進程通常不能訪問另一個進程的記憶體空間。但是android提供了AIDL可以用來進程間資料傳遞。
AIDL (Android Interface Definition Language) 是一種IDL 語言,用於產生可以在Android裝置上兩個進程之間進行處理序間通訊(interprocess communication, IPC)的代碼。如果在一個進程中(例如Activity)要調用另一個進程中(例如Service)對象的操作,就可以使用AIDL產生可序列化的參數。
AIDL IPC機制是面向介面的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在用戶端和實現端傳遞資料。
下面通過一個執行個體來示範AIDL,因為是進程之間資料傳遞,所以這裡要使用建立android工程,一個是AIDL的服務端另一個是用戶端.
服務端的實現步驟:
1.建立.aidl檔案 IMyService.aidl
package cn.com.karl.aidl;import cn.com.karl.aidl.Person;interface IMyService { void savePersonInfo(in Person person); List<Person> getAllPerson(); String sayHello();}
因為這裡用到了Peson對象,所以要建立一個person類。Person類,是一個序列化的類,這裡使用Parcelable 介面來序列化,是Android提供的一個比Serializable 效率更高的序列化類別。
public class Person implements Parcelable { private String name; private String telNumber; private int age; public Person() {} public Person(Parcel pl){ name = pl.readString(); telNumber = pl.readString(); age = pl.readInt(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTelNumber() { return telNumber; } public void setTelNumber(String telNumber) { this.telNumber = telNumber; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeString(telNumber); dest.writeInt(age); } public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() { @Override public Person createFromParcel(Parcel source) { return new Person(source); } @Override public Person[] newArray(int size) { return new Person[size]; } };}
然後建立Person.aidl檔案,注意這裡的parcelable小寫。
package cn.com.karl.aidl;parcelable Person;
上面的IMyService.aidl儲存以後會在gen的相應目錄下啟動產生如下代碼:
Binder
package cn.com.karl.aidl;public interface IMyService extends android.os.IInterface{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements cn.com.karl.aidl.IMyService{private static final java.lang.String DESCRIPTOR = "cn.com.karl.aidl.IMyService";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an cn.com.karl.aidl.IMyService interface, * generating a proxy if needed. */public static cn.com.karl.aidl.IMyService asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof cn.com.karl.aidl.IMyService))) {return ((cn.com.karl.aidl.IMyService)iin);}return new cn.com.karl.aidl.IMyService.Stub.Proxy(obj);}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_savePersonInfo:{data.enforceInterface(DESCRIPTOR);cn.com.karl.aidl.Person _arg0;if ((0!=data.readInt())) {_arg0 = cn.com.karl.aidl.Person.CREATOR.createFromParcel(data);}else {_arg0 = null;}this.savePersonInfo(_arg0);reply.writeNoException();return true;}case TRANSACTION_getAllPerson:{data.enforceInterface(DESCRIPTOR);java.util.List<cn.com.karl.aidl.Person> _result = this.getAllPerson();reply.writeNoException();reply.writeTypedList(_result);return true;}case TRANSACTION_sayHello:{data.enforceInterface(DESCRIPTOR);java.lang.String _result = this.sayHello();reply.writeNoException();reply.writeString(_result);return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements cn.com.karl.aidl.IMyService{private android.os.IBinder mRemote;Proxy(android.os.IBinder remote){mRemote = remote;}public android.os.IBinder asBinder(){return mRemote;}public java.lang.String getInterfaceDescriptor(){return DESCRIPTOR;}public void savePersonInfo(cn.com.karl.aidl.Person person) 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 ((person!=null)) {_data.writeInt(1);person.writeToParcel(_data, 0);}else {_data.writeInt(0);}mRemote.transact(Stub.TRANSACTION_savePersonInfo, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}public java.util.List<cn.com.karl.aidl.Person> getAllPerson() throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.util.List<cn.com.karl.aidl.Person> _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getAllPerson, _data, _reply, 0);_reply.readException();_result = _reply.createTypedArrayList(cn.com.karl.aidl.Person.CREATOR);}finally {_reply.recycle();_data.recycle();}return _result;}public java.lang.String sayHello() throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.lang.String _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_sayHello, _data, _reply, 0);_reply.readException();_result = _reply.readString();}finally {_reply.recycle();_data.recycle();}return _result;}}static final int TRANSACTION_savePersonInfo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_getAllPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);static final int TRANSACTION_sayHello = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);}public void savePersonInfo(cn.com.karl.aidl.Person person) throws android.os.RemoteException;public java.util.List<cn.com.karl.aidl.Person> getAllPerson() throws android.os.RemoteException;public java.lang.String sayHello() throws android.os.RemoteException;}
因為sub類實現了Binder介面,所以以後會使用這個類。
2.實現service類
public class RemoteService extends Service { private LinkedList<Person> personList = new LinkedList<Person>(); @Override public IBinder onBind(Intent intent) { return mBinder; } private final IMyService.Stub mBinder = new IMyService.Stub(){ @Override public void savePersonInfo(Person person) throws RemoteException { if (person != null){ personList.add(person); } } @Override public List<Person> getAllPerson() throws RemoteException { return personList; }@Overridepublic String sayHello() throws RemoteException {// TODO Auto-generated method stubreturn "歡迎你通過AIDL訪問伺服器端";} };}
3.用戶端實現步驟:
首先建立一個項目,把服務端的
包和類一起拷貝到用戶端項目中。因為用戶端要和服務端通訊,必須要使用同一個aidl。
然後構造用戶端的activity類:
public class RemoteClientActivity extends Activity { /** Called when the activity is first created. */private TextView textHello,textPerson;private IMyService myService;private Button btnSave;private Button btnGet;private static Boolean mIsRemoteBound=false;private ServiceConnection conn=new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {// TODO Auto-generated method stubmyService=null;}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// TODO Auto-generated method stubmyService=IMyService.Stub.asInterface(service);try {textHello.setText(myService.sayHello());} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textHello=(TextView) this.findViewById(R.id.textHello); btnSave=(Button) this.findViewById(R.id.btnSave); btnGet=(Button) this.findViewById(R.id.btnGet); textPerson=(TextView) this.findViewById(R.id.textPerson); if(mIsRemoteBound){ unbindService(conn); }else{ Intent intent=new Intent("cn.com.karl.aidl.RemoteService"); bindService(intent, conn, BIND_AUTO_CREATE); } mIsRemoteBound = !mIsRemoteBound; btnSave.setOnClickListener(new OnClickListener() { private int index = 0;@Overridepublic void onClick(View v) {// TODO Auto-generated method stub Person person = new Person(); index = index + 1; person.setName("Person" + index); person.setAge(20); person.setTelNumber("123456"); try { myService.savePersonInfo(person); } catch (RemoteException e) { e.printStackTrace(); } }}); btnGet.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stub List<Person> list = null; try { list = myService.getAllPerson(); } catch (RemoteException e) { e.printStackTrace(); } if (list != null){ StringBuilder text = new StringBuilder(); for(Person person : list){ text.append("\n連絡人:"); text.append(person.getName()); text.append("\n 年齡:"); text.append(person.getAge()); text.append("\n 電話:"); text.append(person.getTelNumber()); } textPerson.setText(text); }else { Toast.makeText(RemoteClientActivity.this, "得到資料出錯", Toast.LENGTH_SHORT).show(); } }}); }}
最後不要忘記註冊service:
<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".RemoteService"> <intent-filter > <action android:name="cn.com.karl.aidl.RemoteService"></action> </intent-filter> </service> </application>
啟動service的時候使用了隱士意圖。
運行服務端工程.
服務端已經啟動,然後運行用戶端工程:
OK,已經從服務端得到了資料,第一句話就是從服務端得到的,下面看看,傳遞對象和擷取對象與服務端。
點擊新增連絡人...,然後點擊擷取連絡人按鈕: