用戶端向服務端發送一個String類型的參數,服務端也傳回一個String類型的結果,看似已經可以完成基本的需要了。不過在有的時候,簡單的資料類型并不能滿足我們的需求,我們就需要考慮怎樣向服務端傳遞複雜的資料類型。今天就來講一下如何向服務端傳遞複雜類型的問題。
首先要了解一下AIDL對Java類型的支援。
1.AIDL支援Java原始資料類型。
2.AIDL支援String和CharSequence。
3.AIDL支援傳遞其他AIDL接口,但你引用的每個AIDL接口都需要一個import語句,即使位于同一個包中。
4.AIDL支援傳遞實作了android.os.Parcelable接口的複雜類型,同樣在引用這些類型時也需要import語句。(Parcelable接口告訴Android運作時在封送(marshalling)和解封送(unmarshalling)過程中實作如何序列化和反序列化對象,你可以很容易聯想到java.io.Serializable接口。有些朋友可能會有疑問,兩種接口功能确實類似,但為什麼Android不用内置的Java序列化機制,而偏偏要搞一套新東西呢?這是因為Android團隊認為Java中的序列化太慢,難以滿足Android的程序間通信需求,是以他們建構了Parcelable解決方案。Parcelable要求顯示序列化類的成員,但最終序列化對象的速度将快很多。另外要注意的是,Android提供了兩種機制來将資料傳遞給另一個程序,第一種是使用Intent将資料束(Bundle)傳遞給活動,第二種也就是Parcelable傳遞給服務。這兩種機制不可互換,不要混淆。也就是說,Parcelable無法傳遞給活動,隻能用作AIDL定義的一部分)。
5.AIDL支援java.util.List和java.util.Map,但是有一些限制。集合中項的允許資料類型包括Java原始類型、String、CharSequence或是android.os.Parcelable。無需為List和Map提供import語句,但需要為Parcelable提供import語句。
6.非原始類型中,除了String和CharSequence以外,其餘均需要一個方向訓示符。方向訓示符包括in、out、和inout。in表示由用戶端設定,out表示由服務端設定,inout表示用戶端和服務端都設定了該值。
接下來就示範一下具體的流程:
我們先看一下服務端的結構:
其中,Person類是我們要在服務端和用戶端中間進行傳遞的類型,代碼如下:
package com.scott.aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Person implements Parcelable {
private String name;
private int sex;
//必須提供一個名為CREATOR的static final屬性 該屬性需要實作android.os.Parcelable.Creator接口
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
@Override
public Person createFromParcel(Parcel source) {
return new Person(source);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
public Person() {
}
private Person(Parcel source) {
readFromParcel(source);
}
@Override
public int describeContents() {
return 0;
}
//注意寫入變量和讀取變量的順序應該一緻 不然得不到正确的結果
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(sex);
}
//注意讀取變量和寫入變量的順序應該一緻 不然得不到正确的結果
public void readFromParcel(Parcel source) {
name = source.readString();
sex = source.readInt();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
}
然後我們需要在同一包下建立一個與包含複雜類型的Person.java檔案比對的Person.aidl檔案,代碼如下:
package com.scott.aidl;
parcelable Person;
這個Person.aidl檔案很簡單,就是定義了一個Parcelable類,告訴系統我們需要序列化和反序列化的類型。每一個實作了Parcelable的類型都需要對應的.aidl檔案。AIDL編譯器在編譯AIDL檔案時會自動查找此類檔案。
接下來,我們需要建立一個IGreetService.aidl檔案,以接收類型為Person的輸入參數,以便用戶端可以将Person傳遞給服務。
package com.scott.aidl;
import com.scott.aidl.Person;
interface IGreetService {
String greet(in Person person);
}
注意,我們需要在參數上加入方向訓示符in,代表參數由用戶端設定,我們還需要為Person提供一個import語句(雖然說在同一個包下)。
此時,在eclipse插件的幫助下,AIDL編譯器會自動編譯生成一個IGreetService.java檔案,格式化後的代碼如下:
package com.scott.aidl;
public interface IGreetService extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.scott.aidl.IGreetService {
private static final java.lang.String DESCRIPTOR = "com.scott.aidl.IGreetService";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.scott.aidl.IGreetService 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 com.scott.aidl.IGreetService))) {
return ((com.scott.aidl.IGreetService) iin);
}
return new com.scott.aidl.IGreetService.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_greet: {
data.enforceInterface(DESCRIPTOR);
com.scott.aidl.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = com.scott.aidl.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
java.lang.String _result = this.greet(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.scott.aidl.IGreetService {
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 java.lang.String greet(com.scott.aidl.Person person) 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);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_greet, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_greet = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String greet(com.scott.aidl.Person person) throws android.os.RemoteException;
}
該檔案的大綱視圖如下:
接下來,就該完成我們的AIDLService邏輯部分了,AIDLService.java代碼如下:
package com.scott.server;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.scott.aidl.IGreetService;
import com.scott.aidl.Person;
public class AIDLService extends Service {
private static final String TAG = "AIDLService";
IGreetService.Stub stub = new IGreetService.Stub() {
@Override
public String greet(Person person) throws RemoteException {
Log.i(TAG, "greet(Person person) called");
String greeting = "hello, " + person.getName();
switch (person.getSex()) {
case 0:
greeting = greeting + ", you're handsome.";
break;
case 1:
greeting = greeting + ", you're beautiful.";
break;
}
return greeting;
}
};
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind() called");
return stub;
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind() called");
return true;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy() called");
}
}
最後,在AndroidManifest.xml中配置該服務:
服務端就已經完成了。接下來我們來看一下用戶端的結構:
我們需要把服務端的Person.java、Person.aidl和IGreetService.aidl拷到對應的包下。我們主要來看看MainActivity.java代碼:
package com.scott.client;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.scott.aidl.IGreetService;
import com.scott.aidl.Person;
public class MainActivity extends Activity {
private Button bindBtn;
private Button greetBtn;
private Button unbindBtn;
private IGreetService greetService;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i("ServiceConnection", "onServiceConnected() called");
greetService = IGreetService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
//This is called when the connection with the service has been unexpectedly disconnected,
//that is, its process crashed. Because it is running in our same process, we should never see this happen.
Log.i("ServiceConnection", "onServiceDisconnected() called");
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
bindBtn = (Button) findViewById(R.id.bindBtn);
bindBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("android.intent.action.AIDLService");
bindService(intent, conn, Context.BIND_AUTO_CREATE);
bindBtn.setEnabled(false);
greetBtn.setEnabled(true);
unbindBtn.setEnabled(true);
}
});
greetBtn = (Button) findViewById(R.id.greetBtn);
greetBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Person person = new Person();
person.setName("scott");
person.setSex(0);
String retVal = greetService.greet(person);
Toast.makeText(MainActivity.this, retVal, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();
}
}
});
unbindBtn = (Button) findViewById(R.id.unbindBtn);
unbindBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(conn);
bindBtn.setEnabled(true);
greetBtn.setEnabled(false);
unbindBtn.setEnabled(false);
}
});
}
}
通過這種方式,就可以将Person類型的對象傳遞到服務端,實作複雜類型的通信傳遞。
當點選greetBtn時,截圖如下:
然後看一下整個過程的日志資訊。點選bindBtn、greetBtn、unbindBtn的整個過程日志如下: