Android学习笔记六之Service二
AIDL传递复杂数据
跨进程传递数据一般有三种方法:
- 文件,将数据保存在文件中,然后再读取,这种方式用于传递大数据
- 广播,这种方式用于传递小数据
- Service Binder机制,这种方式效率比较高,但是编写代码比较麻烦,特别是传递复制数据的时候
在上一篇中,讲过了传递简单数据的实现,只是传递int类型的数据,然后返回String数据。现在讲讲怎么用Binder机制传递复杂数据。
可以传递的类型
-
- int
-
- long
-
- bool
-
- String
-
- CharSequence
-
- android.os.Parcelable
-
- java.util.List
-
- java.util.Map
如果需要传递复杂类型,比如Student类等,就需要Parcelble接口辅助了。
Parcelable接口简介
实现对象的序列化有几个好处:可以永久性保存对象,保存对象的字节序列到本地文件中;可以用过序列化对象在网络中传递对象;可以通过序列化对象在进程间传递对象。Android系统提供了两个实现序列化接口,Parcelable和Serializable。其中Serializable接口是Java中的序列化接口。针对内存受限的移动设备,Android设计了新的序列化接口Parcelable,Parcelable的序列化和反序列化的读写都是在内存中进行的,效率比Java序列化接口Serializable使用外部存储器会高很多。但是Parcelable不能使用在将数据存储在磁盘上的情况,因为Parcelable不能很好的保存数据的持续性在外界有变化的情况下。因此在这种情况下,建议使用Serializable。Parcelable是通过IBinder通信的消息的载体。
简单的说就是:需要保存数据在本地文件或者在网络上传输的时候使用Serializable,需要在内存间传输数据使用Parcelable。Serializable类只需要实现Serializable接口,并提供一个序列化版本id(serialVersionUID)即可。而Parcelable则需要实现writeToParcel、describeContents函数以及静态的CREATOR变量,实际上就是将如何打包和解包的工作自己来定义,而序列化的这些操作完全由底层实现。
一个简单的Parcelable类
package com.example.devin.helloservice.aidl;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by Devin on 2016/6/16.
*/
public class Student implements Parcelable {
private String name;
private String sex;
public Student() {
}
public Student(String name, String sex) {
this.name = name;
this.sex = sex;
}
/**
* 必须要创建的一个常量
*/
public static final Creator<Student> CREATOR = new Creator<Student>() {
/**
* 创建一个Student对象,返回创建的对象
* @param in
* @return
*/
@Override
public Student createFromParcel(Parcel in) {
return new Student(in.readString(), in.readString());
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
/**
* 必须要实现的方法,弄不清楚什么作用,可以直接返回0
*
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 将数据写入到Parcel中的方法,注意写入顺序和读取顺序必须要相同
*
* @param dest
* @param flags
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeString(sex);
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (this == o) {
return true;
}
if (o.getClass() != getClass()) {
return false;
}
Student student = (Student) o;
if (name == null) {
if (student.name != null) {
return false;
}
} else if (!student.name.equals(name)) {
return false;
}
if (sex == null) {
if (student.sex != null) {
return false;
}
} else if (!student.sex.equals(sex)) {
return false;
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((sex == null) ? 0 : sex.hashCode());
return result;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
Parcelable接口介绍到这里,下面我们开始实现AIDL传递复杂的数据
AIDL传递复杂数据实现
传递数据的简单流程是:比如我们传递一个Student类,A进程中的Student类在A进程中打包成Parcelable,传递给B进程,B进程将Parcelable解析成Student类,B进程就可以获取Student的数据内容。
服务端的实现
创建WorkPay.aidl,只有一句代码
parcelable WorkPay;
创建WorkPay,实现Parcelable接口
package com.example.aidl_server.bean;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by Devin on 2016/6/17.
*/
public class WorkPay implements Parcelable {
private String workName;
private String workPay;
protected WorkPay(Parcel in) {
workName = in.readString();
workPay = in.readString();
}
public WorkPay(String workName, String workPay) {
this.workName = workName;
this.workPay = workPay;
}
public static final Creator<WorkPay> CREATOR = new Creator<WorkPay>() {
@Override
public WorkPay createFromParcel(Parcel in) {
return new WorkPay(in);
}
@Override
public WorkPay[] newArray(int size) {
return new WorkPay[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(workName);
dest.writeString(workPay);
}
public String getWorkName() {
return workName;
}
public void setWorkName(String workName) {
this.workName = workName;
}
public String getWorkPay() {
return workPay;
}
public void setWorkPay(String workPay) {
this.workPay = workPay;
}
}
创建一个IADILService.aidl文件,只添加一个简单的查询方法,这里的findStudent方法返回一个WorkPay,所以需要导入包,in是代表传入参数
import com.example.aidl_server.bean.WorkPay;
// Declare any non-default types here with import statements
interface IADILService {
WorkPay findStudent( in String name);
}
核心Service的编写
package com.example.aidl_server;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import com.example.aidl_server.bean.WorkPay;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Devin on 2016/6/17.
*/
public class AIDLService extends Service {
private static Map<String, WorkPay> mStudentWorkPayMap = new HashMap<>();
//创建一些数据
static {
mStudentWorkPayMap.put("zhangsan", new WorkPay("码农", "6000"));
mStudentWorkPayMap.put("lisi", new WorkPay("工程师", "6000"));
mStudentWorkPayMap.put("wangwu", new WorkPay("Android工程师", "8000"));
mStudentWorkPayMap.put("zhaoliu", new WorkPay("Android项目经理", "16000"));
mStudentWorkPayMap.put("tom", new WorkPay("Android码农", "6000"));
mStudentWorkPayMap.put("jay", new WorkPay("Java码农", "6000"));
mStudentWorkPayMap.put("san", new WorkPay("HTML5码农", "6000"));
}
IADILService.Stub mStub = new IADILService.Stub() {
@Override
public WorkPay findStudent(String name) throws RemoteException {
System.out.println("---->>"+name);
System.out.println("------>"+mStudentWorkPayMap.get(name));
return mStudentWorkPayMap.get(name);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
@Override
public void onCreate() {
super.onCreate();
System.out.println("服务被创建了");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("服务启动了");
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("服务被销毁");
}
}
在appl里面注册Service
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<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=".AIDLService">
<intent-filter>
<action android:name="android.intent.AIDLService"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
</application>
到这里,服务端的编写基本完成,但是必须要启动Service才可以
客户端的实现
将main里面的aidl文件整个拷贝过去,将WorkPay拷贝过去,记得WorkPay的路径问题。如图是客户端的目录结构
编写一个简单的布局,将查询到的数据显示
package com.example.aidl_client;
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.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.example.aidl_server.IADILService;
import com.example.aidl_server.bean.WorkPay;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
IADILService mIADILService;
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mIADILService = IADILService.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mIADILService = null;
}
};
EditText mEtEnter;
Button mBtnFind;
TextView mTvResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEtEnter = (EditText) findViewById(R.id.et_enter);
mBtnFind = (Button) findViewById(R.id.btn_find);
mTvResult = (TextView) findViewById(R.id.tv_result);
Intent intent = new Intent();
intent.setPackage("com.example.aidl_server");
intent.setAction("android.intent.AIDLService");
bindService(intent, mServiceConnection, Service.BIND_AUTO_CREATE);
mBtnFind.setOnClickListener(this);
}
/**
* 点击事件
* @param view
*/
@Override
public void onClick(View view) {
String name = mEtEnter.getText().toString();
try {
WorkPay workPay = mIADILService.findStudent(name);
mTvResult.setText(name+"的"+workPay.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
mEtEnter.setText("");
}
}
最后实现的效果如下
Service基本就到这里了,最后要注意Android5.0以后Service隐式启动问题,必须要调用这个方法intent.setPackage(),不然会报错。
笔者文笔有限,望大家谅解!