Which Serialization Wins? Json vs Protocol Buffers vs FlatBuffers on Android, Flutter & H5
This article presents a real‑device performance comparison of Json, Protocol Buffers and FlatBuffers across Android, Flutter and H5 channels, explains their internal structures, shows serialization/deserialization code, and provides practical guidance on choosing the right format for mobile data transmission.
1. Test Sample Overview
Test object: 8‑level depth, 150 fields, representing a complex real‑world payload.
Json payload size ≈ 4 KB.
Protocol Buffers payload size ≈ 2 KB.
FlatBuffers size lies between Protocol Buffers and Json.
2. Structure Analysis
2.1 Json
1 "user":{ "name":"mary" }Json is a key‑value text format. During (de)serialization libraries such as Gson use reflection and object traversal, creating temporary objects and consuming memory, which makes it relatively inefficient.
Gson reflects the target Java class, builds a BoundField for each field and selects a suitable TypeAdapter. JsonReader parses the raw JSON string.
The reader iterates tokens, uses the appropriate TypeAdapter to convert values and assign them to object fields.
2.2 Protocol Buffers
1 message User {
2 string name = 1;
3 }Protocol Buffers (ProtoBuf) use a compact binary TLV format. The tag is encoded as field_number << 3 | wire_type. Various wire types (varint, length‑delimited, etc.) provide efficient encoding for integers, booleans, strings, embedded messages, and repeated fields.
Varint types store the value without a length prefix, using a variable‑length encoding.
Length‑delimited types store a length followed by raw bytes (e.g., strings, embedded messages).
Deserialization is order‑independent; the decoder reads tags and reconstructs a map or object.
2.3 FlatBuffers
FlatBuffers store data in a forward‑compatible, offset‑based binary layout. Tables support optional fields and can evolve without breaking existing readers.
Serialization first computes offsets for each field, then writes data in little‑endian order. The Java schema example:
1 // flat schema, table structure:
2 table User {
3 name: string;
4 user: User;
5 }
6 // data filling
7 FlatBufferBuilder builder = new FlatBufferBuilder(0);
8 int name = builder.createString("mary");
9 User.startUser(builder);
10 User.addName(builder, name);
11 int endUser = User.endUser();
12 builder.finish(endUser);Deserialization extracts the vtable_start and bb_ops from each table, then uses the offset to locate and decode the field value.
1 // api
2 public String name() {
3 int o = __offset(4);
4 return o != 0 ? __string(o + bb_pos) : null;
5 }
6 protected int __offset(int vtable_offset) {
7 return vtable_offset < vtable_size ? bb.getShort(vtable_start + vtable_offset) : 0;
8 }
9 protected String __string(int o) {
10 offset += bb.getInt(offset);
11 int length = bb.getInt(offset);
12 return utf8.decodeUtf8(bb, offset + SIZEOF_INT, length);
13 }Because FlatBuffers avoid extra encoding steps, parsing is very fast.
3. Transmission Channels
Common cross‑VM channels:
Android ↔ Flutter: Flutter’s official MethodChannel (binary data passed as String or Uint8List).
Android ↔ H5: JsBridge using WebView.evaluateJavascript on Android and window.prompt on the web side (Chrome V8 engine).
Flutter ↔ H5: Flutter calls Android, which then calls H5.
Data transfer always involves two steps: serialization to a supported type (String or Uint8List) and deserialization on the receiving side. Smaller payloads and faster (de)serialization improve overall transmission performance.
4. Test Results
Android ↔ Flutter: FlatBuffers > Protocol Buffers > Json.
Android/Flutter ↔ H5: Json > FlatBuffers / Protocol Buffers.
Notes:
When transmitting byte arrays to H5, Base64 encoding/decoding time is included.
Flutter’s channel runs on the UI thread, which may cause UI jank in real apps (not considered in the test).
Single‑VM calls can reach tens of thousands of operations per second; occasional spikes are ignored.
5. About FlatBuffers
FlatBuffers require field‑by‑field parsing after receiving the buffer because the format is offset‑based. Tests that skip parsing report >300 k ops/s, while full parsing drops to ~11 k ops/s, explaining why some online benchmarks appear overly optimistic.
6. Conclusion
For Android ↔ H5 communication, Json is preferred due to native JavaScript support and simplicity.
For Android ↔ Flutter, choose based on scenario:
Json: easy to use, low cost, good for rapidly changing data but larger and slower.
Protocol Buffers: highest compression, strong (de)serialization performance, suitable for stable or low‑frequency data exchange.
FlatBuffers: fastest (de)serialization, ideal for game‑like workloads, but larger size and more complex schema maintenance.
7. Additional Test Data
Test device: Android 9.0.
References:
https://flutter.dev/docs/development/platform-integration/platform-channels
http://google.github.io/flatbuffers/flatbuffers_benchmarks.html
https://code.fb.com/android/improving-facebook-s-performance-on-android-with-flatbuffers/
https://developers.google.com/protocol-buffers/docs/proto3
https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Huajiao Technology
The Huajiao Technology channel shares the latest Huajiao app tech on an irregular basis, offering a learning and exchange platform for tech enthusiasts.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
