OpenHarmony + Flutter 混合开发实战:构建高性能离线优先的行业应用(含 SQLite 与数据同步策略)

引言

在政务、电力、物流、巡检等强线下场景中,网络不稳定是常态。用户需要:

  • 无网时仍可操作(如填写表单、拍照、记录轨迹);
  • 有网时自动同步数据到云端
  • 多端设备间数据不冲突

Flutter 虽有 sqflite 等数据库插件,但在 OpenHarmony 上无法直接使用 (依赖 Android/iOS 原生库)。而 OpenHarmony 提供了强大的 关系型数据库(RDB)分布式能力,却缺乏跨平台 UI 支持。

本文将教你如何:

🔧 在 Flutter 中通过 MethodChannel 调用 OpenHarmony RDB

🔄 设计"本地优先 + 后台同步"架构

🛡️ 实现断点续传、冲突检测、数据加密

📦 构建一个 "电力巡检离线填报系统" ,支持 无网录入 → 自动同步 → 多端一致

所有代码基于 OpenHarmony API 10(4.1 SDK) + Flutter 3.19 + Riverpod,已在 RK3568 工业平板实测。


一、为什么不能直接用 sqflite?

插件 依赖 OpenHarmony 支持
sqflite Android SQLiteOpenHelper / iOS FMDB ❌ 不兼容
drift 同上 + Dart 编译器 ❌ 无法编译
hive 纯 Dart ✅ 可用,但无事务、无关系模型

🚫 结论必须使用 OpenHarmony 原生 RDB,并通过 MethodChannel 暴露给 Flutter。


二、整体架构:三层数据流

复制代码
┌───────────────────┐
│   Flutter (Dart)  │
│ - UI 表单         │
│ - 调用 data_channel│ ← MethodChannel
└─────────▲─────────┘
          │
┌─────────┴─────────┐
│ OpenHarmony (ArkTS)│
│ - 封装 RDB 操作    │
│ - 同步任务队列     │
│ - 网络状态监听     │
└─────────▲─────────┘
          │
┌─────────┴─────────┐
│  OpenHarmony RDB  │
│  (SQLite 内核)    │
└───────────────────┘
          │
┌─────────▼─────────┐
│     云端 API      │
│ (有网时自动同步)  │
└───────────────────┘

核心原则

  • 所有数据先写本地 RDB
  • 同步由后台服务异步完成
  • Flutter 只关心"成功/失败",不关心网络细节

三、Step 1:定义数据模型(巡检任务)

业务需求

  • 每个任务包含:ID、设备编号、检查项、照片路径、状态(草稿/已提交)、时间戳;
  • 支持批量提交;
  • 提交后不可编辑。

OpenHarmony RDB 表结构(ArkTS)

typescript 复制代码
// model/InspectionTask.ts
import rdb from '@ohos.data.relationalStore';

export const STORE_CONFIG: rdb.StoreConfig = {
  name: 'inspection.db',
  securityLevel: rdb.SecurityLevel.S1, // 加密存储
};

export const CREATE_TABLE_SQL = `
  CREATE TABLE IF NOT EXISTS inspection_task (
    id TEXT PRIMARY KEY,
    device_id TEXT NOT NULL,
    items TEXT NOT NULL,        -- JSON 字符串
    photo_paths TEXT,           -- 多图逗号分隔
    status TEXT DEFAULT 'draft',
    created_at INTEGER NOT NULL,
    updated_at INTEGER NOT NULL,
    sync_status TEXT DEFAULT 'pending'  -- pending / synced / failed
  );
`;

🔐 安全提示securityLevel: S1 启用 数据库文件级加密(需设备支持)。


四、Step 2:封装 RDB 操作(ArkTS)

typescript 复制代码
// service/DatabaseService.ts
import rdb from '@ohos.data.relationalStore';
import { STORE_CONFIG, CREATE_TABLE_SQL } from '../model/InspectionTask';

export class DatabaseService {
  private store: rdb.RdbStore | null = null;

  async init(): Promise<void> {
    this.store = await rdb.getRdbStore(globalThis.context, STORE_CONFIG);
    await this.store.executeSql(CREATE_TABLE_SQL);
  }

  // 插入或更新任务
  async saveTask(task: any): Promise<void> {
    const values = new rdb.ValuesBucket();
    values.put('id', task.id);
    values.put('device_id', task.deviceId);
    values.put('items', JSON.stringify(task.items));
    values.put('photo_paths', task.photoPaths?.join(',') || '');
    values.put('status', task.status);
    values.put('created_at', task.createdAt ?? Date.now());
    values.put('updated_at', Date.now());
    values.put('sync_status', 'pending');

    await this.store!.insertOrReplace('inspection_task', values);
  }

