【问题标题】:How to broadcast a parcelable object in an intent?如何在意图中广播可打包对象?
【发布时间】:2013-11-11 17:20:08
【问题描述】:

我是 Java / Android 的新手,正在尝试开发一个与 USB CAN 总线适配器通信的安卓应用程序。我通过我的 CANBusController 类以字符串形式接收来自适配器的消息,并且我构建了一个可打包类 CANMessage,它将接收到的字符串转换为我可以使用的形式(int Id、int Length、byte[] 数据)。现在我正在尝试显示我收到的消息。我有一个片段 CANBusControlFragment,它声明了一个 BroadcastReceiver。当收到一条消息时,我构建我的 CANMessage,将它及其字符串放入一个包中,将包放入一个 Intent,然后使用 sendBroadcast(intent):

CANBusController
{
    public void handleMessage(Message msg)
    {
        Bundle bundle = msg.getData();
        CANMessage canMsg = bundle.getParcelable("CAN_MESSAGE");
        if (canMsg != null)
        {
            Intent intent = new Intent();
            intent.setAction(BroadcastActions.CAN_MESSAGE_RECEIVED);
            //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.putExtra("CAN_MESSAGE", canMsg);
            intent.putExtra("STRING_MESSAGE", canMsg.toString());
            parentContext.sendBroadcast(intent);
        }
    }
}

CANBusControlFragment extends Fragment
{
    private final BroadcastReceiver canMsgReceiver = new BroadcastReceiver()
    {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            String action = intent.getAction();
            Bundle bundle = intent.getExtras();
            try
            {
                if (BroadcastActions.CAN_MESSAGE_RECEIVED.equals(action))
                {
                    synchronized(this)
                    {
                        String str = bundle.getString("STRING_MESSAGE"); // <-- Crash
                        CANMessage canMsg = bundle.getParcelable("CAN_MESSAGE"); // 
                        Append(str);
                        Append(canMsg.toString();
                    }
                }
            }
            catch (Exception ex)
                print exception
        }
    }
}

如果我将 CANMessage 添加到包中,当我尝试 getString 或 getParcelable 时,我的应用程序将在 BroadcastReceiver 的 onReceived 方法中崩溃并出现空指针异常。如果我不包含 CANMessage,那么应用程序将正常工作。我相信 CANMessage 正确地实现了 Parcelable,因为我已经通过执行除了广播消息之外的所有相同步骤来测试它:构建 CANMessage,将 Parcelable 放入 Bundle,将 Bundle 放入 Intent,将 Bundle 从 Intent 中取出,getParcelable 从捆绑。

感谢您的帮助。谢谢:)

编辑 - LogCat 信息: 看来错误出在我的包裹方法中。尝试 readByteArray(byte[]) 会使应用程序崩溃。

11-12 09:17:44.568: E/AndroidRuntime(3087): FATAL EXCEPTION: main

11-12 09:17:44.568: E/AndroidRuntime(3087): java.lang.RuntimeException: Error receiving broadcast Intent { act=com.sb2tablet.CAN_MESSAGE_RECEIVED flg=0x10000010 (has extras) } in com.sb2tablet.CANBusControlFragment$1@9e8f6e00
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:768)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.os.Handler.handleCallback(Handler.java:725)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.os.Handler.dispatchMessage(Handler.java:92)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.os.Looper.loop(Looper.java:137)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.app.ActivityThread.main(ActivityThread.java:5041)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at java.lang.reflect.Method.invokeNative(Native Method)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at java.lang.reflect.Method.invoke(Method.java:511)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at dalvik.system.NativeStart.main(Native Method)
11-12 09:17:44.568: E/AndroidRuntime(3087): Caused by: java.lang.NullPointerException
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.os.Parcel.readByteArray(Parcel.java:1594)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at com.sb2tablet.models.CANMessage.<init>(CANMessage.java:93)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at com.sb2tablet.models.CANMessage$1.createFromParcel(CANMessage.java:18)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at com.sb2tablet.models.CANMessage$1.createFromParcel(CANMessage.java:1)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.os.Parcel.readParcelable(Parcel.java:2103)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.os.Parcel.readValue(Parcel.java:1965)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.os.Parcel.readMapInternal(Parcel.java:2226)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.os.Bundle.unparcel(Bundle.java:223)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.os.Bundle.containsKey(Bundle.java:271)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.content.Intent.hasExtra(Intent.java:4121)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at com.sb2tablet.CANBusControlFragment$1.onReceive(CANBusControlFragment.java:51)
11-12 09:17:44.568: E/AndroidRuntime(3087):     at android.app.LoadedApk$ReceiverDispatcher$Args.run(LoadedApk.java:758)

