状态共享新范式:在 Flutter + OpenHarmony 应用中实现跨框架状态同步(Riverpod + ArkState)

状态共享新范式:在 Flutter + OpenHarmony 应用中实现跨框架状态同步(Riverpod + ArkState)

作者 :L、218
发布平台 :CSDN
发布时间 :2025年12月8日
关键词:Flutter、OpenHarmony、状态管理、Riverpod、ArkState、跨框架通信、数据同步、Reactive Programming、统一状态层


引言

随着 OpenHarmony 生态的快速发展,越来越多企业开始采用"混合架构"开发策略:

  • 核心页面使用 Flutter 实现高性能 UI 与跨端复用
  • 容器层基于 OpenHarmony 原生能力构建服务卡片、分布式任务等

但在实际开发中,我们面临一个棘手问题:

当用户在 Flutter 商品页点击"加入购物车",OpenHarmony 的底部 Tab 如何实时更新 Badge 数量?

换句话说:

🔥 Flutter 与 ArkTS 如何共享同一份应用状态?

本文将由 L、218 带你从零构建一个 跨框架统一状态管理层 ,通过 FFI + 共享内存池 + 发布订阅模型,实现 Flutter(Riverpod)与 OpenHarmony(ArkTS)之间的双向响应式数据同步

这不是简单的消息传递,而是真正意义上的"一处修改,处处响应"。


一、为什么需要跨框架状态同步?

场景 当前痛点
混合架构 App Flutter 负责商品页,ArkTS 负责导航栏
分布式任务 手机上启动下载,手表上显示进度条
主题切换 用户设置深色模式,Flutter 页面未刷新

如果每个框架维护自己的状态副本,就会导致:

🔄 数据不一致

💣 内存冗余

🐞 逻辑冲突

我们的目标是:

✅ 构建一个独立于 UI 框架的统一状态中心

✅ 支持 Flutter 与 ArkTS 双向监听与更新


二、技术选型对比

方案 是否推荐 说明
WebView + localStorage ⚠️ 仅限 Web 方案 性能差,无法用于原生嵌入
FFI 直接读写内存 ✅ 核心基础 高性能,低延迟
文件轮询机制 ❌ 不推荐 延迟高,耗电严重
自定义事件通道 ✅ 推荐 结合 FFI 实现高效通信

最终方案:

FFI + 共享内存池 + 发布订阅模型


三、整体架构设计

复制代码
+----------------------------+
|        Flutter (Dart)      |
|   ┌──────────────────┐     |
|   │   Riverpod       │<----+--- 监听 state/cart.count
|   └──────────────────┘     |
|             ↑               |
|   ┌──────────────────┐     |
|   │  FFI Bridge      │<----+--- 读写 shared_memory
|   └──────────────────┘     |
+------------↑---------------+
             | dlopen / memfd
+------------↓-------------------------+
|     Shared Memory Pool (C++)       |
|   - user.name                        |
|   - cart.count                       |
|   - theme.isDark                     |
|   - download.progress                |
+------------↑-------------------------+
             | JNI / Native API
+------------↓-------------------------+
|   OpenHarmony (ArkTS)                |
|   ┌──────────────────┐               |
|   │   ArkState       │<--------------+--- @Watch('cart.count')
|   └──────────────────┘               |
|   ┌──────────────────┐               |
|   │   NDK Bridge     │<--------------+--- 调用 nativeModule.getState()
|   └──────────────────┘               |
+--------------------------------------+

所有状态变更都通过 C++ 层广播,两端各自触发 UI 刷新。


四、实战步骤:实现"购物车数量"跨框架同步

🧩 功能目标

  • 在 Flutter 商品页点击"加入购物车"
  • OpenHarmony 底部 Tab 上的 Badge 实时 +1
  • 支持反向更新(模拟其他设备同步)

步骤 1:创建共享内存层(C++)

state_pool.h
cpp 复制代码
#ifndef STATE_POOL_H
#define STATE_POOL_H

#include <map>
#include <string>
#include <functional>
#include <mutex>

typedef std::function<void(const std::string& key, const std::string& value)> StateChangeListener;

class StatePool {
public:
    static StatePool& GetInstance() {
        static StatePool instance;
        return instance;
    }

    void SetString(const std::string& key, const std::string& value);
    std::string GetString(const std::string& key);

    int32_t GetInt(const std::string& key);
    void SetInt(const std::string& key, int32_t value);

    void AddListener(const std::string& key, StateChangeListener listener);
    void RemoveListener(const std::string& key);

private:
    std::map<std::string, std::string> data_;
    std::map<std::string, std::vector<StateChangeListener>> listeners_;
    std::mutex mutex_;
};