  // 获取所有待同步任务
  async getPendingTasks(): Promise<any[]> {
    const predicates = new rdb.RdbPredicates('inspection_task');
    predicates.equalTo('sync_status', 'pending');
    
    const resultSet = await this.store!.query(predicates);
    const tasks: any[] = [];
    
    while (resultSet.goToNextRow()) {
      tasks.push({
        id: resultSet.getString(resultSet.getColumnIndex('id')),
        deviceId: resultSet.getString(resultSet.getColumnIndex('device_id')),
        items: JSON.parse(resultSet.getString(resultSet.getColumnIndex('items'))),
        photoPaths: resultSet.getString(resultSet.getColumnIndex('photo_paths'))?.split(',') || [],
        status: resultSet.getString(resultSet.getColumnIndex('status')),
        createdAt: resultSet.getLong(resultSet.getColumnIndex('created_at')),
        updatedAt: resultSet.getLong(resultSet.getColumnIndex('updated_at')),
      });
    }
    resultSet.close();
    return tasks;
  }

  // 标记任务为已同步
  async markSynced(taskId: string): Promise<void> {
    const values = new rdb.ValuesBucket();
    values.put('sync_status', 'synced');
    const predicates = new rdb.RdbPredicates('inspection_task')
      .equalTo('id', taskId);
    await this.store!.update(values, predicates);
  }
}

五、Step 3:MethodChannel 暴露给 Flutter

typescript 复制代码
// EntryAbility.ts
import { DatabaseService } from './service/DatabaseService';
import { SyncManager } from './service/SyncManager'; // 后文定义

export default class EntryAbility extends UIAbility {
  private dbService: DatabaseService | null = null;
  private syncManager: SyncManager | null = null;

  async onCreate() {
    // 初始化数据库
    this.dbService = new DatabaseService();
    await this.dbService.init();

    // 初始化同步管理器
    this.syncManager = new SyncManager(this.dbService);

    // 注册通道
    this.engine.setMethodCallHandler('com.example.data/task', 
      async (call, result) => {
        try {
          if (call.method === 'saveTask') {
            await this.dbService!.saveTask(call.arguments);
            result.success(true);
            
            // 触发后台同步(非阻塞)
            this.syncManager!.triggerSync();
          } 
          else if (call.method === 'getLocalTasks') {
            const tasks = await this.dbService!.getPendingTasks();
            result.success(tasks);
          }
        } catch (e) {
          result.error('DB_ERROR', e.message, null);
        }
      }
    );
  }
}

六、Step 4:Flutter 端调用与状态管理

dart 复制代码
// lib/providers/inspection_provider.dart
final inspectionDataProvider = Provider<InspectionData>((ref) {
  return InspectionData._();
});

class InspectionData {
  static const _channel = MethodChannel('com.example.data/task');

  Future<void> saveTask({
    required String id,
    required String deviceId,
    required List<Map<String, dynamic>> items,
    List<String>? photoPaths,
  }) async {
    await _channel.invokeMethod('saveTask', {
      'id': id,
      'deviceId': deviceId,
      'items': items,
      'photoPaths': photoPaths,
      'status': 'draft',
    });
  }

  Future<List<Task>> getLocalTasks() async {
    final rawList = await _channel.invokeMethod('getLocalTasks') as List;
    return rawList.map((json) => Task.fromJson(json)).toList();
  }
}

@immutable
class Task {
  final String id;
  final String deviceId;
  final List<Map<String, dynamic>> items;
  final List<String> photoPaths;
  final String status;
  final int createdAt;
  final int updatedAt;

  Task.fromJson(Map<String, dynamic> json)
      : id = json['id'],
        deviceId = json['deviceId'],
        items = List<Map<String, dynamic>>.from(json['items']),
        photoPaths = List<String>.from(json['photoPaths'] ?? []),
        status = json['status'],
        createdAt = json['createdAt'],
        updatedAt = json['updatedAt'];
}

UI 使用示例

dart 复制代码
// lib/pages/task_form_page.dart
class TaskFormPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      body: Form(
        child: Column(
          children: [
            // 表单项...
            ElevatedButton(
              onPressed: () async {
                // 保存到本地
                await ref.read(inspectionDataProvider).saveTask(
                  id: DateTime.now().millisecondsSinceEpoch.toString(),
                  deviceId: 'DEV_001',
                  items: [{'name': '电压', 'value': '220V'}],
                  photoPaths: ['/data/.../photo1.jpg'],
                );
                Navigator.pop(context);
              },
              child: Text('保存草稿'),
            )
          ],
        ),
      ),
    );
  }
}

