android開發AIDL例項
由於每個應用程式都執行在自己的程序空間,並且可以從應用程式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;
}
@Override
public String sayHello() throws RemoteException {
// TODO Auto-generated method stub
return "歡迎你通過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() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
myService=null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
myService=IMyService.Stub.asInterface(service);
try {
textHello.setText(myService.sayHello());
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.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;
@Override
public 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() {
@Override
public 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,已經從服務端得到了資料,第一句話就是從服務端得到的,下面看看,傳遞物件和獲取物件與服務端。
點選新增聯絡人,然後點選獲取聯絡人按鈕: