【问题标题】:Android BluetoothGattService - exception cannot marshallAndroid BluetoothGattService - 异常无法编组
【发布时间】:2017-03-27 05:49:26
【问题描述】:

我遇到了以下问题。

我的应用程序架构或多或少由 3 个主要部分组成:

  • 设备视图(显示可用的 BLE 设备)
  • BLE 服务(处理 BLE 连接和数据)
  • 服务视图(显示特定设备的服务)

我的问题是第二次将 BluetoothGATTService 传递给意图(第一次有效)。

或多或少的动作是这样的:

  • 选择要为其显示服务的设备
  • 向 BLEService 发送带有设备和操作的意图
  • BLEService 执行服务发现并将意图与服务一起发送回
  • BroadcastReceiver 正确接收 Intent 并使用接收到的 Intent 中的数据开始新的活动。

最后一步出现问题,因为我无法将 ArrayList 放入意图/捆绑包,因为它会导致异常:

E/AndroidRuntime: 致命异常: main 进程:com.projects.dawid.gattclient,PID:4133 java.lang.RuntimeException:包裹:无法编组值 android.bluetooth.BluetoothGattService@ef62956 在 android.os.Parcel.writeValue(Parcel.java:1337) 在 android.os.Parcel.writeList(Parcel.java:711) 在 android.os.Parcel.writeValue(Parcel.java:1284) 在 android.os.Parcel.writeArrayMapInternal(Parcel.java:638) 在 android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1313) 在 android.os.Bundle.writeToParcel(Bundle.java:1096) 在 android.os.Parcel.writeBundle(Parcel.java:663) 在 android.content.Intent.writeToParcel(Intent.java:7838) 在 android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:2530)

对我来说这很奇怪,好像操作不受支持,那么我们会在从 BLEService 发送服务的 ArrayList 到 BroadcastReceiver 时得到这个异常,而不是现在。

源代码:

BLEService 广播接收器代码

private void notifyServicesDiscovered(BluetoothGatt gatt) {
    Intent intent = new Intent();
    intent.setAction(BLEService.RESPONSE);
    intent.putExtra(BLEService.RESPONSE, BLEService.Responses.SERVICES_DISCOVERED);
    intent.putExtra(BLEService.Responses.DEVICE, gatt.getDevice());
    intent.putParcelableArrayListExtra(BLEService.Responses.SERVICES_LIST, getArrayListServices(gatt));
    LocalBroadcastManager.getInstance(mServiceContext).sendBroadcast(intent);
}

@NonNull
    private ArrayList<BluetoothGattService> getArrayListServices(BluetoothGatt gatt) {
        ArrayList<BluetoothGattService> services = new ArrayList<>();
        services.addAll(gatt.getServices());
        return services;
    }

这很好用。现在

广播接收器到新的活动代码

在识别处理此方法的正确意图后调用

private void createViewWithServices(Intent intent) {
    Log.i(TAG, "creating new activity!");

    BluetoothDevice device = intent.getParcelableExtra(BLEService.Responses.DEVICE);
    ArrayList<BluetoothGattService> services =  intent.getParcelableArrayListExtra(BLEService.Responses.SERVICES_LIST);

    Intent serviceShowIntent = new Intent(mActivityContext, ServiceShowActivity.class);
    serviceShowIntent.putExtra(BLEService.Responses.DEVICE, device);
    serviceShowIntent.putParcelableArrayListExtra(BLEService.Responses.SERVICES_LIST, services); //this line causes error
    mActivityContext.startActivity(serviceShowIntent); // here exception is thrown
}

谁能解释一下这背后的奥秘?我只是不明白为什么第一个代码很好,而第二个代码却因异常而失败。

已经尝试过很多不同的方法,但都失败了。我什至在意图之间交换包,因为它们的内容相同,但这也失败了,复制列表项没有区别。

编辑

指的是AndroidRuntime error: Parcel: unable to marshal value 错误。不同的是,我使用的是android本身提供的类的对象,即BluetoothGATTService,它确实实现了引用https://developer.android.com/reference/android/bluetooth/BluetoothGattService.html的parcelable接口

【问题讨论】:

  • 我明白,但是 BluetoothGATTService 实现 parcelable 见developer.android.com/reference/android/bluetooth/…
  • 公平的,我会写一个正确的答案。
  • 我已经详细说明了可能的原因,为什么您可以看到有问题的行为。我无法撤消标记,但我们希望版主决定支持保持问题开放。

标签: java android android-intent parcelable


【解决方案1】:

这里有两个问题:一个导致使用本地广播时一切正常,另一个导致 BluetoothGattService 的序列化失败。


为了在两个进程之间传递数据,必须对其进行序列化(写入Parcel)。 Bundle 内容被延迟序列化/反序列化——只要 Bundle 对象不跨越进程边界或存储在磁盘上,所有包含的对象都将存储在内存中(在 HashMap 中)并且不会被序列化。

本地广播永远不会跨越进程边界,因此不会序列化/反序列化 Intent extra。进程间通信(全局广播,要求 ActivityManager 启动 Activity)。

为了在进程之间传递,Intent extra 必须是 Parcelable 或 Serializable,否则在某些情况下 Android 可能会将它们视为不透明对象,并会尝试确定它们的序列化方法(如 Serializable/Parcelable/String 等)。 ) 在运行时(参见writeValue)——然后失败。


至于 BluetoothGattService——它显然在首次公开发布后没有实现 Parcelable,was retroactively changed 在 API 24 中实现 Parcelable。这意味着,在野外存在设备,该类没有实现 Parcelable(即使它在最新的 Android 版本的源代码中执行)。就二进制兼容性而言,将父级添加到继承链在技术上并不是一个重大变化——Java 类加载器不会抱怨。但是任何依赖于此类的可打包性的代码,要么在字节码验证期间失败,要么在旧版本上导致 ClassCastException/ArrayStoreException。

Java 泛型是 not reified — 当编译为二进制代码时,ArrayList&lt;String&gt;ArrayList&lt;Parcelable&gt; 在 ClassLoader 中的外观相同:与 ArrayList&lt;?&gt;。您没有将 BluetoothGattService 转换为 Parcelable,ArrayList 也不是(它在内部将其内容存储在 Object[] 中)。为了序列化 ArrayList、HashMap 和类似的类,Android 必须使用反射,这在你的情况下会失败,因为 BluetoothGattService 的运行时类型没有在设备上实现 Parcelable。只需添加一个代码,显式进行强制转换(例如将 BluetoothGattService 放在 Parcelable[] 中),您就会看到。

此外,即使在 API 24 之前确实实现 Parcelable 的设备上,尝试序列化 BluetoothGattService 仍然是个坏主意(这不是正式的公共 API,没有被 CTS 覆盖,因此完全未经测试)。

我建议您不要通过 parcelization 传递 BluetoothGattService 并寻找其他方式来处理您的任务(例如传递它的 contents 或将实例存储在 singlton 中)。

【讨论】:

  • 我永远不会自己找到它。非常感谢!
【解决方案2】:

您可以通过LocalBroadcastReceiver发送BluetoothGattService

【讨论】:

  • 单行解决方案应在评论中发布。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-16
  • 2021-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多