[Android] Bluetooth Gatt 객체에다가 Parcelable 안쓰는게 좋은이유

3 분 소요

There are two issues in play here: one causes everything to work fine when local broadcasts are used, another one makes serialization of BluetoothGattService fail.


In order to pass data between two processes, it must be serialized (written to Parcel). Bundle contents are serialized/deserialized lazily — as long as Bundle object does not cross process boundaries or get stored on disk, all contained objects will be stored in memory (within a HashMap) and won't get serialized.


Local broadcasts never cross process boundaries, thus do not serialize/deserialize Intent extras. Inter-process communications (global broadcasts, asking ActivityManager to start an Activity) do.


In order to be passed between processes, Intent extras must be either Parcelable or Serializable, otherwise Android may treat them as opaque objects under some circumstances, and will attempt to determine approach to their serialization (as Serializable/Parcelable/String etc.) at runtime (see writeValue) — and fail.


As for BluetoothGattService — it clearly didn't implement Parcelable after initial public release and was retroactively changed to implement Parcelable in API 24. This means, that there are devices in the wild, where that class does not implement Parcelable (even if it does in the source code of the latest Android version). Adding a parent to inheritance chain is not technically a breaking change as far as binary compatibility is concerned — Java Classloaders won't complain. But any code, that relies on parcelability of such class, will either fail during bytecode validation or cause ClassCastException/ArrayStoreException on older versions.


Java generics are not reified — when compiled to binary code, ArrayList<String> and ArrayList<Parcelable> look to ClassLoader the same: as ArrayList<?>. You are not casting BluetoothGattService to Parcelable and neither is ArrayList (internally it stores it's contents in Object[]). In order to serialize ArrayList, HashMap and similar classes, Android have to use reflection, and that fails in your case, because the runtime type of BluetoothGattService does not implement Parcelable on the device. Just add a code, that explicitly makes a cast (such as placing BluetoothGattService in Parcelable[]) and you will see.


Also, even on devices, where it does happen to implement Parcelable prior to API 24, trying to serialize a BluetoothGattService is still bad idea (that wasn't officially public API, wasn't covered by CTS, and thus is completely untested).


I recommend you to refrain from passing BluetoothGattService via parcelization and find other way to approach your task (such as passing it's contents or storing an instance in singlton).


========================================================



여기에는 두 가지 문제가 있습니다. 하나는 로컬 브로드 캐스트를 사용할 때 모든 것이 제대로 작동하도록하고 다른 하나는 BluetoothGattService의 직렬화를 실패하게 만듭니다.


두 프로세스간에 데이터를 전달하려면 직렬화 (Parcel에 기록)해야합니다. 번들 내용은 느리게 직렬화 / 역 직렬화됩니다. Bundle 객체가 프로세스 경계를 ​​넘지 않거나 디스크에 저장되지 않는 한 포함 된 모든 객체는 메모리 (HashMap 내)에 저장되고 직렬화되지 않습니다.


로컬 브로드 캐스트는 프로세스 경계를 ​​넘지 않으므로 인 텐트 엑스트라를 직렬화 / 역 직렬화하지 않습니다. 프로세스 간 통신 (전역 브로드 캐스트, ActivityManager에게 활동 시작을 요청)은 수행합니다.


프로세스간에 전달 되려면 인 텐트 엑스트라가 Parcelable 또는 Serializable이어야합니다. 그렇지 않으면 Android가 일부 상황에서이를 불투명 한 객체로 취급하고 런타임시 직렬화에 대한 접근 방식 (Serializable / Parcelable / String 등)을 결정하려고 시도합니다. (writeValue 참조) — 실패합니다.


BluetoothGattService의 경우 — 최초 공개 이후에 Parcelable을 구현하지 않았으며 API 24에서 Parcelable을 구현하도록 소급 변경되었습니다. 즉, 해당 클래스가 Parcelable을 구현하지 않는 장치가 있습니다. 최신 Android 버전의 소스 코드). 상속 체인에 부모를 추가하는 것은 바이너리 호환성에 관한 한 기술적으로 주요 변경 사항이 아닙니다. Java 클래스 로더는 불평하지 않습니다. 그러나 이러한 클래스의 소포성에 의존하는 모든 코드는 바이트 코드 유효성 검사 중에 실패하거나 이전 버전에서 ClassCastException / ArrayStoreException을 발생시킵니다.


Java 제네릭은 수정되지 않습니다. 바이너리 코드로 컴파일 할 때 ArrayList <String> 및 ArrayList <Parcelable>은 ClassLoader를 ArrayList <?>와 동일하게 찾습니다. BluetoothGattService를 Parcelable로 캐스팅하지 않고 ArrayList도 아닙니다 (내부적으로 Object []에 해당 내용을 저장함). ArrayList, HashMap 및 유사한 클래스를 직렬화하려면 Android에서 리플렉션을 사용해야하며 BluetoothGattService의 런타임 유형이 장치에서 Parcelable을 구현하지 않기 때문에 귀하의 경우 실패합니다. 명시 적으로 캐스트를 만드는 코드를 추가하면됩니다 (예 : Parcelable []에 BluetoothGattService 배치).


또한 API 24 이전에 Parcelable을 구현 한 기기에서도 BluetoothGattService를 직렬화하려는 시도는 여전히 나쁜 생각입니다 (공식적으로 공개 API가 아니었고 CTS에서 다루지 않았으므로 완전히 테스트되지 않았습니다).


Parcelization을 통해 BluetoothGattService를 전달하는 것을 자제하고 작업에 접근하는 다른 방법을 찾는 것이 좋습니다 (예 : 내용 전달 또는 singlton에 인스턴스 저장).




https://stackoverflow.com/questions/40574384/android-bluetoothgattservice-exception-cannot-marshall



요약 : 싱글톤 인스턴스로 구현하는게 더 좋다고함