大家都知道在Android中通过AIDL可以跨进程调用Service中的数据,网上也有很多实例,但是大部分实例都是关于基本数据类型的远程调用,很少讲到复杂数据的调用,今天我用一个例子来演示一下怎样用AIDL Service 传递复杂数据。
我们分2步开始:
第一步:部署我们的服务端,也就是Service端:
1:在Service端我先自定义2个类型:Person和Pet。因为我们需要跨进程传递Person对象和Pet对象,所以Person类和Pet类都必须实现Parcelable接口,并要求在实现类中定义一个名为CREATER,类型为Parcelable.creator的静态Field。
代码如下:
1 package com.example.remoteservice; 2 3 import android.os.Parcel; 4 import android.os.Parcelable; 5 6 public class Person implements Parcelable { 7 int id; 8 String name; 9 String pass; 10 11 public Person() { 12 13 } 14 15 public Person(int id, String name, String pass) { 16 this.id = id; 17 this.name = name; 18 this.pass = pass; 19 } 20 21 @Override 22 public boolean equals(Object o) { 23 if (this == o) { 24 return true; 25 } 26 if (o == null) { 27 return false; 28 } 29 30 if (getClass() != o.getClass()) { 31 return false; 32 } 33 Person other = (Person) o; 34 35 if (name == null) { 36 if (other.name != null) { 37 return false; 38 } 39 } else if (!name.equals(other.name)) { 40 return false; 41 } 42 43 if (pass == null) { 44 if (other.pass != null) { 45 return false; 46 } 47 } else if (!pass.equals(other.pass)) { 48 return false; 49 } 50 51 return true; 52 } 53 54 @Override 55 public int hashCode() { 56 final int prime = 31; 57 int result = 1; 58 result = prime * result + (name == null ? 0 : name.hashCode()); 59 result = prime * result + (pass == null ? 0 : pass.hashCode()); 60 return result; 61 } 62 63 @Override 64 public int describeContents() { 65 66 return 0; 67 } 68 69 @Override 70 public void writeToParcel(Parcel arg0, int arg1) { 71 arg0.writeInt(id); 72 arg0.writeString(name); 73 arg0.writeString(pass); 74 } 75 76 public static final Parcelable.Creator<Person> CREATOR = new Creator<Person>() { 77 78 @Override 79 public Person createFromParcel(Parcel source) { 80 81 return new Person(source.readInt(), source.readString(), source.readString()); 82 } 83 84 @Override 85 public Person[] newArray(int size) { 86 87 return new Person[size]; 88 } 89 }; 90 91 public int getId() { 92 return id; 93 } 94 95 public void setId(int id) { 96 this.id = id; 97 } 98 99 public String getName() { 100 return name; 101 } 102 103 public void setName(String name) { 104 this.name = name; 105 } 106 107 public String getPass() { 108 return pass; 109 } 110 111 public void setPass(String pass) { 112 this.pass = pass; 113 } 114 115 }
因为我们会对Person进行比较,所以在Person类中我重写了
public int hashCode() 和 public boolean equals(Object o)方法
1 package com.example.remoteservice; 2 3 import android.os.Parcel; 4 import android.os.Parcelable; 5 6 public class Pet implements Parcelable { 7 String name; 8 float weight; 9 10 public Pet(String name, float weight) { 11 this.name = name; 12 this.weight = weight; 13 } 14 15 public String getName() { 16 return name; 17 } 18 19 public void setName(String name) { 20 this.name = name; 21 } 22 23 public float getWeight() { 24 return weight; 25 } 26 27 public void setWeight(float weight) { 28 this.weight = weight; 29 } 30 31 @Override 32 public int describeContents() { 33 34 return 1; 35 } 36 37 @Override 38 public void writeToParcel(Parcel dest, int flags) { 39 dest.writeString(name); 40 dest.writeFloat(weight); 41 42 } 43 44 public static final Parcelable.Creator<Pet> CREATOR = new Creator<Pet>() { 45 46 @Override 47 public Pet createFromParcel(Parcel source) { 48 49 return new Pet(source.readString(), source.readFloat()); 50 } 51 52 @Override 53 public Pet[] newArray(int size) { 54 55 return new Pet[size]; 56 } 57 }; 58 59 @Override 60 public String toString() { 61 62 return "name:" + this.name + ";weight:" + this.weight; 63 } 64 65 }
2:创建完自定义类型之后还需要用AIDL来定义它们,Person.aidl和Pet.aidl的代码如下:
1 package com.example.remoteservice; 2 parcelable Person;
1 package com.example.remoteservice; 2 parcelable Pet;
3:完成1,2之后就可以使用AIDL定义通信接口了,在这里我定义一个IPet.aidl的接口,代码如下:
1 package com.example.remoteservice; //必须导入包 2 import com.example.remoteservice.Person; //指定自定义类的位置 3 import com.example.remoteservice.Pet; 4 5 interface IPet 6 { 7 List<Pet> getPets(in Person owner);//这里的in表示Person对象是输入的参数 8 }
4:服务端的最后一步就是实现Service了,当然不要忘了注册Service,代码如下:
1 package com.example.remoteservice; 2 3 import com.example.remoteservice.IPet.Stub; 4 5 import java.util.ArrayList; 6 import java.util.HashMap; 7 import java.util.List; 8 import java.util.Map; 9 10 import android.app.Service; 11 import android.content.Intent; 12 import android.os.IBinder; 13 import android.os.RemoteException; 14 import android.util.Log; 15 16 public class RemoteService extends Service { 17 18 private PetBinder petBinder; 19 20 private static Map<Person, List<Pet>> pets = new HashMap<Person, List<Pet>>(); 21 static { 22 ArrayList<Pet> list1 = new ArrayList<Pet>(); 23 list1.add(new Pet("candy", 2.2f)); 24 list1.add(new Pet("sandy", 4.2f)); 25 pets.put(new Person(1, "sun", "sun"), list1); 26 27 ArrayList<Pet> list2 = new ArrayList<Pet>(); 28 list2.add(new Pet("moon", 5.2f)); 29 list2.add(new Pet("hony", 6.2f)); 30 pets.put(new Person(1, "csx", "csx"), list2); 31 32 } 33 34 public class PetBinder extends Stub {// 继承IPet接口中的Stub类,Stub类继承了Binder类,所有PetBinder也间接的继承了Binder类 35 36 @Override 37 public List<Pet> getPets(Person owner) throws RemoteException { 38 39 return pets.get(owner); 40 } 41 42 } 43 44 @Override 45 public IBinder onBind(Intent intent) { 46 47 Log.i("csx", "onBind"); 48 return petBinder; 49 } 50 51 @Override 52 public void onCreate() { 53 54 super.onCreate(); 55 Log.i("csx", "onCreate"); 56 petBinder = new PetBinder();// 实例化Binder 57 58 } 59 60 @Override 61 public boolean onUnbind(Intent intent) { 62 63 Log.i("csx", "onUnbind"); 64 return super.onUnbind(intent); 65 } 66 67 @Override 68 public void onDestroy() { 69 70 super.onDestroy(); 71 Log.i("csx", "onDestroy"); 72 } 73 74 }
这是我Service端的部署情况(其中MainActivity可以不用去实现,因为我们只提供服务,没有窗口显示):
第二步:部署客户端:
1.在客户端新建一个包,命名需要和服务端放置aidl文件的包名相同(我这里是com.example.remoteservice),然后把服务端的Person.java,Pet.java,Person.aidl,Pet.aidl,IPet.aidl复制到这个包下面
2.在activity中绑定远程服务进行数据交换,layout布局和activity代码如下:
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context="com.example.remoteclient.RemoteClient" > 10 11 <LinearLayout 12 android:layout_width="match_parent" 13 android:layout_height="match_parent" 14 android:orientation="vertical" > 15 16 <LinearLayout 17 android:layout_width="match_parent" 18 android:layout_height="wrap_content" 19 android:orientation="horizontal" > 20 21 <EditText 22 android:id="@+id/editText_person" 23 android:layout_width="wrap_content" 24 android:layout_height="wrap_content" 25 android:layout_gravity="bottom" 26 android:ems="10" > 27 </EditText> 28 29 <Button 30 android:id="@+id/button_ok" 31 android:layout_width="wrap_content" 32 android:layout_height="wrap_content" 33 android:layout_gravity="bottom" 34 android:text="确定" /> 35 </LinearLayout> 36 37 <ListView 38 android:id="@+id/listView_pet" 39 android:layout_width="match_parent" 40 android:layout_height="wrap_content" > 41 </ListView> 42 </LinearLayout> 43 44 </RelativeLayout>
1 package com.example.remoteclient; 2 3 import android.app.Service; 4 import android.content.ComponentName; 5 import android.content.Intent; 6 import android.content.ServiceConnection; 7 import android.os.Bundle; 8 import android.os.IBinder; 9 import android.os.RemoteException; 10 import android.support.v7.app.ActionBarActivity; 11 import android.util.Log; 12 import android.view.View; 13 import android.view.View.OnClickListener; 14 import android.widget.ArrayAdapter; 15 import android.widget.Button; 16 import android.widget.EditText; 17 import android.widget.ListView; 18 19 import com.example.remoteservice.IPet; 20 import com.example.remoteservice.Person; 21 import com.example.remoteservice.Pet; 22 23 import java.util.List; 24 25 public class RemoteClient extends ActionBarActivity { 26 27 public static final String REMOTE_SERVICE_ACTION = "com.example.remoteservice.RemoteService.ACTION"; 28 EditText editText; 29 Button button; 30 ListView listView; 31 32 IPet petService;// 声明IPet接口 33 List<Pet> pets; 34 ServiceConnection conn = new ServiceConnection() { 35 36 @Override 37 public void onServiceDisconnected(ComponentName name) { 38 Log.i("csx", "onServiceDisconnected"); 39 conn = null; 40 } 41 42 @Override 43 public void onServiceConnected(ComponentName name, IBinder service) { 44 Log.i("csx", "onServiceConnected"); 45 petService = IPet.Stub.asInterface(service);// 通过远程服务的Binder实现接口 46 47 } 48 }; 49 50 @Override 51 protected void onCreate(Bundle savedInstanceState) { 52 super.onCreate(savedInstanceState); 53 setContentView(R.layout.remote_client_layout); 54 editText = (EditText) findViewById(R.id.editText_person); 55 button = (Button) findViewById(R.id.button_ok); 56 listView = (ListView) findViewById(R.id.listView_pet); 57 58 Intent service = new Intent(); 59 service.setAction(REMOTE_SERVICE_ACTION); 60 61 bindService(service, conn, Service.BIND_AUTO_CREATE);// 绑定远程服务 62 63 button.setOnClickListener(new OnClickListener() { 64 65 @Override 66 public void onClick(View v) { 67 String personName = editText.getText().toString(); 68 if (personName == null || personName.equals("")) { 69 70 return; 71 } 72 73 try { 74 pets = petService.getPets(new Person(1, personName, personName));// 调用远程service的getPets方法 75 updataListView(); 76 77 } catch (RemoteException e) { 78 79 e.printStackTrace(); 80 } catch (NullPointerException e) { 81 e.printStackTrace(); 82 } 83 84 } 85 }); 86 87 } 88 89 public void updataListView() { 90 listView.setAdapter(null); 91 92 if (pets == null || pets.isEmpty()) { 93 return; 94 95 } 96 ArrayAdapter<Pet> adapter = new ArrayAdapter<Pet>(RemoteClient.this, 97 android.R.layout.simple_list_item_1, pets); 98 listView.setAdapter(adapter); 99 100 } 101 102 @Override 103 protected void onDestroy() { 104 105 unbindService(conn);// 解除绑定 106 super.onDestroy(); 107 } 108 109 }
到此为止所有的工作都完成了,下面我们看一下效果:我在编辑框中输入“csx”,点击确定,就会显示出服务端RemoteService中pets的相应数据。