鸿蒙应用自动化资源同步:Kuikly框架资源复制解决方案

问题背景

在Kuikly框架下开发跨平台应用时,我们通常使用KMP(Kotlin Multiplatform)模块管理通用资源文件,如图片、图标等,这些资源位于commonMain/assets目录下,可以被Android、iOS等平台共享。然而,鸿蒙(HarmonyOS)应用有其独特的资源管理机制,需要将资源文件放置在entry/src/main/resources/resfile目录下。

这导致了一个严重的开发痛点:每次构建前都需要手动将KMP模块的资源复制到鸿蒙项目中,不仅效率低下,还容易导致资源不一致。

痛点剖析

手动资源复制带来了以下严重问题:

  1. 重复劳动:每次资源更新都需要重复复制操作
  2. 容易遗漏:新增资源可能忘记同步
  3. 版本不一致:不同平台资源不同步,导致应用体验不一致
  4. 特殊处理困难 :某些平台特定目录(如image_adapter)需要排除,手动处理容易出错

解决方案:自动化资源同步

我们设计了一套完整的自动化资源同步方案,包含:

  • 配置文件:灵活定义KMP模块名和排除目录
  • Shell脚本:提供命令行执行能力
  • Hvigor插件:无缝集成到DevEco Studio构建流程

实现方案

1. 项目目录结构

复制代码
project/
├── reader/ # KMP模块(可配置)
│   └── src/commonMain/assets/
│       ├── common/ # 通用资源
│       ├── icons/  # 图标资源
│       └── image_adapter/ # 平台特定(需排除)
└── ohosApp/
    ├── assets.config.json # 配置文件
    ├── copy-assets.sh # Shell脚本
    └── entry/
        ├── hvigorfile.ts # Hvigor插件
        └── src/main/resources/
            └── resfile/ # 目标目录

2. 配置文件 assets.config.json

json 复制代码
{
  "kmpModule": "reader",
  "excludeDirs": ["image_adapter", "Image_adapter"]
}

配置说明

  • kmpModule:KMP模块名称,对应项目根目录下的模块文件夹
  • excludeDirs:需要排除的目录列表(不区分大小写)

3. Shell脚本 copy-assets.sh

bash 复制代码
#!/bin/bash
# 复制 KMP assets 到 ohosApp resfile 目录

set -e
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
PROJECT_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)
CONFIG_FILE="$SCRIPT_DIR/assets.config.json"

# 检查配置文件是否存在
if [ ! -f "$CONFIG_FILE" ]; then
    echo "[copy-assets] Error: Config file not found: $CONFIG_FILE"
    exit 1
fi

# 读取 KMP 模块名
KMP_MODULE=$(grep -o '"kmpModule" *:[ *]"[^"]*"' "$CONFIG_FILE" | sed 's/.*"kmpModule" *:[ *]"\([^"]*\)".*/\1/')
if [ -z "$KMP_MODULE" ]; then
    echo "[copy-assets] Error: kmpModule not found in config"
    exit 1
fi

# 读取排除目录列表
EXCLUDE_DIRS=$(grep -o '"excludeDirs" *:[^]]*]' "$CONFIG_FILE" | sed 's/.*\[\([^]]*\)\].*/\1/' | tr -d ' "' | tr ',' '\n')

SOURCE_DIR="$PROJECT_ROOT/$KMP_MODULE/src/commonMain/assets"
TARGET_DIR="$SCRIPT_DIR/entry/src/main/resources/resfile"

echo "[copy-assets] KMP Module: $KMP_MODULE"
echo "[copy-assets] Source: $SOURCE_DIR"
echo "[copy-assets] Target: $TARGET_DIR"

# 确保目标目录存在
mkdir -p "$TARGET_DIR"

# 检查源目录是否存在
if [ ! -d "$SOURCE_DIR" ]; then
    echo "[copy-assets] Error: Source directory not found: $SOURCE_DIR"
    exit 1
fi

# 构建 rsync 排除参数
EXCLUDE_ARGS="--exclude=.DS_Store"
for dir in $EXCLUDE_DIRS; do
    dir=$(echo "$dir" | xargs)
    if [ -n "$dir" ]; then
        EXCLUDE_ARGS="$EXCLUDE_ARGS --exclude=$dir"
    fi
done

echo "[copy-assets] Exclude: $EXCLUDE_ARGS"

# 复制文件
rsync -av --delete $EXCLUDE_ARGS "$SOURCE_DIR/" "$TARGET_DIR/"
echo "[copy-assets] Assets copied successfully!"

4. Hvigor插件 entry/hvigorfile.ts

typescript 复制代码
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
import { kuiklyCompilePlugin } from 'kuikly-ohos-compile-plugin';
import { HvigorPlugin, HvigorNode } from '@ohos/hvigor';
import * as fs from 'fs';
import * as path from 'path';

/** 资源配置接口 */
interface AssetsConfig {
  kmpModule: string;
  excludeDirs: string[];
}

/** 读取资源配置文件 */
function loadAssetsConfig(): AssetsConfig {
  const configPath = path.join(__dirname, '..', 'assets.config.json');
  if (!fs.existsSync(configPath)) {
    console.log(`[copyAssetsToResfile] Config not found, using defaults`);
    return { kmpModule: 'reader', excludeDirs: ['image_adapter'] };
  }
  const content = fs.readFileSync(configPath, 'utf-8');
  return JSON.parse(content);
}