编辑 - writeToParcel 和 Parcel in:

public void writeToParcel(Parcel out, int flags)
{
    out.writeValue(id);    // int
    out.writeValue(len);   // int
    out.writevalue(data);  // byteArray, the value is not null here
    //out.writeByArray(data);
}

public CANMessage(Parcel in)
{
    id = in.readInt();
    len = in.readInt();
    in.readByteArray(data); // Actual crash is here
}

编辑 - 测试用例 1 - 我的包裹中似乎添加了额外的数据,我不确定如何。

public void writeToParcel(Parcel out, int flags)
{
    out.writeValue(id);    // int = 256
    out.writeValue(len);   // int = 2, length of byte array
    out.writevalue(data);  // byte array = { 17, 51 }
}

public CANMessage(Parcel in)
{
    len = in.readInt();        // 1
    id = in.readInt();         // 256
    len = in.readInt();        // 1
    len = in.readInt();        // 2
    int i = in.readInt();      // 13
    if (data == null) data = new byte[len];
    in.readByteArray(data);    // data = { 17, 51 }
}

编辑 - 测试用例 2

public void writeToParcel(Parcel out, int flags)
{
    out.writeValue(id);       // int = 256
    out.writeValue(len);      // int = 2, length of byte array
    out.writeByteArray(data); // byte array = { 17, 51 }
}

public CANMessage(Parcel in)
{
    len = in.readInt();        // 1
    id = in.readInt();         // 256
    len = in.readInt();        // 1
    len = in.readInt();        // 2
    len = in.readInt();        // 2
    if (data == null) data = new byte[len];
    // in.readByteArray(data); // Crashes
    int i = in.readInt();      // 13073 = 0x3311 = byte[] { 17, 51 }
}

【问题讨论】:

  • 从 logcat 发布错误。您是否收到“找不到类”异常?
  • 刚刚发布了 logcat 错误。看来我不太了解包裹:)
  • Parcel 只是一种序列化技术。这实际上是一个很常见的问题。看我的回答。

标签: android android-intent android-broadcast


【解决方案1】:

当您读取一个字节数组时,您需要事先分配一个(至少)正确大小的字节数组。像这样:

byte[] blah = new byte[100];
in.readByteArray(blah);

另外,您不想使用writeValue() 将原语写入Parcel。使用适当的方法。示例:

out.writeInt(id);    // int = 256
out.writeInt(len);   // int = 2, length of byte array
out.writeByteArray(data);  // byte array = { 17, 51 }

您必须始终将write() 方法与包裹上相同类型的read() 方法配对。例如,如果您使用writeValue()Parcel 写入内容,则必须使用readValue() 读取它。如果您知道变量是int,那么您应该使用writeInt() 编写它并使用readInt() 读取它。如果您使用writeValue() 编写它,那么对象的“类型”也会写入Parcel,以便readValue() 知道它是什么类型的对象。这就是您在 Parcel 中看到额外数据的原因。

【讨论】:

  • 额外的数据让我陷入了循环。我假设 writeValue() 会调用原始类型的相应函数。我的代码现在就像一个魅力,谢谢!
  • writeValue() 用于将通用“事物”写入Parcel。它首先写入一个整数,指示随后的“事物”的种类,然后写入数据。这样,它也可以从Parcel 中读取一个通用的“事物”。它首先读取一个整数,然后对其进行检查以确定接下来会出现哪种“事物”,然后读取并创建适当的“事物”。
  • @DavidWasser 这有点跑题了,但是说到 Parcelable 和广播接收器,您认为将“事物”数组传递给您的活动的方式是什么?我的意思是“Parcelable”概念主要是为 IPC 设计的,对吧?
  • @zgulser Parcelable 是一种序列化技术。它用于将对象从一个组件传递到另一个组件。这些组件可能位于不同的进程 (IPC) 中,也可能位于同一进程中。那是无关紧要的。 Android 使用Parcelable 概念将Intents 保存到持久存储中(因此它可以杀死您的进程并在以后用户返回时启动新进程,并恢复原始Intent)。您可以使用Parcelable 在组件之间发送一系列事物。如果你有大量的东西,有更好的方法......
  • ...比如使用共享内存(static 成员变量)、SharedPreferences、SQLite 数据库或普通文件。每种机制都有其优点和缺点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-15
  • 1970-01-01
  • 1970-01-01
  • 2010-12-07
相关资源
最近更新 更多