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异常,导致应用崩溃。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kmblack1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值