android 在线程之间传递对象注意事项
android 在线程之间传递对象注意事项
·
在线程之间传递对象应该尽量使用对象的副本,待传递的对象应该实现Parcelable接口。例如下面的sendToTarget方法就是不正确的,虽然它可以正常运行(可能会出莫名其妙的问题),但是不建议这样做。
//不建议的方法
private void sendToTarget(Handler handler, int action, Parcelable parcelable) throws IOException {
Message msg = handler.obtainMessage();
Bundle bundle = msg.getData();
bundle.putParcelable("xxx",parcelable);
msg.what = action;
msg.sendToTarget();
}
正确的使用方法
实现Parcelable接口的对象应该转换为字节数组(byte[]),然后再将字节数组传递给其它线程使用。代码如下:
public class SelfUtils {
//region TODO:对象序列化及反序列化
/**
* Parcelable 序列化时使用的字节缓冲区,常量虽然会占用一定的内存,但是运行性能较好
*/
private static final ByteArrayOutputStream PARCELABLE_STREAM = new ByteArrayOutputStream(256);
/**
* 将对象转换为字节数组
*
* @param parcelable 转换对象
* @return 字节数组
* @throws IOException IOException
*/
public static byte[] bytesFromParcel(Parcelable parcelable) throws IOException {
Parcel parcel = Parcel.obtain();
PARCELABLE_STREAM.reset();
parcelable.writeToParcel(parcel, 0);
PARCELABLE_STREAM.write(parcel.marshall());
PARCELABLE_STREAM.flush();
return PARCELABLE_STREAM.toByteArray();
}
/**
* 字节数组转换为对象
*
* @param data 字节数组
* @return Parcel对象
*/
public static Parcel bytesToParcel(byte[] data) {
Parcel parcel = Parcel.obtain();
parcel.unmarshall(data, 0, data.length);
parcel.setDataPosition(0);
return parcel;
}
//endregion
}
线程A
private void sendToTarget(Handler handler, int action, Parcelable parcelable) throws IOException {
Message msg = handler.obtainMessage();
Bundle bundle = msg.getData();
bundle.putByteArray(ASYNC_RESULT, SelfUtils.bytesFromParcel(parcelable));
msg.what = action;
msg.sendToTarget();
}
//使用方法:将对象SampleObject的副本传递给其它线程使用,SampleObject已经实现了Parcelable接口。
//其中ActionCode.System.CLEAR_CACHE为自定义的任务号,它是一个整数,你可以使用任何一个数字。
//SampleObject obj = new SampleObject();
this.sendToTarget(handler, ActionCode.System.CLEAR_CACHE, obj);
线程B
在线程B的消息循环处理中,接收来自线程A发送的消息(消息任务号为:ActionCode.System.CLEAR_CACHE),然后将SampleObject对象反序列化后得到SampleObject副本。
//region TODO 消息循环处理
Handler handlerMessage() {
return new Handler(requireContext().getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
//在消息循环中获取Context对象不使用requireContext()
//应该使用getContext(),然后检查它的返回值是否为null。
Context context = getContext();
if (null == context)
return;
try {
switch (msg.what) {
case ActionCode.System.CLEAR_CACHE:
do {
Bundle bundle = msg.getData();
byte[] data = bundle.getByteArray(ServiceAsync.ASYNC_RESULT);
//反序列化SampleObject
SampleObject obj = new SampleObject (SelfUtils.bytesToParcel(data));
} while (false);
break;
} catch (Exception e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
};
}
//endregion
总结
- 在线程之间传递数据最好使用原始数据类型,例如int、long、bytes、String。尽量不要使用其它相对高级的数据类型。
- 在消息循环中获取Context对象应该使用getContext()方法。因为当Fragment消息循环时,在消息到达时刚好Fragment从Activity中分离(onDetach方法执行完成),此时消息循环使用requireContext获取的Context 对象为null, requireContext在内部会检查Context是否为null,为null时会抛出IllegalStateException异常,导致应用崩溃。
更多推荐
已为社区贡献2条内容
所有评论(0)