OpenHarmony + Flutter 离线能力构建指南:打造无网可用的高可靠政务/工业应用 - 实践

示例图片在这里插入图片描述

引言

在边防巡检、电力抢修、野外勘探、基层政务等场景中,网络不可用是常态而非例外。然而,多数 OpenHarmony + Flutter 应用仍严重依赖在线服务,一旦断网即“瘫痪”——无法登录、不能提交表单、地图空白、数据丢失。

要构建真正高可靠的国产化应用,必须实现 全链路离线能力

  • 身份认证离线化(支持离线登录);
  • 业务数据本地持久化与同步;
  • 静态资源(UI、地图、文档)预置;
  • 网络恢复后自动回传队列。

本文将提供一套 端到端离线架构方案,结合 OpenHarmony 的分布式能力与 Flutter 的跨端优势,打造“有网增强、无网可用”的韧性应用,并附完整代码示例。


一、离线能力全景设计

┌───────────────────────────────────────────────────────┐
│                Flutter App (Dart)                     │
├───────────────┬───────────────┬───────────────────────┤
│ 离线登录      │ 本地数据库    │ 静态资源管理          │
│ - Token 缓存  │ - Hive / Moor │ - AssetBundle 预加载  │
│ - 生物认证    │ - 冲突解决    │ - 动态资源包下载      │
└───────▲───────┴───────▲───────┴───────────▲───────────┘
        │               │                   │
        ▼               ▼                   ▼
┌───────────────────────────────────────────────────────┐
│           OpenHarmony 离线支撑层 (ArkTS)              │
├───────────────┬───────────────┬───────────────────────┤
│ @ohos.account │ @ohos.data.rdb│ @ohos.file.fs         │
│ .distributed  │ /relational   │                       │
│               │               │                       │
└───────▲───────┴───────▲───────┴───────────▲───────────┘
        │               │                   │
        ▼               ▼                   ▼
┌───────────────────────────────────────────────────────┐
│            设备本地存储(SQLite / 文件系统)           │
│            + 分布式软总线(SoftBus)备用同步通道       │
└───────────────────────────────────────────────────────┘

核心理念所有关键功能必须能在无网络、无服务器情况下运行;网络仅用于增强与同步


二、离线登录:支持无网身份验证

问题

传统 JWT Token 过期后需联网刷新 → 断网即登出。

✅ 解决方案:长有效期 Token + 本地生物认证二次校验

步骤 1:登录时缓存凭证(Dart)
// lib/services/auth_service.dart
class AuthService {
static const String _tokenKey = 'offline_token';
static const String _userIdKey = 'user_id';
static const Duration _longExpiry = Duration(days: 30);
Future<void> login(String username, String password) async {
  // 1. 联网认证(首次)
  final response = await http.post('/api/login', body: {'username': username, 'password': password});
  if (response.statusCode == 200) {
  final data = jsonDecode(response.body);
  // 2. 保存长期 Token(加密存储,见前文安全篇)
  await SecureStorage().write(_tokenKey, data['token']);
  await SecureStorage().write(_userIdKey, data['userId']);
  // 3. 注册生物认证(如指纹)
  await BiometricAuth.register();
  }
  }
  // 离线登录:验证本地 Token + 生物特征
  Future<bool> offlineLogin() async {
    final token = await SecureStorage().read(_tokenKey);
    if (token == null) return false;
    // 检查 Token 是否在有效期内(宽松策略)
    final payload = Jwt.parse(token);
    if (DateTime.now().isAfter(payload.expiry.add(_longExpiry))) {
    return false; // 需重新联网
    }
    // 生物认证二次确认(防设备丢失)
    final authResult = await BiometricAuth.authenticate();
    return authResult.success;
    }
    }

安全提示:Token 存储需结合前文《安全加固》中的 TEE 加密方案。


三、本地数据库:结构化数据持久化

选型对比

方案优点缺点适用场景
Hive纯 Dart、零依赖、快无关系查询配置、缓存
Moor (Drift)SQL 支持、类型安全需 native 编译复杂业务数据
OpenHarmony RDB系统级、高性能需 ArkTS 封装高频读写

推荐组合Hive(轻量) + OpenHarmony RDB(核心业务)

示例:使用 OpenHarmony RDB 存储巡检记录(ArkTS)
// model/InspectionDb.ts
import relationalStore from '@ohos.data.relationalStore';
export class InspectionDb {
private store: relationalStore.RdbStore | null = null;
async init(context: any): Promise<void> {
  const config = relationalStore.createRdbStoreConfig('inspection.db');
  this.store = await relationalStore.getRdbStore(context, config);
  // 创建表
  await this.store.executeSql(`
  CREATE TABLE IF NOT EXISTS inspections (
  id TEXT PRIMARY KEY,
  location TEXT,
  status TEXT,
  timestamp INTEGER,
  synced BOOLEAN DEFAULT 0
  )
  `);
  }
  async saveInspection(inspection: any): Promise<void> {
    const values = new relationalStore.ValuesBucket();
    values.putString('id', inspection.id);
    values.putString('location', inspection.location);
    values.putString('status', inspection.status);
    values.putLong('timestamp', Date.now());
    values.putBoolean('synced', false);
    await this.store!.insert('inspections', values);
    }
    async getUnsyncedInspections(): Promise<any[]> {
      const resultSet = await this.store!.querySql(
      'SELECT * FROM inspections WHERE synced = 0'
      );
      const list: any[] = [];
      while (resultSet.goToNextRow()) {
      list.push({
      id: resultSet.getString(resultSet.getColumnIndex('id')),
      location: resultSet.getString(resultSet.getColumnIndex('location')),
      status: resultSet.getString(resultSet.getColumnIndex('status')),
      timestamp: resultSet.getLong(resultSet.getColumnIndex('timestamp'))
      });
      }
      resultSet.close();
      return list;
      }
      async markAsSynced(id: string): Promise<void> {
        const values = new relationalStore.ValuesBucket();
        values.putBoolean('synced', true);
        await this.store!.update(values, 'id = ?', [id]);
        }
        }
Dart 层调用(通过 MethodChannel)
// lib/repositories/inspection_repo.dart
class InspectionRepository {
static const _channel = MethodChannel('com.example.inspection_db');
Future<void> save(Inspection inspection) async {
  await _channel.invokeMethod('save', inspection.toJson());
  }
  Future<List<Inspection>> getUnsynced() async {
    final List<dynamic> rawList = await _channel.invokeMethod('getUnsynced');
      return rawList.map((e) => Inspection.fromJson(e)).toList();
      }
      Future<void> markSynced(String id) async {
        await _channel.invokeMethod('markSynced', {'id': id});
        }
        }

四、静态资源离线化:UI、地图、文档预置

1. Flutter Asset 预加载

将图片、字体、JSON 配置打包进 HAP:

# pubspec.yaml
flutter:
assets:
- assets/offline_maps/
- assets/forms/
- assets/icons/

运行时直接读取:

final ByteData data = await rootBundle.load('assets/forms/patrol_template.json');

2. 动态资源包按需下载

对超大资源(如城市离线地图),启动时检测并下载:

// lib/services/resource_manager.dart
class ResourceManager {
Future<void> ensureOfflineMap(String city) async {
  final file = File('/data/storage/el2/base/haps/entry/files/maps/$city.dat');
  if (!await file.exists()) {
  // 从内网 CDN 下载(有网时)
  final response = await http.get('http://intranet-cdn/maps/$city.dat');
  await file.writeAsBytes(response.bodyBytes);
  }
  }
  }

路径说明:OpenHarmony 应用私有目录为 /data/storage/el2/base/haps/entry/files/,无需额外权限。


五、网络状态感知与自动同步

步骤 1:监听网络变化(ArkTS)

// model/NetworkMonitor.ts
import net from '@ohos.net.connection';
export class NetworkMonitor {
private callback: () => void;
constructor(callback: () => void) {
this.callback = callback;
}
start(): void {
const conn = net.getDefaultNet();
conn.on('netAvailable', () => {
console.log(' 网络已恢复');
this.callback(); // 触发同步
});
conn.on('netUnavailable', () => {
console.log(' 网络断开');
});
}
}

步骤 2:Dart 层实现同步队列

// lib/services/sync_service.dart
class SyncService {
final InspectionRepository _repo = InspectionRepository();
Future<void> syncAll() async {
  final unsynced = await _repo.getUnsynced();
  for (final item in unsynced) {
  try {
  // 上传到服务器
  await http.post('/api/inspections', body: item.toJson());
  // 标记为已同步
  await _repo.markSynced(item.id);
  } catch (e) {
  // 网络再次中断?保留未同步状态
  break;
  }
  }
  }
  }
  // 在 main.dart 中监听
  void main() {
  runApp(MyApp());
  // 注册网络恢复回调
  const EventChannel('com.example.network/events')
  .receiveBroadcastStream()
  .listen((_) => SyncService().syncAll());
  }

六、利用 SoftBus 实现 P2P 离线协同(高级场景)

当多台设备同处无网环境(如野外作业队),可通过 OpenHarmony 软总线实现设备间数据交换:

// ArkTS: 发送未同步数据到附近设备
async function shareViaSoftBus(peerDeviceId: string) {
const unsynced = await inspectionDb.getUnsyncedInspections();
const message = JSON.stringify(unsynced);
await softbus.sendMessage(peerDeviceId, message); // 前文 SoftBus 插件
}

接收方解析后存入本地数据库,待任一设备联网即可统一回传。

价值:构建“去中心化”离线协作网络,极大提升野外作业效率。


七、测试与验证:模拟无网环境

1. DevEco Studio 网络控制

  • 设备 > 网络 > 关闭 Wi-Fi/蜂窝数据;
  • 使用 Network Profiler 验证无外发请求。

2. 自动化测试脚本

test('离线登录成功', () async {
// 模拟无网
HttpOverrides.runZoned(() {}, createHttpClient: (_) => MockHttpClient());
final success = await AuthService().offlineLogin();
expect(success, true);
});
class MockHttpClient extends HttpClient {

Future<HttpClientRequest> openUrl(String method, Uri url) {
  throw SocketException('No network'); // 强制断网
  }
  }

八、总结:离线能力 Checklist

能力是否实现验证方式
离线登录关闭网络后仍可进入主界面
本地数据存储重启应用后数据不丢失
静态资源可用无网时地图/表单正常显示
自动同步队列恢复网络后数据自动上传
P2P 协同(可选)⚠️多设备无网环境下数据互通

终极目标:用户完全感知不到“在线”与“离线”的边界——这才是真正的高可靠应用。

通过本文方案,你的 OpenHarmony + Flutter 应用将具备 军工级离线韧性,从容应对最严苛的野外与应急场景。


posted @ 2026-01-06 13:44  gccbuaa  阅读(4)  评论(0)    收藏  举报