Android Servie AIDL 复杂数据
上一章节中我们传递的是 int
类型的参数,然后服务端返回一个 String
类型的参数,很简单,但是实际开发中,传递的往往是复杂的数据类型
本章节我们就来学习如何使用 AIDL
Service 向服务端传递复杂数据类型的数据
Parcelable 接口
Parcelable
和 Serializable
一样,都是用于序列化的,只是 Parcelable 更加轻量级,速度更快
但是写起来就有点麻烦了,复杂无比,好在有工具 Android Parcelable Code Generator 来完成序列化
-
首先实现
writeToParcel()
和readFromPacel()
方法写入方法将对象写入到包裹 (parcel) 中
读取方法则从包裹中读取对象
注意: 写入属性顺序需与读取顺序相同
-
接着在该类中添加一个名为
CREATOR
的static final
属性,该属性需要实现android.os.Parcelable.Creator
接口 -
重写接口中的两个方法
-
createFromParcel(Parcel source)
方法实现从 source 创建出 JavaBean 实例的功能
-
newArray(int size)
创建一个类型为
T
,长度为size
的数组,只有一个简单的return new T[size]
;
-
-
最后创建
describeContents()
直接返回 0 即可
注意
非原始类型 中除了 String 和 CharSequence 以外,其余均需要一个 方向指示符
方向指示符包括 in 、 out 、和 inout
方向符 | 说明 |
---|---|
in | 表示由客户端设置 |
out | 表示由服务端设置 |
inout | 表示客户端和服务端都设置了该值 |
范例
自定义两种对象类型 Person 与 Salary
- Person 作为调用远程的 Service 的参数
- Salary 作为返回值
先来看看最后的效果图
创建服务端
-
创建一个 空的 Android 项目
cn.twle.android.MixedServer
-
创建
AIDL
目录,在app
目录上点右键,选择NEW -> Folder -> AIDL Folder
然后点Finish
-
在
app/aidl
目录下新建包cn.twle.android.aidl
-
在
app/aidl
目录下的包cn.twle.android.aidl
上点右键,然后选择New->AIDL->AIDL File
分别创建两个文件Person.aidl
和Salary.aidl
它们需要实现 Parcelable 接口
Person.aidl
parcelable Person;
Salary.aidl
parcelable Salary;
-
在
app/aidl
目录下的包cn.twle.android.aidl
上点右键,然后选择New->AIDL->AIDL File
创建文件ISalary.aidl
在里面写一个简单的获取工资信息的方法package cn.twle.android.aidl; import cn.twle.android.aidl.Salary; import cn.twle.android.aidl.Person; interface ISalary { //定义一个Person对象作为传入参数 //接口中定义方法时,需要制定新参的传递模式,这里是传入,所以前面有一个in Salary getMsg(in Person owner); }
-
然后点击
Build->Make Project
先生成我们需要的ISalary.java
这时候会报错,提示Person.java
和Salary.java
不存在,没关系 -
在
app/java
目录下新建包cn.twle.android.aidl
-
在
app/java
的包cn.twle.android.aidl
创建文件Person.java
和Salary.java
,需要实现 Parcelable 接口,重写对应的方法因为我们后面是根据 Person 对象来获取 Map 集合中的数据,所以 Person.java 中我们重写了 hashcode 和 equals 的方法;而 Salary 类则不需要
Person.java
package cn.twle.android.aidl; import android.os.Parcel; import android.os.Parcelable; public class Person implements Parcelable{ private Integer id; private String name; public Person() {} public Person(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public String getName() { return name; } //实现 Parcelable 必须实现的方法 @Override public int describeContents() { return 0; } //写入数据到Parcel中的方法 @Override public void writeToParcel(Parcel dest, int flags) { //把对象所包含的数据写入到parcel中 dest.writeInt(id); dest.writeString(name); } //必须提供一个名为CREATOR的static final属性 该属性需要实现 //android.os.Parcelable.Creator<T> 接口 public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() { //从Parcel中读取数据,返回Person对象 @Override public Person createFromParcel(Parcel source) { return new Person(source.readInt(),source.readString()); } @Override public Person[] newArray(int size) { return new Person[size]; } }; //因为我们集合取出元素的时候是根据Person对象来取得,所以比较麻烦, //需要我们重写hashCode()和equals()方法 @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
Salary.java
package cn.twle.android.aidl; import android.os.Parcel; import android.os.Parcelable; public class Salary implements Parcelable { private String type; private Integer salary; public Salary() { } public Salary(String type, Integer salary) { this.type = type; this.salary = salary; } public String getType() { return type; } public Integer getSalary() { return salary; } public void setType(String type) { this.type = type; } public void setSalary(Integer salary) { this.salary = salary; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(type); dest.writeInt(salary); } public static final Parcelable.Creator<Salary> CREATOR = new Parcelable.Creator<Salary>() { //从Parcel中读取数据,返回Person对象 @Override public Salary createFromParcel(Parcel source) { return new Salary(source.readString(), source.readInt()); } @Override public Salary[] newArray(int size) { return new Salary[size]; } }; public String toString() { return "工作:" + type + " 薪水: " + salary; } }
-
然后点击
Build-> rebuild project
重新编译,这时候是成功的 -
在
MainActivity.java
目录下新建文件MsAidlService.java
定义一个 SalaryBinder 类继承 Stub,从而实现 ISalary 和 IBinder 接口;定义一个存储信息的 Map 集合
重载 onBind() 方法,返回 SalaryBinder 类的对象实例
package cn.twle.android.mixedserver; import java.util.HashMap; import java.util.Map; import cn.twle.android.aidl.ISalary.Stub; import cn.twle.android.aidl.Person; import cn.twle.android.aidl.Salary; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class MsAidlService extends Service { private SalaryBinder salaryBinder; private static Map<Person,Salary> ss = new HashMap<Person, Salary>(); //初始化Map集合,这里在静态代码块中进行初始化,当然你可也以在构造方法中完成初始化 static { ss.put(new Person(1, "jav"), new Salary("码农", 2000)); ss.put(new Person(2, "gen"), new Salary("歌手", 20000)); ss.put(new Person(3, "xm"), new Salary("学生", 20)); ss.put(new Person(4, "mrwang"), new Salary("老师", 2000)); } @Override public void onCreate() { super.onCreate(); salaryBinder = new SalaryBinder(); } @Override public IBinder onBind(Intent intent) { return salaryBinder; } //同样是继承Stub,即同时实现ISalary接口和IBinder接口 public class SalaryBinder extends Stub { @Override public Salary getMsg(Person owner) throws RemoteException { return ss.get(owner); } } @Override public void onDestroy() { System.out.println("服务结束!"); super.onDestroy(); } }
-
修改
AndroidManifest.xml
注册 Service<service android:name=".MsAidlService"> <intent-filter> <action android:name="cn.twle.android.aidl.MsAidlService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
先运行一下
客户端
-
创建一个 空的 Android 项目
cn.twle.android.MixedClient
-
重复上面的 [2-9] 步骤
-
先 build 一下
-
修改
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请输入要查询的人的姓名" /> <EditText android:id="@+id/ipt_search" android:inputType="text" android:layout_width="match_parent" android:layout_height="wrap_content"/> <Button android:id="@+id/btn_query" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="查询"/> <TextView android:id="@+id/txt_show" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout>
-
修改
MainActivity.java
定义一个ServciceConnection
对象, 重写对应方法,和前面的普通数据的类似package cn.twle.android.mixedclient; import cn.twle.android.aidl.ISalary; import cn.twle.android.aidl.Person; import cn.twle.android.aidl.Salary; import android.app.Activity; 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.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity { private ISalary salaryService; private Button btn_query; private EditText ipt_search; private TextView txt_show; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { salaryService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { //返回的是代理对象,要调用这个方法哦! salaryService = ISalary.Stub.asInterface(service); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_query = (Button) findViewById(R.id.btn_query); ipt_search = (EditText) findViewById(R.id.ipt_search); txt_show = (TextView) findViewById(R.id.txt_show); Intent it = new Intent("cn.twle.android.aidl.MsAidlService"); it.setPackage("cn.twle.android.mixedserver"); bindService(it, conn, Service.BIND_AUTO_CREATE); btn_query.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { String name = ipt_search.getText().toString(); Salary salary = salaryService.getMsg(new Person(1,name)); txt_show.setText(name + salary.toString()); }catch(RemoteException e){e.printStackTrace();} } }); } @Override protected void onDestroy() { super.onDestroy(); this.unbindService(conn); } }