// C 导出函数(供 FFI 调用)
extern "C" {
    void state_set_int(const char* key, int32_t value);
    int32_t state_get_int(const char* key);
}

#endif
state_pool.cpp
cpp 复制代码
#include "state_pool.h"
#include <iostream>

using namespace std;

void StatePool::SetString(const string& key, const string& value) {
    lock_guard<mutex> lock(mutex_);
    auto old = data_[key];
    data_[key] = value;
    
    // 广播变更
    if (listeners_.find(key) != listeners_.end()) {
        for (auto& cb : listeners_[key]) {
            cb(key, value);
        }
    }
}

int32_t StatePool::GetInt(const string& key) {
    lock_guard<mutex> lock(mutex_);
    auto it = data_.find(key);
    if (it == data_.end()) return 0;
    try {
        return stoi(it->second);
    } catch (...) {
        return 0;
    }
}

void StatePool::SetInt(const string& key, int32_t value) {
    SetString(key, to_string(value));
}

void StatePool::AddListener(const string& key, StateChangeListener listener) {
    lock_guard<mutex> lock(mutex_);
    listeners_[key].push_back(listener);
}
C 导出函数实现
cpp 复制代码
void state_set_int(const char* key, int32_t value) {
    StatePool::GetInstance().SetInt(string(key), value);
}

int32_t state_get_int(const char* key) {
    return StatePool::GetInstance().GetInt(string(key));
}

步骤 2:Flutter 端接入(Riverpod)

添加依赖 pubspec.yaml
yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  riverpod: ^2.4.0
  ffi: ^2.1.1
创建 FFI 绑定 lib/state_ffi.dart
dart 复制代码
import 'dart:ffi' as ffi;

typedef SetIntNative = ffi.Void Function(ffi.Pointer<Utf8>, ffi.Int32);
typedef GetIntNative = ffi.Int32 Function(ffi.Pointer<Utf8>);

class NativeStateBridge {
  final ffi.Pointer<ffi.DynamicLibrary> _dylib;
  final SetIntNative _setState;
  final GetIntNative _getState;

  NativeStateBridge(String soPath)
      : _dylib = ffi.DynamicLibrary.open(soPath),
        _setState = _dylib
            .lookup<ffi.NativeFunction<SetIntNative>>('state_set_int')
            .asFunction(),
        _getState = _dylib
            .lookup<ffi.NativeFunction<GetIntNative>>('state_get_int')
            .asFunction();

  void setInt(String key, int value) {
    final cKey = ffi.Utf8.toUtf8(key);
    _setState(cKey, value);
    ffi.free(cKey);
  }

  int getInt(String key) {
    final cKey = ffi.Utf8.toUtf8(key);
    final result = _getState(cKey);
    ffi.free(cKey);
    return result;
  }
}
封装为 Riverpod Provider
dart 复制代码
final stateBridgeProvider = Provider<NativeStateBridge>((ref) {
  return NativeStateBridge("libs/libstate_pool.so");
});

final cartCountProvider = StateNotifierProvider<CartNotifier, int>((ref) {
  final bridge = ref.watch(stateBridgeProvider);
  final initial = bridge.getInt('cart.count');
  return CartNotifier(initial, bridge);
});

class CartNotifier extends StateNotifier<int> {
  final NativeStateBridge bridge;

  CartNotifier(int state, this.bridge) : super(state);

  void increment() {
    final next = state + 1;
    state = next;
    bridge.setInt('cart.count', next); // 同步到底层
  }

  void decrement() {
    final next = state - 1;
    if (next >= 0) {
      state = next;
      bridge.setInt('cart.count', next);
    }
  }
}

步骤 3:OpenHarmony 端接入(ArkTS)

创建 NDK 接口 native_state.cpp
cpp 复制代码
#include <cstdint>
#include "bridge_util.h"

extern "C" {
    int32_t state_get_int(const char* key);
    void state_set_int(const char* key, int32_t value);
}

static napi_value GetCartCount(napi_env env, napi_callback_info info) {
    int32_t count = state_get_int("cart.count");
    napi_value result;
    napi_create_int32(env, count, &result);
    return result;
}

static napi_value SetCartCount(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    int32_t value;
    napi_get_value_int32(env, args[0], &value);
    state_set_int("cart.count", value);

    return nullptr;
}

EXTERN_C_START
napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        { "getCartCount", nullptr, GetCartCount, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "setCartCount", nullptr, SetCartCount, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, 2, desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "state",
    .nm_priv = nullptr,
    .reserved = { 0 }
};

