☰
Flutter/OpenHarmony 三方库适配实战:sqflite 数据库
2026/10/25 1:54:58 网站建设 项目流程

Flutter/OpenHarmony 三方库适配实战:sqflite 数据库

一、项目信息

项目内容
仓库名称sqflite
仓库地址https://atomgit.com/oh-flutter/sqflite
基于版本sqflite 2.4.5
适配 TAG2.4.5-ohos.1
OHOS 插件包sqflite_ohos
OHOS 原生入口SqflitePlugin
Dart 注册类SqfliteOhos
原生执行模型BinaryMessenger + ThreadWorker + RDB

sqflite是 Flutter 生态中最常用的 SQLite 插件之一。业务代码通常直接使用
openDatabase()、Database.execute()、Batch和transaction(),并不关心数据库
最终由 Android 的 SQLite、iOS 的 SQLite,还是 OpenHarmony 的 RDB 承载。

因此,本次适配的核心目标不是重写一套数据库 API,而是让原有的
sqflite_platform_interface契约在 OpenHarmony 上得到完整实现:

  • Dart 侧继续使用sqflite的公开 API。
  • OHOS 侧使用@ohos.data.relationalStore执行 SQL。
  • 数据库路径、打开、关闭、查询、更新、删除、批处理和事务保持原有语义。
  • 大 SQL 和批处理不能长时间占用平台主线程。
  • 插件销毁时释放 Worker、RDB Store 和待回复消息。

环境搭建与版本

下面是本次适配实际使用的环境。环境搭建过程这里不再展开,直接看 Flutter OH 环境搭建指南。

项目版本/说明
Flutter OH3.44.9+ohos-0.0.1-canary1
Dart3.12.2
DevEco Studio26.0.0
HarmonyOS SDKAPI 26
目标仓库sqflite

二、整体适配架构

本次适配分为四层:

业务 Dart 代码 | v sqflite / sqflite_common | v sqflite_platform_interface | v SqfliteOhos(Dart 插件注册) | v BinaryMessenger(未解码 ArrayBuffer) | v SqflitePlugin(主线程仅负责分发) | v SqfliteWorker(解码 SQL、调用 RDB、编码结果) | v @ohos.data.relationalStore

这个结构最重要的一点是:MethodChannel的标准处理流程会在平台主线程解码
MethodCall。当一次批处理包含大量 SQL 时,消息体可能非常大,主线程解码和执行
很容易触发长时间卡顿。适配版本改用BinaryMessenger.setMessageHandler()获取
未解码的ArrayBuffer,主线程只分配序号并转发消息,实际解码和数据库操作放入
ArkTS Worker。

OHOS 工程目录创建

本次适配前,上游仓库没有ohos/目录。适配分支上按 Flutter OH 的插件工程约束创建平台目录,再把 ArkTS 入口接入pubspec.yaml:

gitswitch-c<your-ohos-adaptation-branch>flutter create--platformsohos.

工程生成后至少保留以下边界:

ohos/ ├── index.ets ├── oh-package.json5 └── src/main/ ├── module.json5 └── ets/components/plugin/

示例工程还需要生成example/ohos/,并在插件pubspec.yaml的flutter.plugin.platforms.ohos中声明pluginClass;只创建目录、没有注册入口,Dart 方法通道仍然不会工作。

三、让 Flutter 找到 OHOS 实现

根包声明 OHOS 默认实现:

flutter:plugin:platforms:android:default_package:sqflite_androidios:default_package:sqflite_darwinmacos:default_package:sqflite_darwinohos:default_package:sqflite_ohos

sqflite_ohos/pubspec.yaml声明原生插件类和 Dart 注册类:

name:sqflite_ohosversion:2.4.5flutter:plugin:platforms:ohos:package:io.flutter.plugins.sqflitepluginClass:SqflitePlugindartPluginClass:SqfliteOhos

Dart 注册类将 OHOS 实现接入统一平台接口:

import'package:sqflite_platform_interface/sqflite_platform_interface.dart';classSqfliteOhosextendsSqflitePlatform{staticvoidregisterWith(){SqflitePlatform.initWithDatabaseFactoryMethodChannel();}}

这样业务代码仍然通过同一个DatabaseFactory和Database抽象工作,不需要为
OpenHarmony 单独封装一套数据库调用。

四、主线程只做消息分发

SqflitePlugin在引擎附加时创建 Worker,并维护消息序号与回复对象的映射:

