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

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

一、核心特性

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

二、工作原理

  1. ‌定义数据结构 ‌
    使用 .proto 文件声明消息类型及其字段规则,例如:
Kotlin 复制代码
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 复制代码
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原生配置

  1. 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开发及编译

  1. 编写代码: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;
}
  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不太了解版本关系,一直搞不定,坑了半天,总结一下实践步骤以供借鉴。

相关推荐
千里马学框架4 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台4 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo4 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077004 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼4 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone4 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen4 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone4 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui