当前位置: 首页 > news >正文

Flutter接入ProtoBuff和原生Android通信【性能最优】

Protocol Buffers(简称Protobuf)是由 Google 开发的一种结构化数据序列化框架,旨在实现高效的数据交换与存储。其核心特性及优势如下:

一、核心特性

  1. 跨语言与跨平台
    支持多种编程语言(如 C++、Java、Python、Dart 等),生成的语言无关代码可在不同平台间无缝交互。
  2. 高性能编码
    采用二进制格式进行序列化,数据体积小且解析速度快,相较于 XML 和 JSON 可缩减数据量 3–10 倍,解析速度提升 20–100 倍。
  3. 灵活扩展性
    通过 .proto 文件定义数据结构,支持向后兼容的字段更新(如新增字段不影响旧版解析)。
     

二、工作原理

  1. 定义数据结构
    使用 .proto 文件声明消息类型及其字段规则,例如:
message User {required string name = 1;optional int32 age = 2;repeated string emails = 3;
}
  1. 代码生成
    通过 Protobuf 编译器(protoc)配合语言插件(如 protoc_plugin)生成目标语言的序列化/反序列化类。
  2. 序列化与反序列化
    生成的类提供接口将对象转换为二进制流(网络传输或存储)或从二进制流重建对象。

三、典型应用场景

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

 五、flutter访问原生Android图库实践

5.1 protobuff环境搭建

需要 protobuff 安装环境,以mac为例:

1. 检查protoc_plugin插件版本

dart pub global list | grep protoc_plugin

 安装protoc_plugin插件

MacBook-Pro ~ % dart pub global activate protoc_pluginDownloading 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也需要配置:

  protobuf: ^4.0.0protoc_plugin: ^22.0.1

2. 检查libprotoc版本及安装

MacBook-Pro ~ % protoc --version 
libprotoc 3.21.12

5.2 Android原生配置

 1. android/build.radle

    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

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_pluginimplementation 'com.google.protobuf:protobuf-javalite:3.21.12'
}

5.3 Android原生proto开发及编译

1. 编写代码:android/app/src/main/proto/media.proto

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;
}

2. 编译输出产物flutter项目根目录下:lib/protos/media目录

执行终端命令:

# 创建目录
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:

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注册自己的插件业务:


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

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

  protobuf: ^4.0.0protoc_plugin: ^22.0.1

运行输出:
2025-05-08 19:21:37.929 12321-12487 flutter                 com.example.test                   I  Performance: getPhotoMedias 结束: 264

六、总结

由于对protobuff不太了解版本关系,一直搞不定,坑了半天,总结一下实践步骤以供借鉴。

相关文章:

  • tmux 入门与实用指南
  • 39、.NET GC是什么? 为什么需要GC?
  • 深泽多层电路在PCB行业中属于什么水平
  • 初识Linux · 传输层协议TCP · 上
  • Python爬虫中time.sleep()与动态加载的配合使用
  • C语言自定义类型:联合与枚举详解
  • 手撕基于AMQP协议的简易消息队列-2(所用第三方库的介绍与简单使用)
  • 【MCP】为什么使用Streamable HTTP: 相比SSE的优势与实践指南
  • 【SpringMVC】详解cookie,session及实战
  • ping_test_parallel.sh 并行网络扫描脚本
  • (leetcode) 力扣100 7.接雨水(两种非官解,三种官解,对官解进一步解释)
  • QT实现曲线图缩放、拖拽以及框选放大
  • 【特别版】Kubernetes集群安装(1master,2node)
  • docker 安装 sqlserver2022 和注意点
  • 长事务:数据库中的“隐形炸弹“——金仓数据库运维避坑指南
  • P2415 集合求和 详解
  • 需求分析阶段测试工程师主要做哪些事情
  • Kubernetes探针生产环境实战指南
  • Linux下部署Keepalived
  • 代理服务器
  • 北外滩集团21.6亿元摘上海虹口地块,为《酱园弄》取景地
  • 代理销售保险存在误导行为,农业银行重庆市分行相关负责人被罚款0.1万元
  • 重庆党政代表团在沪考察,陈吉宁龚正与袁家军胡衡华共商两地深化合作工作
  • 北上广深均宣布下调个人住房公积金贷款利率
  • 绿城房地产集团:近半年累计花费20.6亿元购买旗下债券
  • “80后”海南琼海市长傅晟,去向公布