exportdefaultclassSqflitePluginimplementsFlutterPlugin{privatesqfliteWorker:worker.ThreadWorker|null=null;privatepending:Map<number,BinaryReply>=newMap<number,BinaryReply>();privateseq:number=0;onAttachedToEngine(binding:FlutterPluginBinding):void{this.messenger=binding.getBinaryMessenger();this.sqfliteWorker=newworker.ThreadWorker('./SqfliteWorker.ets',{name:'SqfliteWorker',});this.sqfliteWorker.onmessage=(e:MessageEvents):void=>{constdata=e.dataasSqfliteReply;constreply=this.pending.get(data.seq);if(reply!=null){reply.reply(data.result);this.pending.delete(data.seq);}};}}

原始消息处理器只做三件事:

constrawHandler:BinaryMessageHandler={onMessage:(message:ArrayBuffer,reply:BinaryReply):void=>{constid:number=++this.seq;this.pending.set(id,reply);this.sqfliteWorker?.postMessage({seq:id,call:message,context:this.context},[message],);},};

这里使用seq而不是“最新一条回复”作为关联键,可以避免并发请求互相串号。
未处理的消息不会在平台线程解码,也不会直接调用RdbStore。

引擎分离时必须终止旧 Worker 并清空回复映射,防止热重启后产生重复 Worker:

onDetachedFromEngine(binding:FlutterPluginBinding):void{this.messenger?.setMessageHandler(Constant.SQFLITE_CHANNEL,null);this.sqfliteWorker?.terminate();this.sqfliteWorker=null;this.pending.clear();}

五、Worker 中完成 SQL 解码与执行

Worker 收到原始消息后,先用标准方法解码器还原MethodCall,再按方法名分发:

asyncfunctiondispatch(call:MethodCall,result:MethodResult,context:common.Context,):Promise<void>{switch(call.method){caseConstant.METHOD_OPEN_DATABASE:awaitDatabaseHelper.openDatabase(context,call,result);break;caseConstant.METHOD_GET_DATABASES_PATH:handleGetDatabasesPathCall(context,result);break;caseConstant.METHOD_EXECUTE:awaithandleExecuteCall(call,result);break;caseConstant.METHOD_INSERT:awaithandleInsertCall(call,result);break;caseConstant.METHOD_QUERY:awaithandleQueryCall(call,result);break;caseConstant.METHOD_BATCH:awaithandleBatchCall(call,result);break;caseConstant.METHOD_CLOSE_DATABASE:awaithandleCloseDatabaseCall(call,result);break;default:result.notImplemented();}}

查询路径先根据路径取得对应RdbStore,再创建MethodCallOperation:

asyncfunctionhandleQueryCall(call:MethodCall,result:MethodResult,):Promise<void>{constrdbStore=DatabaseHelper.getDatabaseOrError(call,result);if(rdbStore==null){return;}constoperation=newMethodCallOperation(call,result);awaitDatabase.query(operation,rdbStore);}

Operation、BatchOperation和MethodCallOperation将原库的参数转换成
RDB 的参数数组,并把查询结果转换为 Flutter 可识别的 Map/List。这个转换层是
兼容性的关键,尤其需要注意null、整数、浮点数、Blob 和字符串的类型保持。

六、数据库目录与业务用法

OHOS 实现把应用数据库目录返回给 Dart:

functionhandleGetDatabasesPathCall(context:common.Context,result:MethodResult,):void{constappContext=context.getApplicationContext();result.success(appContext.databaseDir+'/rdb');}

业务代码无需改动:

import'package:path/path.dart'asp;import'package:sqflite/sqflite.dart';Future<void>demo()async{finalStringpath=p.join(awaitgetDatabasesPath(),'demo.db');finalDatabasedb=awaitopenDatabase(path,version:1,onCreate:(db,version)async{awaitdb.execute('CREATE TABLE user(id INTEGER PRIMARY KEY, name TEXT, age INTEGER)',);},);finalint id=awaitdb.insert('user',<String,Object?>{'name':'Alice','age':18,});finalList<Map<String,Object?>>rows=awaitdb.query('user',where:'id = ?',whereArgs:<Object?>[id],);print(rows);awaitdb.close();}

参数化查询必须继续使用占位符,避免通过字符串拼接生成 SQL:

awaitdb.query('user',where:'age >= ? AND name != ?',whereArgs:<Object?>[18,'Bob'],);

事务中应使用回调传入的txn:

awaitdb.transaction((txn)async{awaittxn.insert('user',<String,Object?>{'name':'Tom','age':19,});awaittxn.update('user',<String,Object?>{'age':20},where:'name = ?',whereArgs:<Object?>['Tom'],);});

适配过程 FAQ 与解决方案

为什么主线程只做消息分发,SQL 要放到 Worker?
解决方案:ArkTS 主线程不适合长时间执行数据库查询。把 SQL 解码和 RDB 调用放到 Worker,可以避免页面卡顿,也能让并发调用按请求隔离。

数据库打开失败时,如何区分路径和 schema 问题?
解决方案:先打印数据库目录、文件是否存在和 open 返回值,再检查建表 SQL。路径错误通常发生在首次启动,schema 错误多发生在升级或旧文件迁移。

为什么查询结果要重新构造成 sqflite 行格式?
解决方案:RDB 返回的列名、空值和 blob 类型不能直接当作 Dart 的动态 map。适配层要按列重建结果,并保留 rowid、null 和二进制值的语义。

升级 RDB SDK 后如何做回归?
解决方案:至少覆盖首次建表、升级迁移、事务、批量插入、blob/null 和进程重启。只跑一个查询成功,不能证明旧数据库仍然兼容。

七、构建、安装与模拟器验证

本次在以下环境完成验证:

  • Flutter:3.44.9+ohos-0.0.1-canary1
  • OHOS SDK:26
  • DevEco Studio:26.0.0
  • 模拟器:emulator 7.0.0.107(SP8DEVC00E999R4P11)

执行静态分析:

flutter analyze --no-pub

结果:未发现问题。

示例应用已构建并安装到 OpenHarmony 模拟器。示例首页包含 Raw tests、Open tests、
Type tests、Batch tests、Todo database example、Exception tests 和 IO tests 等
入口,可以分别验证 SQL、数据库生命周期、值类型和异常处理。

截图中的Sqflite demo页面来自sqflite/example,说明 OHOS 插件已经成功注册,
Dart 示例页面可以正常加载并进入数据库测试入口。

八、需要注意的能力边界

  1. 当前实现聚焦 OpenHarmony RDB,不等同于桌面端对任意 SQLite 动态库的加载能力。
  2. 加密数据库、SQLCipher 和外部自定义 SQLite 构建不在本次适配范围内。
  3. Worker 能避免大 SQL 阻塞平台主线程,但业务仍应控制超大事务和不必要的全表查询。
  4. 返回的查询结果是跨通道序列化后的新对象,业务不应依赖原生 RDB 对象。
  5. 真机、多窗口、后台销毁和异常断电场景仍应在业务上线前补充测试。

如果安装 HAP 时提示9568289,应检查应用是否申请了超出当前应用等级的权限。
数据库本身不需要该权限,错误通常来自示例或宿主工程的其他权限声明。

九、代码提交与协作

本次适配已经推送到 AtomGitmain分支,并创建 TAG:

main: e7ad98de849b16038f6f5815f29891e2840a5d0e tag: 2.4.5-ohos.1

业务工程可以按 README 中的 TAG 引用适配版本:

dependencies:sqflite:git:url:https://atomgit.com/oh-flutter/sqflite.gitref:2.4.5-ohos.1path:sqflite

欢迎加入 CPF-Flutter 鸿蒙社区,参与
Flutter OHOS 插件适配、HAP 构建和模拟器/真机验证经验交流。

  • 仓库地址:https://atomgit.com/oh-flutter/sqflite
  • 提交 Issue:https://atomgit.com/oh-flutter/sqflite/issues
  • 提交 Pull Request:https://atomgit.com/oh-flutter/sqflite/pulls

版本与提交基线

项目版本/地址
上游包sqflite
上游最新版本2.4.5
文章目标基线2.4.5-ohos.1
当前已发布 TAG2.4.5-ohos.1
适配分支main
目标最新 TAG2.4.5-ohos.1
最终提交10f256a

版本号更新只代表文章和适配分支的基线已对齐;涉及上游 API 变更时,仍需在该 TAG 上重新运行 Dart 测试、ArkTS 编译、HAP 安装和真机回归。

参考资料和仓库地址

  • 适配仓库主页
  • 适配分支 main
  • 当前已发布 TAG 2.4.5-ohos.1
  • 目标最新 TAG 2.4.5-ohos.1
  • 最终提交 10f256a
  • ArkTS 通道实现
  • 鸿蒙适配说明
  • pub.dev 包页面
  • pub.dev 2.4.5 版本页
  • 上游仓库
  • Flutter 官方插件与平台通道文档
  • Flutter OH 鸿蒙 SDK 仓库

欢迎加入 CPF-Flutter 鸿蒙社区。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询