七、Step 5:实现后台自动同步(关键!)

同步策略设计

场景 行为
有网 + 应用前台 立即同步
有网 + 应用后台 延迟 5 分钟同步(省电)
无网 等待网络恢复
同步失败 重试 3 次,标记为 failed

SyncManager 实现(ArkTS)

typescript 复制代码
// service/SyncManager.ts
import net from '@ohos.net.connection';
import http from '@ohos.net.http';

export class SyncManager {
  private db: DatabaseService;
  private isSyncing = false;

  constructor(db: DatabaseService) {
    this.db = db;
    this.listenNetwork();
  }

  private listenNetwork() {
    const connMgr = net.getConnectionManager();
    connMgr.getDefaultNet().then(netHandle => {
      netHandle.on('netAvailable', () => {
        console.log('网络恢复,触发同步');
        this.triggerSync();
      });
    });
  }

  async triggerSync() {
    if (this.isSyncing) return;
    this.isSyncing = true;

    try {
      const tasks = await this.db.getPendingTasks();
      for (const task of tasks) {
        const success = await this.uploadTask(task);
        if (success) {
          await this.db.markSynced(task.id);
        } else {
          // TODO: 记录失败次数,超过3次标记为 failed
        }
      }
    } finally {
      this.isSyncing = false;
    }
  }

  private async uploadTask(task: any): Promise<boolean> {
    const httpRequest = http.createHttp();
    try {
      const response = await httpRequest.request(
        'https://api.yourcompany.com/inspection/sync',
        {
          method: http.RequestMethod.POST,
          header: { 'Content-Type': 'application/json' },
          extraData: JSON.stringify(task)
        }
      );
      return response.responseCode === 200;
    } catch {
      return false;
    } finally {
      httpRequest.destroy();
    }
  }
}

性能优化 :使用 http.createHttp() 而非 WebView,减少内存开销。


八、安全与可靠性增强

1. 数据加密

  • RDB 启用 SecurityLevel.S1
  • 敏感字段(如照片路径)在传输前 AES 加密。

2. 事务保证

typescript 复制代码
// 批量操作使用事务
await store!.executeBatch([
  { sql: 'INSERT INTO ...', bindArgs: [...] },
  { sql: 'UPDATE ...', bindArgs: [...] }
]);

3. 存储配额监控

typescript 复制代码
import filemanager from '@ohos.file.fs';
const stat = await filemanager.stat('/data/storage/el2/base/haps/entry/databases/inspection.db');
if (stat.size > 100 * 1024 * 1024) {
  // 触发清理策略
}

九、调试技巧

  1. 查看数据库内容

    bash 复制代码
    hdc shell sqlite3 /data/storage/el2/base/haps/entry/databases/inspection.db "SELECT * FROM inspection_task;"
  2. 模拟断网

    • 在 DevEco Studio 关闭模拟器网络;
    • 或真机开启飞行模式。
  3. 日志跟踪同步状态

    bash 复制代码
    hdc logcat | grep -E "(SyncManager|RDB)"

十、总结:打造真正可用的离线应用

通过本文,你已掌握:

在 Flutter 中安全调用 OpenHarmony RDB

设计"本地优先"数据架构

实现智能后台同步机制

保障数据安全与一致性

💼 适用场景

  • 电力/水务/燃气巡检
  • 物流签收
  • 政务外勤
  • 工业点检

在国产化替代浪潮下,离线能力是行业应用的生命线。掌握此模式,你将能构建真正可靠、安全、高效的 OpenHarmony 行业解决方案。


https://openharmonycrossplatform.csdn.net/content

相关推荐
冉冰学姐3 小时前
SSM实验室安全管理系统c03w5(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm 框架应用·实验室安全管理·数字化管理系统
语落心生3 小时前
解读广告数仓(四) - 指标计算与应用实现
数据库
帅气马战的账号3 小时前
开源鸿蒙+Flutter:跨端隐私保护与原生安全能力深度融合实战
flutter
语落心生3 小时前
解读广告数仓(一) - 广告业务模型与指标体系深化分析
数据库
老华带你飞3 小时前
旅游|基于Java旅游信息推荐系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·后端·旅游
冉冰学姐3 小时前
SSM石家庄铁道大学影视资料管理系统ql5pa(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm框架·石家庄铁道大学
西西学代码3 小时前
Flutter中常用的UI设计
前端·flutter·ui
Sunhen_Qiletian3 小时前
《Python开发之语言基础》第七集:库--时间库
前端·数据库·python
程序边界4 小时前
金仓数据库助力Oracle迁移实战:破局四大挑战,解锁高效迁移新路径
数据库·oracle