大家都知道在Android中通过AIDL可以跨进程调用Service中的数据,网上也有很多实例,但是大部分实例都是关于基本数据类型的远程调用,很少讲到复杂数据的调用,今天我用一个例子来演示一下怎样用AIDL Service 传递复杂数据。
我们分2步开始:
第一步:部署我们的服务端,也就是Service端:
1:在Service端我先自定义2个类型:Person和Pet。因为我们需要跨进程传递Person对象和Pet对象,所以Person类和Pet类都必须实现Parcelable接口,并要求在实现类中定义一个名为CREATER,类型为Parcelable.creator的静态Field。
代码如下:
package com.example.remoteservice;import android.os.Parcel;
import android.os.Parcelable;public class Person implements Parcelable {int id;String name;String pass;public Person() {}public Person(int id, String name, String pass) {this.id = id;this.name = name;this.pass = pass;}@Overridepublic boolean equals(Object o) {if (this == o) {return true;}if (o == null) {return false;}if (getClass() != o.getClass()) {return false;}Person other = (Person) o;if (name == null) {if (other.name != null) {return false;}} else if (!name.equals(other.name)) {return false;}if (pass == null) {if (other.pass != null) {return false;}} else if (!pass.equals(other.pass)) {return false;}return true;}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + (name == null ? 0 : name.hashCode());result = prime * result + (pass == null ? 0 : pass.hashCode());return result;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel arg0, int arg1) {arg0.writeInt(id);arg0.writeString(name);arg0.writeString(pass);}public static final Parcelable.Creator<Person> CREATOR = new Creator<Person>() {@Overridepublic Person createFromParcel(Parcel source) {return new Person(source.readInt(), source.readString(), source.readString());}@Overridepublic Person[] newArray(int size) {return new Person[size];}};public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPass() {return pass;}public void setPass(String pass) {this.pass = pass;}}
因为我们会对Person进行比较,所以在Person类中我重写了
public int hashCode() 和 public boolean equals(Object o)方法
package com.example.remoteservice;import android.os.Parcel;
import android.os.Parcelable;public class Pet implements Parcelable {String name;float weight;public Pet(String name, float weight) {this.name = name;this.weight = weight;}public String getName() {return name;}public void setName(String name) {this.name = name;}public float getWeight() {return weight;}public void setWeight(float weight) {this.weight = weight;}@Overridepublic int describeContents() {return 1;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(name);dest.writeFloat(weight);}public static final Parcelable.Creator<Pet> CREATOR = new Creator<Pet>() {@Overridepublic Pet createFromParcel(Parcel source) {return new Pet(source.readString(), source.readFloat());}@Overridepublic Pet[] newArray(int size) {return new Pet[size];}};@Overridepublic String toString() {return "name:" + this.name + ";weight:" + this.weight;}}
2:创建完自定义类型之后还需要用AIDL来定义它们,Person.aidl和Pet.aidl的代码如下:
Person.aidl
package com.example.remoteservice;
parcelable Person;
Pet.aidl
package com.example.remoteservice;
parcelable Pet;
3:完成1,2之后就可以使用AIDL定义通信接口了,在这里我定义一个IPet.aidl的接口,代码如下:
package com.example.remoteservice; //必须导入包
import com.example.remoteservice.Person; //指定自定义类的位置
import com.example.remoteservice.Pet;interface IPet
{
List<Pet> getPets(in Person owner);//这里的in表示Person对象是输入的参数
}
4:服务端的最后一步就是实现Service了,当然不要忘了注册Service,代码如下:
package com.example.remoteservice;import com.example.remoteservice.IPet.Stub;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;public class RemoteService extends Service {private PetBinder petBinder;private static Map<Person, List<Pet>> pets = new HashMap<Person, List<Pet>>();static {ArrayList<Pet> list1 = new ArrayList<Pet>();list1.add(new Pet("candy", 2.2f));list1.add(new Pet("sandy", 4.2f));pets.put(new Person(1, "sun", "sun"), list1);ArrayList<Pet> list2 = new ArrayList<Pet>();list2.add(new Pet("moon", 5.2f));list2.add(new Pet("hony", 6.2f));pets.put(new Person(1, "csx", "csx"), list2);}public class PetBinder extends Stub {// 继承IPet接口中的Stub类,Stub类继承了Binder类,所有PetBinder也间接的继承了Binder类@Overridepublic List<Pet> getPets(Person owner) throws RemoteException {return pets.get(owner);}}@Overridepublic IBinder onBind(Intent intent) {Log.i("csx", "onBind");return petBinder;}@Overridepublic void onCreate() {super.onCreate();Log.i("csx", "onCreate");petBinder = new PetBinder();// 实例化Binder}@Overridepublic boolean onUnbind(Intent intent) {Log.i("csx", "onUnbind");return super.onUnbind(intent);}@Overridepublic void onDestroy() {super.onDestroy();Log.i("csx", "onDestroy");}}
这是我Service端的部署情况(其中MainActivity可以不用去实现,因为我们只提供服务,没有窗口显示):
第二步:部署客户端:
1.在客户端新建一个包,命名需要和服务端放置aidl文件的包名相同(我这里是com.example.remoteservice),然后把服务端的Person.java,Pet.java,Person.aidl,Pet.aidl,IPet.aidl复制到这个包下面
2.在activity中绑定远程服务进行数据交换,layout布局和activity代码如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.example.remoteclient.RemoteClient" ><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal" ><EditTextandroid:id="@+id/editText_person"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom"android:ems="10" ></EditText><Buttonandroid:id="@+id/button_ok"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom"android:text="确定" /></LinearLayout><ListViewandroid:id="@+id/listView_pet"android:layout_width="match_parent"android:layout_height="wrap_content" ></ListView></LinearLayout></RelativeLayout>
package com.example.remoteclient;import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;import com.example.remoteservice.IPet;
import com.example.remoteservice.Person;
import com.example.remoteservice.Pet;import java.util.List;public class RemoteClient extends ActionBarActivity {public static final String REMOTE_SERVICE_ACTION = "com.example.remoteservice.RemoteService.ACTION";EditText editText;Button button;ListView listView;IPet petService;// 声明IPet接口List<Pet> pets;ServiceConnection conn = new ServiceConnection() {@Overridepublic void onServiceDisconnected(ComponentName name) {Log.i("csx", "onServiceDisconnected");conn = null;}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.i("csx", "onServiceConnected");petService = IPet.Stub.asInterface(service);// 通过远程服务的Binder实现接口}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.remote_client_layout);editText = (EditText) findViewById(R.id.editText_person);button = (Button) findViewById(R.id.button_ok);listView = (ListView) findViewById(R.id.listView_pet);Intent service = new Intent();service.setAction(REMOTE_SERVICE_ACTION);bindService(service, conn, Service.BIND_AUTO_CREATE);// 绑定远程服务button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {String personName = editText.getText().toString();if (personName == null || personName.equals("")) {return;}try {pets = petService.getPets(new Person(1, personName, personName));// 调用远程service的getPets方法updataListView();} catch (RemoteException e) {e.printStackTrace();} catch (NullPointerException e) {e.printStackTrace();}}});}public void updataListView() {listView.setAdapter(null);if (pets == null || pets.isEmpty()) {return;}ArrayAdapter<Pet> adapter = new ArrayAdapter<Pet>(RemoteClient.this,android.R.layout.simple_list_item_1, pets);listView.setAdapter(adapter);}@Overrideprotected void onDestroy() {unbindService(conn);// 解除绑定super.onDestroy();}}
到此为止所有的工作都完成了,下面我们看一下效果:我在编辑框中输入“csx”,点击确定,就会显示出服务端RemoteService中pets的相应数据。