extern "C" __attribute__((constructor)) void RegisterModule() {
    napi_register_module(&demoModule);
}
在 ArkTS 中使用
ets 复制代码
import state from 'libstate.so'; // 加载 native 模块

@Entry
@Component
struct BottomTabs {
  @State cartCount: number = 0
  private timer: number | undefined = undefined

  aboutToAppear(): void {
    this.loadInitialState();
    this.startPolling(); // 简单轮询(进阶可用 callback)
  }

  loadInitialState(): void {
    this.cartCount = state.getCartCount() ?? 0;
  }

  startPolling(): void {
    this.timer = setInterval(() => {
      const remote = state.getCartCount();
      if (remote !== this.cartCount) {
        this.cartCount = remote;
        console.info(`[Sync] Cart count updated: ${remote}`);
      }
    }, 300);
  }

  build() {
    Row({ space: 50 }) {
      TabItem(icon: $r('app.media.home'), text: '首页')
      TabItem(icon: $r('app.media.discover'), text: '发现')

      // 购物车 Tab(带 Badge)
      Column() {
        Image($r('app.media.cart'))
          .width(24).height(24)
        if (this.cartCount > 0) {
          Text(this.cartCount.toString())
            .fontSize(10).fontColor(Color.White)
            .backgroundColor(Color.Red)
            .size({ width: 16, height: 16 })
            .borderRadius(8)
            .position({ x: 18, y: -8 })
        }
      }.onClick(() => router.pushUrl('/cart'))

      TabItem(icon: $r('app.media.profile'), text: '我的')
    }
    .justifyContent(FlexAlign.SpaceAround)
    .height(60)
    .padding(5)
    .background(Color.White)
  }
}

日志验证
复制代码
INFO: [NDK] state_get_int(cart.count) = 1
INFO: [Sync] Cart count updated: 1

五、进阶方向

功能 实现方式
真·响应式监听 在 C++ 层暴露 onStateChange(void(*cb)(const char*, const char*))
JSON 对象支持 使用 nlohmann/json 存储复杂结构
持久化存储 联动 SQLite 自动落盘
分布式同步 结合 OpenHarmony 分布式数据服务(DistributedDataManager)

七、结语:状态即真理

在现代前端开发中,UI 是状态的函数

当我们把"状态"从 UI 框架中剥离出来,变成一个独立的服务层时,我们就获得了前所未有的灵活性:

🔄 Flutter 可以是视图层

🖼️ ArkTS 也可以是视图层

💾 而真正的业务逻辑和数据,始终如一

这不仅是技术方案,更是一种架构哲学。

💬 如果你也正在构建混合架构应用,欢迎关注我,后续将推出《跨端路由系统设计》《统一埋点 SDK 实践》等系列文章。


参考资料


❤️ 欢迎交流

你在项目中是如何处理多框架状态一致性的?有没有更好的解法?

欢迎在评论区分享你的经验!

📌 关注我 @L、218,获取更多 Flutter × OpenHarmony 工程化深度内容,包括:

  • 跨端路由系统
  • 统一日志埋点
  • 分布式任务调度

让我们共同推动中国基础软件生态走向成熟!


版权声明 :本文原创,转载请注明出处及作者。商业转载请联系授权。
作者主页https://blog.csdn.net/L218

点赞 + 收藏 + 转发,让更多人告别"状态不同步"的噩梦!


📌 标签:#Flutter #OpenHarmony #状态管理 #Riverpod #ArkState #跨框架通信 #共享内存 #响应式编程 #L218 #CSDN #2025架构实践

https://openharmonycrossplatform.csdn.net/content

相关推荐
Umi·2 小时前
shell 条件测试
linux·前端·javascript
赵财猫._.2 小时前
React Native鸿蒙开发实战(八):打包发布与AppGallery上架
react native·react.js·harmonyos
小白勇闯网安圈2 小时前
wife_wife、题目名称-文件包含、FlatScience
javascript·python·网络安全·web·原型模式
北极象2 小时前
CEF 与 Electron简单对比
前端·javascript·electron
极客范儿2 小时前
华为HCIP网络工程师认证—数据链路层与MAC地址
网络·华为
小天博客2 小时前
向后端发起POST请求
开发语言·前端·javascript
中国云报2 小时前
从数据贯通到智能重构:华为工业AI平台的进化之路
人工智能·华为·重构
晚霞的不甘2 小时前
[鸿蒙2025领航者闯关]鸿蒙实战进阶:多端协同与性能优化实践心得
华为·性能优化·harmonyos
艾小码2 小时前
Pinia 入门:为什么说它是 Vuex 更具魅力的现代继任者?
前端·javascript·vue.js