最近做跨进程下载时遇到一个问题差点引起血案,所以在此记录一下。
问题是这样的:客户端调用下载库下载视频,下载库是在一个独立的进程里运行。下载库需要通过Binder回调客户端获取下载地址,然后下载库获取到下载地址进行视频下载。遇到的问题是下载库通过Binder回调客户端获取到的下载地址为null。
此问题涉及到两个进程里的三个线程,两个进程分别是客户端进程和下载库独立进程。三个线程分别是客户端获取视频列表的子线程ThreadA、下载库里的任务下载线程ThreadB以及在ThreadB中通过Binder调用客户端的回调方法,因为通过Binder调用的方法不会在ThreadB中而是在BinderC线程中。因为是在ThreadB中调用BinderC线程,这就涉及到线程同步问题。所以就以为是线程不同步造成烦人获取不到下载地址问题。解决了ThreadB与BinderC的同步问题,发现通过Binder还是回调得不到下载地址。排除线程同步问题。
因为不是所有的视频下载都获取不到下载地址,所以就想是不是AIDL传输时数据格式的问题。 查了下资料AIDL支持的数据格式有:
基本数据类型(int,long,char,boolean,double等)
String和CharSequence
List:只支持ArrayList,而且list中的元素也必须是AIDL支持的类型
Map:只支持HashMap,里面的key和value也必须是AIDL支持的类型
Parceable:所有实现了Parceable接口的对象
看到这里恍然大悟,与下载库进程通信的数据对象里包含一个枚举类型,而通过Parceable序列化对象时是自动过滤掉枚举类型的。下载库通过AIDL的方式获取客户端的下载地址的方法里正好是通过这个枚举类型判断的,因为Parceable序列化时过滤了枚举类型的字段,所以得到的结果为null。
造成获取不到下载地址的根本原因是AIDL通信时包含枚举类型是该怎么处理。
下面给出包含枚举类型字段的Parceable对象的正确实现
public class DownLoadInfo implements Parcelable {
public int id;
public String download_url;
public DoWnLoadType doWnLoadType;
protected DownLoadInfo(Parcel in) {
id = in.readInt();
download_url = in.readString();
//使用该方式来读取枚举的值。。。Parcelable默认不实现
doWnLoadType = DoWnLoadType.values()[in.readInt()];
}
public static final Creator<DownLoadInfo> CREATOR = new Creator<DownLoadInfo>() {
@Override
public DownLoadInfo createFromParcel(Parcel in) {
return new DownLoadInfo(in);
}
@Override
public DownLoadInfo[] newArray(int size) {
return new DownLoadInfo[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(id);
parcel.writeString(download_url);
//使用该方式来写入枚举。。。Parcelable默认不实现 parcel.writeInt(doWnLoadType.ordinal());
}
public enum DoWnLoadType {
Shark, Koo;
}
}