Android学习笔记六之Service二

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的路径问题。如图是客户端的目录结构

Android学习笔记六之Service二_第1张图片

编写一个简单的布局,将查询到的数据显示

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("");
}
}

最后实现的效果如下

Android学习笔记六之Service二_第2张图片

Service基本就到这里了,最后要注意Android5.0以后Service隐式启动问题,必须要调用这个方法intent.setPackage(),不然会报错。
笔者文笔有限,望大家谅解!

你可能感兴趣的:(android,数据,aidl)