Protocol Buffers(简称Protobuf)是由 Google 开发的一种结构化数据序列化框架,旨在实现高效的数据交换与存储。其核心特性及优势如下:
一、核心特性
- 跨语言与跨平台
支持多种编程语言(如 C++、Java、Python、Dart 等),生成的语言无关代码可在不同平台间无缝交互。 - 高性能编码
采用二进制格式进行序列化,数据体积小且解析速度快,相较于 XML 和 JSON 可缩减数据量 3--10 倍,解析速度提升 20--100 倍。 - 灵活扩展性
通过.proto
文件定义数据结构,支持向后兼容的字段更新(如新增字段不影响旧版解析)。
二、工作原理
- 定义数据结构
使用.proto
文件声明消息类型及其字段规则,例如:
Kotlin
message User {
required string name = 1;
optional int32 age = 2;
repeated string emails = 3;
}
- 代码生成
通过 Protobuf 编译器(protoc
)配合语言插件(如protoc_plugin
)生成目标语言的序列化/反序列化类。 - 序列化与反序列化
生成的类提供接口将对象转换为二进制流(网络传输或存储)或从二进制流重建对象。
三、典型应用场景

四、与其他序列化协议的对比

五、flutter访问原生Android图库实践
5.1 protobuff环境搭建
需要 protobuff 安装环境,以mac为例:
1. 检查protoc_plugin插件版本
Dart
dart pub global list | grep protoc_plugin
安装protoc_plugin插件
Dart
MacBook-Pro ~ % dart pub global activate protoc_plugin
Downloading packages... .
+ collection 1.19.1
+ fixnum 1.1.1
+ meta 1.16.0
+ path 1.9.1
+ protobuf 4.0.0
+ protoc_plugin 22.0.1
输出(protoc_plugin 22.0.1、要求:protobuf 4.0.0):
⚠️注意:flutter项目pubspec.yaml也需要配置:
Dart
protobuf: ^4.0.0
protoc_plugin: ^22.0.1
2. 检查libprotoc版本及安装
Dart
MacBook-Pro ~ % protoc --version
libprotoc 3.21.12
5.2 Android原生配置
- android/build.radle
Groovy
dependencies {
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.4' // 增加
}
2. android/app/build.radle
Groovy
apply plugin: 'com.google.protobuf' // 导入插件
android {
// 添加
sourceSets {
main {
proto {
srcDir 'src/main/proto'
}
}
}
}
// 添加
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.24.4'
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.multidex:multidex:2.0.1'
// # Flutter 侧(确保两侧一致protoc --version)
// protoc --version && dart pub global list | grep protoc_plugin
implementation 'com.google.protobuf:protobuf-javalite:3.21.12'
}
5.3 Android原生proto开发及编译
- 编写代码:android/app/src/main/proto/media.proto
Kotlin
syntax = "proto3";
package com.example.test.proto;
message MediaItem {
int64 id = 1;
string path = 2;
string album = 3;
int64 size = 4;
int64 date_added = 5;
int64 date_modified = 6;
int64 duration = 7; // 0表示图片
string mime_type = 8; // 新增字段
}
message MediaList {
repeated MediaItem items = 1;
}
- 编译输出产物flutter项目根目录下:lib/protos/media目录
执行终端命令:
bash
# 创建目录
mkdir -p lib/protos/media
# 生成代码
protoc --dart_out=grpc:lib/protos/media --proto_path=android/app/src/main/proto android/app/src/main/proto/media.proto
输出:

5.4 Android原生插件通信开发
开发业务model:
Kotlin
class PhotoMediaModel {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@SuppressLint("Range")
fun getPhotoMedias(context: Context): ByteArray {
val proto = MediaList.newBuilder()
val projection = arrayOf(
MediaStore.MediaColumns._ID,
MediaStore.MediaColumns.DATA,
MediaStore.MediaColumns.BUCKET_DISPLAY_NAME,
MediaStore.MediaColumns.SIZE,
MediaStore.MediaColumns.DATE_ADDED,
MediaStore.MediaColumns.DATE_MODIFIED,
MediaStore.Video.VideoColumns.DURATION
)
val uri = MediaStore.Files.getContentUri("external")
val selection = "${MediaStore.Files.FileColumns.MEDIA_TYPE} IN (?,?)"
val args = arrayOf(
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE.toString(),
MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO.toString()
)
context.contentResolver.query(
uri,
projection,
selection,
args,
null
)?.use { cursor ->
val idCol = cursor.getColumnIndex(MediaStore.MediaColumns._ID)
val dataCol = cursor.getColumnIndex(MediaStore.MediaColumns.DATA)
val albumCol = cursor.getColumnIndex(MediaStore.MediaColumns.BUCKET_DISPLAY_NAME)
val sizeCol = cursor.getColumnIndex(MediaStore.MediaColumns.SIZE)
val addedCol = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_ADDED)
val modifiedCol = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED)
val durationCol = cursor.getColumnIndex(MediaStore.Video.VideoColumns.DURATION)
val itemBuilder = MediaItem.newBuilder()
while (cursor.moveToNext()) {
val item = itemBuilder.clear().apply {
id = cursor.getLong(idCol)
path = cursor.getString(dataCol)
album = cursor.getString(albumCol) ?: ""
size = cursor.getLong(sizeCol)
dateAdded = cursor.getLong(addedCol)
dateModified = cursor.getLong(modifiedCol)
duration = if (cursor.isNull(durationCol)) 0 else cursor.getLong(durationCol)
}.build()
proto.addItems(item)
}
}
return proto.build().toByteArray()
}
}
然后在Actvity注册自己的插件业务:
Kotlin
class MainActivity : FlutterActivity() {
private val CHANNEL_PHOTO = "com.example.test/photo_media_channel"
private val photoMediaModel = PhotoMediaModel()
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
registerPlugins(flutterEngine, this)
}
private fun registerPlugins(flutterEngine: FlutterEngine, context: Context) {
// 图库查询
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_PHOTO).setMethodCallHandler { call, result ->
when (call.method) {
"getPhotoMedias" -> {
val byteArray = photoMediaModel.getPhotoMedias(context)
result.success(byteArray)
}
else -> {
result.notImplemented()
}
}
}
}
}
至此原生开发完成。
5.6 Flutter访问原生插件
开发对应的MethodChannel
Dart
class PhotoMediaOptChannel {
static const MethodChannel _photoMediaChannel = MethodChannel("com.example.test/photo_media_channel");
static Future<List<MediaItem>> getPhotoMedias() async {
if (!Platform.isAndroid) {
return [];
}
try {
final byteArray = await _photoMediaChannel.invokeMethod<Uint8List>('getPhotoMedias');
// 使用 Protobuf 生成的代码进行反序列化
if (byteArray == null) {
debugPrint('Performance: getPhotoMedias 错误: protoBytes 为空');
return [];
}
final MediaList mediaList = MediaList.fromBuffer(byteArray);
debugPrint('Performance: getPhotoMedias 结束: ${mediaList.items.length}');
return mediaList.items;
} catch (e) {
debugPrint('Performance: getPhotoMedias 错误: $e');
return [];
}
}
}
注意:flutter需要配置依赖:pubspec.yaml
Dart
protobuf: ^4.0.0
protoc_plugin: ^22.0.1
运行输出:
2025-05-08 19:21:37.929 12321-12487 flutter com.example.test I Performance: getPhotoMedias 结束: 264
六、总结
由于对protobuff不太了解版本关系,一直搞不定,坑了半天,总结一下实践步骤以供借鉴。