Android学习笔记六之Service二

来源:互联网 发布:java培训班达内免费 编辑:程序博客网 时间:2024/06/16 10:55

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 */@Overridepublic int describeContents() {    return 0;}/** * 将数据写入到Parcel中的方法,注意写入顺序和读取顺序必须要相同 * * @param dest * @param flags */@Overridepublic void writeToParcel(Parcel dest, int flags) {    dest.writeString(name);    dest.writeString(sex);}@Overridepublic 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;}@Overridepublic 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];    }};@Overridepublic int describeContents() {    return 0;}@Overridepublic 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 statementsinterface 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@Overridepublic IBinder onBind(Intent intent) {    return mStub;}@Overridepublic void onCreate() {    super.onCreate();    System.out.println("服务被创建了");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {    System.out.println("服务启动了");    return super.onStartCommand(intent, flags, startId);}@Overridepublic boolean onUnbind(Intent intent) {    return super.onUnbind(intent);}@Overridepublic 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;@Overrideprotected 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 */@Overridepublic 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(),不然会报错。 
笔者文笔有限,望大家谅解!

原创粉丝点击