Android BluetoothGattService - 异常无法编组

时间:2016-11-13 13:31:52

标签: java android android-intent parcelable

我遇到了以下问题。

我的应用程序架构或多或少包含3个主要部分:

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

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

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

  • 选择要为其显示服务的设备
  • 使用设备发送意图并执行操作以执行BLEService
  • BLEService执行服务发现并通过服务发送意图
  • BroadcastReceiver正确接收意图并使用已接收意图中的数据开始新活动。

在最后一步有一个问题,因为我无法将ArrayList放到intent / bundle中,因为它会导致异常:

  

E / AndroidRuntime:致命异常:主要                     过程:com.projects.dawid.gattclient,PID:4133                     java.lang.RuntimeException:parcel:无法编组值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 to Broadcast receiver code

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,它实现了可引用的parcelable接口,引用了https://developer.android.com/reference/android/bluetooth/BluetoothGattService.html

2 个答案:

答案 0 :(得分:3)

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

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

本地广播从不跨越进程边界,因此不会序列化/反序列化Intent附加组件。进程间通信(全局广播,要求ActivityManager启动一个Activity)。

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

至于BluetoothGattService - 它在初始公开发布后显然没有实现Parcelable,而was retroactively changed在API 24中实现Parcelable。这意味着,有一些设备在野外,那个类没有实现Parcelable(即使它在最新Android版本的源代码中也是如此)。就二进制兼容性而言,在继承链中添加父级在技术上并不是一个重大改变 - Java类加载器不会抱怨。但是依赖于这种类的可分区性的任何代码都会在字节码验证期间失败,或者在旧版本上导致ClassCastException / ArrayStoreException。

Java泛型是not reified - 当编译为二进制代码时,ArrayList<String>ArrayList<Parcelable>看起来与ClassLoader相同:ArrayList<?>。您没有将BluetoothGattService转换为Parcelable,也不是ArrayList(在内部它将它的内容存储在Object[]中)。为了序列化ArrayList,HashMap和类似的类,Android必须使用反射,并且在您的情况下失败,因为BluetoothGattService的运行时类型不在设备上实现Parcelable。只需添加一个明确进行转换的代码(例如将BluetoothGattService置于Parcelable[]中),您就会看到。

此外,即使在设备上,它确实在API 24之前实现了Parcelable,尝试序列化BluetoothGatt服务仍然是个坏主意(这不是正式的公共API,不是CTS所涵盖的,因此完全未经测试。)

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

答案 1 :(得分:0)

您可以通过LocalBroadcastReceiver发送BluetoothGattService