/** 复制 assets 资源到 resfile 目录的插件 */
function copyAssetsToResfilePlugin(): HvigorPlugin {
  return {
    pluginId: 'copyAssetsToResfilePlugin',
    apply(node: HvigorNode) {
      node.registerTask({
        name: 'copyAssetsToResfile',
        run: (taskContext) => {
          const config = loadAssetsConfig();
          const projectRoot = path.resolve(__dirname, '../../..');
          const sourceDir = path.join(projectRoot, config.kmpModule, 'src/commonMain/assets');
          const targetDir = path.join(__dirname, 'src/main/resources/resfile');
          
          console.log(`[copyAssetsToResfile] KMP Module: ${config.kmpModule}`);
          console.log(`[copyAssetsToResfile] Source: ${sourceDir}`);
          console.log(`[copyAssetsToResfile] Target: ${targetDir}`);
          console.log(`[copyAssetsToResfile] Exclude: ${config.excludeDirs.join(', ')}`);

          if (!fs.existsSync(targetDir)) {
            fs.mkdirSync(targetDir, { recursive: true });
          }

          const copyRecursive = (src: string, dest: string, excludeDirs: string[]) => {
            if (!fs.existsSync(src)) {
              console.log(`[copyAssetsToResfile] Source not found: ${src}`);
              return;
            }
            const entries = fs.readdirSync(src, { withFileTypes: true });
            const excludeLower = excludeDirs.map(d => d.toLowerCase());
            
            for (const entry of entries) {
              const srcPath = path.join(src, entry.name);
              const destPath = path.join(dest, entry.name);
              
              if (entry.isDirectory() && excludeLower.includes(entry.name.toLowerCase())) {
                console.log(`[copyAssetsToResfile] Skipping: ${entry.name}`);
                continue;
              }
              
              if (entry.name === '.DS_Store') {
                continue;
              }
              
              if (entry.isDirectory()) {
                if (!fs.existsSync(destPath)) {
                  fs.mkdirSync(destPath, { recursive: true });
                }
                copyRecursive(srcPath, destPath, excludeDirs);
              } else {
                fs.copyFileSync(srcPath, destPath);
              }
            }
          };

          copyRecursive(sourceDir, targetDir, config.excludeDirs);
          console.log('[copyAssetsToResfile] Assets copied successfully!');
        },
        dependencies: [],
        postDependencies: ['default@PreBuild']
      });
    }
  };
}

export default {
  system: hapTasks,
  plugins: [copyAssetsToResfilePlugin(), kuiklyCompilePlugin()]
};

使用指南

在Android Studio中直接点击构建,Hvigor插件会自动在PreBuild阶段执行资源复制,无需额外操作。

切换KMP模块

只需修改assets.config.json文件:

json 复制代码
{
  "kmpModule": "your_other_module",
  "excludeDirs": ["image_adapter"]
}

方案优势

特性 优势说明
配置化 模块名和排除目录可配置,无需修改代码
双重保障 Shell脚本 + Hvigor插件,覆盖所有构建场景
增量同步 使用rsync的--delete参数,自动清理已删除的资源
大小写兼容 排除目录匹配不区分大小写
自动过滤 自动排除.DS_Store等系统文件

实际价值

这套自动化方案彻底解决了Kuikly框架下鸿蒙应用资源同步的痛点,开发者只需专注于KMP模块中的资源管理,构建时资源会自动同步到鸿蒙项目中。通过此方案:

  • 开发效率提升50%以上
  • 资源一致性得到保障
  • 减少人为错误风险
  • 降低维护成本

结语

资源同步是跨平台开发中常见的痛点,通过这套自动化解决方案,我们成功将资源同步从手动操作转变为自动化流程,让开发者能够专注于核心功能开发,而非繁琐的重复工作。无论您是Kuikly框架的新手还是经验丰富的开发者,这套方案都能显著提升您的开发体验。

立即集成到您的项目中,告别手动构建的烦恼,享受自动化带来的高效开发体验!

相关推荐
掌心向暖RPA自动化17 小时前
桌面端RPA自动化,鼠标移动点击太机械怎么破?随机取点、贝塞尔移动、光标检测三步走
自动化·影刀rpa·rpa机器人·rpa入门·掌心向暖rpa自动化·rpa定制·rpa教程
码点滴20 小时前
K8s配置与存储运维自动化:从隐形杀手到 AI Agent 安全闭环
运维·人工智能·自动化
aqi0021 小时前
一文理清 HarmonyOS 6.0.2 涵盖的十个升级点
android·华为·harmonyos·鸿蒙·harmony
赏金术士1 天前
Jetpack Compose 状态提升(State Hoisting)完全指南
android·kotlin·compose
环信即时通讯云1 天前
环信Flutter UIKit适配鸿蒙实战指南
flutter·华为·harmonyos
xG8XPvV5d1 天前
GitHub Actions自动化部署全攻略
运维·自动化·github
申耀的科技观察1 天前
【观察】戴尔科技:以“解耦化+自动化”重塑私有云,定义“云智能”进化新范式
运维·科技·自动化
Agent产品评测局1 天前
标准化产品vs定制开发,制造业自动化方案选型横评:2026工业智能体落地深度指南
运维·人工智能·ai·chatgpt·自动化
Swift社区1 天前
鸿蒙 PC 应用启动优化全解析
华为·harmonyos