OpenHarmony应用构建工具Hvigor的构建流程

前言

OpenHarmony 应用和服务使用 Hvigor 作为工程的构建工具。本篇文章将介绍 Hvigor 的构建流程,通过修改脚本配置使 Hvigor 执行自定义任务。

Hvigor 的构建流程

  1. 加载命令行参数和环境变量;
  2. 初始化项目结构,创建 Project 和 Module 实例;
  3. 配置项目插件和任务流;
  4. 执行任务流;
// hvigor/index.js
// LiftOff 命令行辅助工具
const cli = new LiftOff({
    name: 'hvigor',
    processTitle: make_title.makeTitle('hvigor', process.argv.slice(2)),
    moduleName: exports.hvigorPath,// @ohos/hvigor-base
    configName: "hvigorFile",
    v8flags: v8flags,
    extensions: interpret.jsVariants
});
// cli options定义所有的可支持的命令行
const parser = yargs.usage("Usage", cli_options.cliOptions);
// 解析命令行
const opts = parser.argv;
function run() {
    cli.prepare({
        cwd: opts.cwd,
        require: opts.require,
        completion: opts.completion,
    }, function (env) {
        // help 指令
        if (opts.help) {
            yargs.usage('Usage: hvigor [options]')
                .example('hvigor assembleApp', 'Do assembleApp task')
                .help('h')
                .alias('h', 'help')
                .epilog('copyright 2021')
                .argv;
            exit.exit(0);
        }
        // version 指令
        if (opts.version) {
            _log.info('CLI version:', cliVersion);
            _log.info('Local version:', env.modulePackage.version || 'Unknown');
            exit.exit(0);
        }
        // 设置日志等级
        evaluateLogLevel();
​
        // 判断当前 nodejs工具版本信息
        const LOWEST_VERSION = "v14.18.3";
        if (process.version < LOWEST_VERSION) {
            _log.warn(`node version: ${process.version}`);
            _log.warn(`node version cannot be lower than ${LOWEST_VERSION}`);
            process.exit(-1);
        }
        // 1. 加载命令行参数和环境变量
        init_env_config_props.initEnvConfigProps(env, opts);
        cli.execute(env, env.nodeFlags, function () {
            return __awaiter(this, void 0, void 0, function* () {
                const taskBeginTime = process.hrtime();
                // 执行具体的 Hvigor 任务
                yield process_utils.processUtils(opts, env);
                const taskEndTime = process.hrtime(taskBeginTime);
                const realTime = pretty_hrtime.default)(taskEndTime);
                if (0 == profile_js.profile.executed) {
                    _log.error(`No task found to execute in project!`);
                }
                _log.info(`BUILD SUCCESSFUL in ${realTime}`);
            });
        });
    });
}

执行具体的 Hvigor 的任务

// hvigor/src/process-utils.js
function processUtils(opts, env) {
    ···
    // 2. 初始化项目结构,创建 Project 和 Module 实例;
    init.init(env);
    // 3. 配置项目插件和任务流;
    const project = configuration.configuration(env);
    // 4. 执行任务流;
    const executeMode = env.configProps.get(modeAlias);
    switch (executeMode) {
       ···
    }
}

1. 加载命令行参数和环境变量

读取命令行参数,并把参数和配置文件的路径放入全局配置项中。

// hvigor/src/init-env-config-props.js
function initEnvConfigProps(env, opts) {
    ···
    // 获取项目级别 build-profile.json5 文件路径,[项目路径]/build-profile.json5
    const configFilePath = path.resolve(process.cwd(), hvigor_base.HvigorCommonConst.PROJECT_CONFIG_FILE);
    ···
    const properties = new Map();
    // 获取命令行 prop(p) 参数,放入 Map 中
    if (opts.prop !== undefined) {
        [].concat(opts.prop).forEach((value) => {
            const arr = value.split('=');
            properties.set(arr[0], arr[1]);
        });
    }
    // 把"项目级别 build-profile.json5 文件路径"、"命令行 prop(p) 参数集合"和"命令行 mode(m) 模式参数"配置进环境变量中
    env.configProps = new Map([
        [configFileName, configFilePath],
        [propertiesAlias, properties],
        [modeAlias, opts.mode]
    ]);
    return configFilePath;
}

2. 初始化项目结构,创建 Project 和 Module 实例

工程结构实例化。

// hvigor/src/lifecycle/init.js
function init(env) {
    // env.modulePath 是在命令行辅助工具 LiftOff 中加载的模块函数,"modulePath": "[项目路径]/node_modules/@ohos/hvigor-base/index.js",
    const vigorConfigInst = require(env.modulePath).vigorConfigInst;
    // 把从命令行加载来的 prop(p) 参数放入 vigorConfigInst 实体的 ExtraConfig 中
    vigorConfigInst.setExtraConfig(env.configProps.get(propertiesAlias));
    // 初始化项目,参数:[项目路径]/build-profile.json5,[项目路径]
    vigorConfigInst.initRootProject(path.resolve(env.cwd, hvigor_base.HvigorCommonConst.PROJECT_CONFIG_FILE), env.cwd);
}

// hvigor-base/index.js
exports.vigorConfigInst = new VigorConfig();
class VigorConfig {
    constructor() {
        this._log = hvigor_log_js.HvigorLogger.getLogger(VigorConfig.name);
        this._project = undefined;
        this._projectDir = "";
        this._projectConfigFile = "";
        this._extraConfig = new Map();
    }
    ···
    // 初始化项目,参数:[项目路径]/build-profile.json5,[项目路径]
    initRootProject(projectConfigFile, projectRootDir) {
        // 配置项目级别 build-profile.json5 文件路径
        this._projectConfigFile = projectConfigFile;
        // 配置项目根路径
        this._projectDir = projectRootDir;
        // 创建项目实例,参数:[项目名称],[项目路径]
        const projectImpl = new project_impl_js.ProjectImpl(path.basename(projectRootDir), projectRootDir);
        // 读取 [项目路径]/build-profile.json5 配置数据
        const projectStructureOpt = json5_reader_js.Json5Reader.getJson5Obj(this._projectConfigFile);
        ···
        // 从配置文件中读取项目下的全部 Modules 
        projectStructureOpt.modules?.forEach(module => {
            // 校验 Module 配置参数
            validate_util_js.ValidateUtil.validateModule(module);
            // 添加子项目,根据配置文件中 Modules 循环创建 Module 实例,参数:项目实例,Module 名称,Module 的路径
            projectImpl.addSubProject(new module_impl_js.ModuleImpl(projectImpl, module.name, module.srcPath));
        });
        this._project = projectImpl;
        return projectImpl;
    }
}
class ValidateUtil {
    static validateModule(module) {
        if (module.name === undefined) {
            this.logger.errorMessageExit(`Project level build-profile.json5 lose required property: module-name`);
        }
        if (module.srcPath === undefined) {
            this.logger.errorMessageExit(`Project level build-profile.json5 lose required property: module-srcPath`);
        }
    }
}

Project 实现类

// hvigor-base/src/impl/project-impl.js
class ProjectImpl extends default_module_impl.DefaultModuleImpl {
    constructor(moduleName, moduleDir) {
        super(moduleName, moduleDir);
        // 配置项目级别 build-profile.json5 文件路径
        this._projectStructureFile = path.resolve(moduleDir, common_const.HvigorCommonConst.PROJECT_CONFIG_FILE);
        this._subProjects = new Map();
    }
    ···
}

Module 实现类

// hvigor-base/src/impl/module-impl.js
class ModuleImpl extends default_module_impl.DefaultModuleImpl {
    constructor(project, moduleName, moduleDir) {
        super(moduleName, moduleDir);
        this._project = project;
    }
    ···
}

默认 Module 实现类

// hvigor-base/src/impl/default-module-impl.js
class DefaultModuleImpl {
    constructor(moduleName, modulePath) {
        this._moduleName = moduleName;
        this._modulePath = modulePath;
        // 获取项目和模块的 package.json 文件路径
        this._packageJsonPath = path.resolve(modulePath, "package.json");
        // 获取项目和模块的 hvigorfile.js 文件路径
        this._buildFilePath = path.resolve(modulePath, common_const.HvigorCommonConst.BUILD_FILE_NAME);
    }
    ···
}

3. 配置项目插件和任务流

加载 hvigorfile.js 文件中配置的任务脚本。

// hvigor/src/lifecycle/configuration.js
function configuration(env) {
    // 整个项目的结构在 init 中初始化完成,读取 vigorConfigInst
    const vigorConfigInst = require(env.modulePath).vigorConfigInst;
    // 通过 vigorConfigInst 获取项目实例
    const project = vigorConfigInst.getProject();
    // 获取项目全部的 Module 实例,遍历加载任务脚本
    for (const subModule of project.getAllSubProjects()) {
        // 获取 Module 的 hvigorfile.js 文件路径
        const subModuleVigorFileJs = subModule.getBuildFilePath();
        // 加载任务脚本
        configModule(subModule, subModuleVigorFileJs);
    }
    // 获取 Project 的 hvigorfile.js 文件路径
    const projectVigorFileJs = path.resolve(env.cwd, hvigor_base.HvigorCommonConst.BUILD_FILE_NAME);
    // 加载任务脚本
    configModule(project, projectVigorFileJs);
    return project;
}

function configModule(module, hvigorFilePath) {
    // FA 模型
    // 工程级别
    // module.exports = require('@ohos/hvigor-ohos-plugin').legacyAppTasks
    // 模块级别
    // module.exports = require('@ohos/hvigor-ohos-plugin').legacyHapTasks
    // module.exports = require('@ohos/hvigor-ohos-plugin').legacyHarTasks

    // Stage 模型
    // 工程级别
    // module.exports = require('@ohos/hvigor-ohos-plugin').appTasks
    // 模块级别
    // module.exports = require('@ohos/hvigor-ohos-plugin').hapTasks
    // module.exports = require('@ohos/hvigor-ohos-plugin').harTasks

    // 加载 hvigorFile 任务脚本
    const moduleExported = require(hvigorFilePath);

    // 配置 Project 和 Module 的任务流
    const pluginNTasks = moduleExported(module);
    module.exported = pluginNTasks;

    // 绑定创建的 Plugin 对象到 Project 实例和 Module 实例上
    module.bindPlugin(pluginNTasks.plugin);
}

4. 执行任务流

根据命令行 mode 参数判断要执行任务流的级别。

// hvigor/src/process-utils.js
// 从环境变量中获取 mode(m) 参数
const executeMode = env.configProps.get(modeAlias);

// 根据 mode 判断要执行任务流的级别
switch (executeMode) {
    case hvigor_base.HvigorBuildConst.PROJECT_MODE: // project
        execute_task.execute(project, true, opts).then(outputInfo);
        break;
    case hvigor_base.HvigorBuildConst.MODULE_MODE: // module
        execute_task.executeModuleTask(project, opts).then(outputInfo);
        break;
    case undefined: // 未配置
        execute_task.execute(project, false, opts); 
        execute_task.executeModuleTask(project, opts).then(outputInfo);
        break;
    default:
        _log.error(`Unknown mode '${executeMode}' specified in command line,Please check!`);
}

执行任务

//hvigor/src/lifecycle/execute-task.js
function executeModuleTask(project, opts) {
    // 获取 prop(p) 参数中 module 的取值
    const modules = hvigor_base.vigorConfigInst.getExtraConfig().get("module");
    // 如果不指定 module 则默认执行所有 Module 任务
    if (modules === undefined) {
        // 从项目实例中获取全部 Module 并执行任务
        yield Promise.all(project.getAllSubProjects().map((module) => {
            execute(module, false, opts);
        })).catch(error => {
            _log.error(error);
        });
    }
    else {
        // 如果指定 module 则从项目级别 build-profile.json5 配置文件中查找匹配的 Module ,如果存在则执行任务
        yield Promise.all(modules.split(",").map((value) => {
            const values = value.split("@");
            const module = project.findModuleByName(values[0]);
            if (module === undefined) {
                _log.errorMessageExit(`Unknown module '${values[0]}' in command lines,Please check!`);
            }
            execute(module, true, opts);
        })).catch(error => {
            _log.error(error);
        });
    }
}
// 执行任务流
function execute(model, shouldCheckTask, opts) {
    return __awaiter(this, void 0, void 0, function* () {
        // 从命令行获取需要执行的任务列表
        const tasks = opts._;
        // 如果没配置则执行 default 任务
        const toRun = tasks.length ? tasks : ['default'];
        ···
        const moduleName = model.getName();
        // [项目路径]/node_modules/@ohos/hvigor-base/index.js.Hvigor
        const HvigorClass = require(index.hvigorPath).Hvigor;
        // 项目打包工具的主入口
        const vigorInst = new HvigorClass();
        // 注册任务
        const registryTasks = register_exports.registerExports(vigorInst, model.exported);
        if (needSync) {
            profile_js.profile.executed += 1;
            yield sync.sync(model, registryTasks, vigorInst);
            return;
        }
        // 筛选最终要执行的操作
        const finallyToRun = toRun.filter((taskName) => {
            return registryTasks.has(taskName);
        });
        // 指定具体要执行的模块,但是找不到对应的任务
        if (!needSync && shouldCheckTask && finallyToRun.length === 0) {
            _log.errorMessageExit(`Task ${toRun} not found in module:${moduleName}.`);
        }
        // 未指定具体要执行的模块,没有可执行任务时直接返回
        if (finallyToRun.length === 0) {
            _log.debug(`No task found to execute in ${moduleName}!`);
            return;
        }
        profile_js.profile.executed += finallyToRun.length;
        // series:串行 parallel:并行
        const runMethod = opts.series ? 'series' : 'parallel';
        // 执行任务流
        vigorInst[runMethod](finallyToRun)(function (err) {
            if (err) {
                _log.errorExit(err.message);
            }
        });
    });
}

注册任务

// hvigor/src/register-exports.js
function registerExports(vigorInst, hvigorFileObj) {
    const registryTasks = new Map();
    const taskNames = Object.keys(hvigorFileObj);
    if (taskNames.length) {
        taskNames.forEach((taskName) => {
            const exportObj = hvigorFileObj[taskName];
            // plugin 不会被注册
            if (exportObj instanceof Task) {
                const task = exportObj.registry();
                vigorInst.task(taskName, task);
                registryTasks.set(taskName, exportObj instanceof DefaultSyncTask);
            }
        });
    }
    return registryTasks;
}

Hvigor 的插件和任务流

Build 任务脚本

// hvigor-ohos-plugin/index.js
// Stage 模型的 hap 任务流
const hapTasks = (module) => {
    const plugin = plugin_factory_js.PluginFactory.getHapPlugin(module);
    return {
        plugin,
        assembleHap: plugin.assembleHap,
        clean: plugin.clean,
        compileNative: plugin.compileNative,
        sync: plugin.sync,
        buildPreviewerResource: plugin.buildPreviewerRes
    };
};
// FA 模型的 hap 任务流
const legacyHapTasks = (module) => {
    const plugin = plugin_factory_js.PluginFactory.getHapPlugin(module, true);
    return {
        plugin,
        assembleHap: plugin.assembleHap,
        clean: plugin.clean,
        compileNative: plugin.compileNative,
        buildPreviewerResource: plugin.buildPreviewerRes,
        sync: plugin.sync
    };
};
// Stage 模型的 app 任务流
const appTasks = (module) => {
    const plugin = plugin_factory_js.PluginFactory.getAppPlugin(module);
    return {
        plugin,
        assembleApp: plugin.assembleApp,
        clean: plugin.clean,
        sync: plugin.sync
    };
};
// FA 模型的 app 任务流
const legacyAppTasks = (module) => {
    const plugin = plugin_factory_js.PluginFactory.getAppPlugin(module, true);
    return {
        plugin,
        assembleApp: plugin.assembleApp,
        clean: plugin.clean,
        sync: plugin.sync
    };
};
// Stage 模型的 Har 插件
const harTasks = (module) => {
    const plugin = plugin_factory_js.PluginFactory.getHarPlugin(module);
    return {
        plugin,
        clean: plugin.clean,
        assembleHar: plugin.assembleHar,
        assembleSubHar: plugin.assembleSubHar,
        buildPreviewerResource: plugin.buildHarPreviewerRes
    };
};
// FA 模型的 Har 插件
const legacyHarTasks = (module) => {
    const plugin = plugin_factory_js.PluginFactory.getHarPlugin(module, true);
    return {
        plugin,
        clean: plugin.clean,
        assembleHar: plugin.assembleHar,
        assembleSubHar: plugin.assembleSubHar,
        buildPreviewerResource: plugin.buildHarPreviewerRes
    };
};

Plugin 工厂类

// hvigor-ohos-plugin/src/plugin/factory/plugin-factory.js
class PluginFactory {
    static getAppPlugin(project, isFaMode = false) {
        // 创建 AppPlugin 实例
        return new app_plugin_js.AppPlugin(project, isFaMode);
    }
    static getHapPlugin(module, isFaMode = false) {
        // 创建 HppPlugin 实例
        return new hap_plugin_js.HapPlugin(module, isFaMode);
    }
    static getHarPlugin(module, isFaMode = false) {
        // 创建 HarPlugin 实例
        return new har_plugin_js.HarPlugin(module, isFaMode);
    }
}

HapPlugin & HarPlugin

// hvigor-ohos-plugin/src/plugin/hap-plugin.js
class HapPlugin extends abstract_module_plugin_js.AbstractModulePlugin {
    constructor(module, isFaMode) {
        super(module, isFaMode);
    }
    initTasks() {
        // Hap 打包的任务流
        this.assembleHap = new assemble_hap_js.AssembleHap(this._taskService, this.isFaMode);
        // Clean 任务
        this.clean = new clean_js.Clean(this._taskService);
        // Native 代码编译任务
        this.compileNative = new compile_native_js.CompileNative(this._taskService);
        // Module 级别的 Sync 任务
        this.sync = new sync_module_js.SyncModule(this._module);
        // Hap 在 Priviewer 模式构建的任务流
        this.buildPreviewerRes = new build_previewer_res_js.BuildPreviewerRes(this._taskService, this.isFaMode);
    }
}

// hvigor-ohos-plugin/src/plugin/har-plugin.js
class HarPlugin extends abstract_module_plugin_js.AbstractModulePlugin {
    constructor(module, isFaMode) {
        super(module, isFaMode);
    }
    initTasks() {
        // Har 打包的任务流
        this.assembleHar = new assemble_har_js.AssembleHar(this._taskService, this.isFaMode);
        // 子 Har 打包的任务流
        this.assembleSubHar = new assemble_sub_har_js.AssembleSubHar(this._taskService, this.isFaMode);
        // Clean 任务
        this.clean = new clean_js.Clean(this._taskService);
        // Har 在 Priviewer 模式构建的任务流
        this.buildHarPreviewerRes = new build_har_previewer_res_js.BuildHarPreviewerRes(this._taskService, this.isFaMode);
    }
}
初始化 ModulePlugin 结构实例
// hvigor-ohos-plugin/src/plugin/common/abstract-module-plugin.js
class AbstractModulePlugin {
    constructor(module, isFaMode) {
        this._log = ohos_logger_js.OhosLogger.getLogger(AbstractModulePlugin.name);
        this._module = module;
        const project = module.getProject();
        this.isFaMode = isFaMode;
        // FA 模型
        if (this.isFaMode) {
            // 创建 LegacyProjectModelImpl 实例,LegacyProjectModelImpl 为单例,同一个项目只会创建一次
            this._projectModel = legacy_project_model_impl_js.LegacyProjectModelImpl.getInstance(project.getModuleDir(), project.getName());

            // 创建 LegacyModuleModelImpl 实例
            this._moduleModel = new legacy_module_model_impl_js.LegacyModuleModelImpl(module.getModuleDir(), module.getName(), this._projectModel);

            // 检查任务脚本和模块级别 build-profile.json5 中 apiType 是否匹配
            this.checkModuleStatus(hap_extra_info_js.ApiType.FA);
        }
        // Stage 模型
        else {
             // 创建 ProjectModelImpl 实例,ProjectModelImpl 为单例,同一个项目只会创建一次
            this._projectModel = project_model_impl_js.ProjectModelImpl.getInstance(project.getModuleDir(), project.getName());

            // 创建 ModuleModelImpl 实例
            this._moduleModel = new module_model_impl_js.ModuleModelImpl(module.getModuleDir(), module.getName(), this._projectModel);

            // 检查任务脚本和模块级别 build-profile.json5 中 apiType 是否匹配
            this.checkModuleStatus(hap_extra_info_js.ApiType.STAGE);
        }
        
        // 创建 Module 任务服务实例
        this._taskService = new module_task_service_js.ModuleTaskService(this._projectModel, this._moduleModel);

        // 初始化任务
        this.initTasks();
    }
    checkModuleStatus(apiType) {
        // 获取模块级别 build-profile.json5 中 apiType 配置信息
        const curApiType = this._moduleModel.getProfileOpt().apiType === undefined ?
            hap_extra_info_js.ApiType.STAGE :
            this._moduleModel.getProfileOpt().apiType;
        if (curApiType !== apiType) {
            this._log._buildError("The 'hvigorfile.js' import is not match the apiType!")
                ._solution(`Change the apiType to '${apiType}'.`)
                ._file(this._moduleModel.getProfilePath())
                ._printErrorAndExit(this._moduleModel.getName());
        }
    }
    ···
}
创建 FA 模型 LegacyModuleModelImpl 实例

FA 模型的模块持久化数据模型,包含模块源码数据,配置数据等

// hvigor-ohos-plugin/src/model/module/legacy-module-model-impl.js
class LegacyModuleModelImpl extends core_module_model_impl_js.CoreModuleModelImpl {
    constructor(modulePath, moduleName, parentProject) {
        super(modulePath, moduleName, parentProject);
        this._legacyAbilitiesMap = new Map();
        this.initDefaultTargetSourceSet();
    }
    ···
    initDefaultTargetSourceSet() {
        // 配置 [模块路径]/src/main 文件夹下配置文件 config.json 路径和 resources 路径实例
        const defaultTargetSourceSet = new legacy_target_source_set_impl_js.LegacyTargetSourceSetImpl(path.default.resolve(this._sourceRootDir, "main"));
        // defaultTargetName = 'default'
        this.targetSourceSetMap.set(defaultTargetName, defaultTargetSourceSet);
        this.initAbilityInfo(defaultTargetName, defaultTargetSourceSet);
        // 创建 [模块路径]/src/ohosTest 文件夹下配置文件 config.json 路径和 resources 路径
        const ohosTestTargetSourceSet = new legacy_target_source_set_impl_js.LegacyTargetSourceSetImpl(path.default.resolve(this._sourceRootDir, "ohosTest"));
        // ohosTestTargetName = 'ohosTest'
        this.targetSourceSetMap.set(ohosTestTargetName, ohosTestTargetSourceSet);
        // 初始化 Ability 信息
        this.initAbilityInfo(ohosTestTargetName, ohosTestTargetSourceSet);
    }
    ···
    // 初始化 Module 中的 Ability 信息
    initAbilityInfo(targetName, targetSourceSet) {
        ···
        // 读取 config.json 配置项
        const configJsonObj = project_file_reader_js.ProjectFileReader.getJson5Obj(configJsonPath);
        // 获取配置中 module.abilities 配置项
        const abilityObjs = configJsonObj.module.abilities ?
            configJsonObj.module.abilities : [];
        const legacyAbilities = [];
        for (let i = 0; i < abilityObjs.length; i++) {
            // 创建 FA模型 Ability 实例
            legacyAbilities.push(new legacy_ability_model_impl_js.LegacyAbilityModelImpl(configJsonPath, abilityObjs[i].name));
        }
        // 验证是否有重名的 Ability,如果重名则会报错并退出
        LegacyModuleModelImpl.validateSameNameAbility(legacyAbilities);
        // 读取 module.js 配置信息
        const jsObjs = configJsonObj.module.js ? configJsonObj.module.js : [];
        // 查找 js 配置项中 type 为 form 的配置项
        for (let i = 0; i < jsObjs.length; i++) {
            if ('form' !== jsObjs[i].type) {
                continue;
            }
            // 创建 FA模型 Form 实例
            legacyAbilities.push(new legacy_form_model_impl_js.LegacyFormModelImpl(configJsonPath, jsObjs[i]));
        }
        this._legacyAbilitiesMap.set(targetName, legacyAbilities);
    }
}
创建 Stage 模型 ModuleModelImpl 实例

Stage 模型的模块数据管理类

// hvigor-ohos-plugin/src/model/module/module-model-impl.js
class ModuleModelImpl extends core_module_model_impl_js.CoreModuleModelImpl {
    constructor(modulePath, moduleName, parentProject) {
        super(modulePath, moduleName, parentProject);
        this.initDefaultTargetSourceSet();
    }
    initDefaultTargetSourceSet() {
        // 创建 [模块路径]/src/main 文件夹下配置文件 module.json5 路径和 resources 路径实例
        const defaultTargetSourceSet = new target_source_set_impl_js.TargetSourceSetImpl(path.default.resolve(this._sourceRootDir, "main"));
        // defaultTargetName = 'default'
        this.targetSourceSetMap.set(defaultTargetName, defaultTargetSourceSet);
        // 创建 [模块路径]/src/ohosTest 文件夹下配置文件 module.json5 路径和 resources 路径实例
        const ohosTestTargetSourceSet = new target_source_set_impl_js.TargetSourceSetImpl(path.default.resolve(this._sourceRootDir, "ohosTest"));
        // OHOS_TEST_TARGET = 'ohosTest'
        this.targetSourceSetMap.set(common_const_js.DefaultTargetConst.OHOS_TEST_TARGET, ohosTestTargetSourceSet);
    }
    ···
}
Module 模块的核心数据管理类

Hvigor 工程的 module 模块的核心数据管理类

// hvigor-ohos-plugin/src/model/module/core-module-model-impl.js
class CoreModuleModelImpl {
    constructor(modulePath, moduleName, parentProject) {
        this._log = ohos_logger_js.OhosLogger.getLogger(CoreModuleModelImpl.name);
        this.targetSourceSetMap = new Map();
        // 模块名称
        this.name = moduleName;
        // 模块路径
        this.modulePath = path.default.resolve(modulePath);
        // 项目 LegacyProjectModelImpl 实例
        this.parentProject = parentProject;
        // Module 的 hvigorfile.js 文件路径
        this.buildProfilePath = path.default.resolve(this.modulePath, this.getBuildProfileName());
        // Module 的 build-profile.json5 文件路径
        const moduleBuildProfilePath = path.default.resolve(this.modulePath, common_const_js.CommonConst.PROFILE_JSON5);
        // 使用 Schema 验证配置文件是否正确,错误则退出
        this.moduleBuildProfileCheck(moduleBuildProfilePath);
        // Module 的 src 路径
        this._sourceRootDir = path.default.resolve(modulePath, "src");
    }
}
创建 Module 任务服务实例

基于持久化 Module 的模型层提供的数据,经过处理后,提供给打包hap任务流需要使用的服务和数据

// hvigor-ohos-plugin/src/tasks/service/module-task-service.js
class ModuleTaskService extends task_service.TaskService {
    constructor(projectModel, moduleModel) {
        super(projectModel);
        this._log = ohos_logger_js.OhosLogger.getLogger(ModuleTaskService.name);
        this.computeTargets = () => {
            // 默认不配置target时,执行所有的target
            const targets = ["all"];
            // 从命令行 prop(p) 参数中获取 module 配置 
            const configModules = hvigor_base.vigorConfigInst.getExtraConfig().get("module");
            if (configModules === undefined) {
                return targets;
            }
            // 例:entry@phone@free 则 targets为 ['phone','free']
            const modules = configModules.split(",");
            for (let i = 0; i < modules.length; i++) {
                const module = modules[i];
                const values = module.split("@");
                if (this._moduleModel.getName() !== values[0]) {
                    continue;
                }
                for (let j = 1; j < values.length; j++) {
                    targets[j - 1] = values[j];
                }
            }
            return targets;
        };
        this.checkNeedPack = (targetProduct, targetName, curModuleConfigTargets) => {
            let needPack = false;
            // 如果在 prop(p) module 中配置则需要打包
            if (curModuleConfigTargets.indexOf(targetName) > -1) {
                needPack = true;
            }
            if ((curModuleConfigTargets.indexOf("all") > -1)) {
                // 默认不配置target时,不打包ohosTest的包
                needPack = targetName !== "ohosTest";
            }
            const checkApplyProduct = (targetProduct) => {
                // 获取模块级别 build-profile.json5 配置文件中对应的 target 的 applyToProducts
                let products = this._projectModel?.getTargetApplyProducts(this._moduleModel.getName(), targetName);
                // 如果没有配置applyToProducts则默认是default
                if (products === undefined) {
                    products = ["default"];
                }
                // 如果存在则需要打包
                return products.includes(targetProduct);
            };
            return checkApplyProduct(targetProduct.name) && needPack;
        };
        // 初始化 Hap 模块打包流的 target 数据集合
        this.initTargetData = () => {
            // 根据命令行参数计算需要的 target 项
            const curModuleConfigTargets = this.computeTargets();
            // 获取命令行 prop(p) 中 buildRoot 参数
            let buildRoot = hvigor_base.vigorConfigInst.getExtraConfig().get("buildRoot");
            if (!buildRoot) {
                // 默认打包路径 build
                buildRoot = build_directory_const_js.BuildDirConst.BUILD_ROOT;
            }
            // 读取模块级别 build-profile.json5 的 targets
            let targets = this._moduleModel.getProfileOpt().targets;
            if (targets === undefined) {
                targets = [{
                        name: "default",
                    }, {
                        name: "ohosTest"
                    }];
            }
            else {
                const targetNames = targets.map(target => {
                    return target.name;
                });
                if (!targetNames.includes("default")) {
                    targets.push({
                        name: "default",
                    });
                }
                if (!targetNames.includes("ohosTest")) {
                    targets.push({
                        name: "ohosTest"
                    });
                }
            }
            // 获取命令行中配置的需要打包的 product 如果未配置则默认为 default
            const targetProduct = find_target_product.findTargetProduct(this._projectModel);
            let hasTargetNeedPack = false;
            targets.forEach((target) => {
                const targetName = target.name;
                // 验证可打包的 target
                const needPack = this.checkNeedPack(targetProduct, targetName, curModuleConfigTargets);
                if (needPack) {
                    hasTargetNeedPack = true;
                }
                // 创建路径信息实例
                const pathInfo = new module_path_info_iml.ModulePathInfoIml(this._moduleModel, targetName, targetProduct.name, buildRoot);
                this._targetDataMap.set(targetName, [new hap_task_target_data.ModuleTargetData(this._moduleModel, targetName, pathInfo, targetProduct), needPack]);
            });
            if (!hasTargetNeedPack) {
                this._log._buildError(`Current product is ${targetProduct.name},There is no executable target!`)
                    ._solution(`Please check the module targets ${util.format(targets)} applyToProducts field`)
                    ._file(this._projectModel.getProfilePath())
                    ._printErrorAndExit(this._moduleModel.getName());
            }
        };
        this._moduleModel = moduleModel;
        this._targetDataMap = new Map();
        // 获取额外信息, isSupportHos 和 isStageMode
        this._hapExtraInfo = project_extra_info_service_js.ProjectExtraInfoService.getProjectExtraInfoByPath(this._moduleModel);
        // 获取编译 SDK 版本信息
        this._compileSdkVersion = this._projectModel.getProfileOpt().app.compileSdkVersion;
        // 实例化 SDK 工具信息实例
        this._sdkInfo = new sdk_info.SdkInfo(this._compileSdkVersion, this.getModuleRequiredSDKs());
        // 初始化 Target 数据
        this.initTargetData();
    }
    getModuleRequiredSDKs() {
        // 默认加载 Toolchains
        const sdkComponents = [sdkmanager_common.ComponentPath.TOOLCHAINS];
        for (const key of this._moduleModel.getSourceSetByTargetName(common_const_js.DefaultTargetConst.DEFAULT_TARGET).getCodeMap().keys()) {
            // 按照目录存在就加载的逻辑,例如main目录下存在 ets目录则加载 ets SDK 实例
            // CodeType["JS"] = "js";
            // CodeType["ETS"] = "ets";
            // CodeType["CPP"] = "cpp";
            if (code_type_enum.CodeType.CPP === key) {
                if (this._moduleModel.getProfileOpt().buildOption.externalNativeOptions) {
                    sdkComponents.push(code_type_enum.CodeType.getSDKComponentName(key));
                }
            }
            else {
                sdkComponents.push(code_type_enum.CodeType.getSDKComponentName(key));
            }
        }
        this._log.debug(`${this._moduleModel.getName()} require SDK: ${sdkComponents.join(" ").toLowerCase()}`);
        return sdkComponents;
    }
    ···
}

根据命令行中的配置找到项目级别 build-profile.json5 中的 Product

// hvigor-ohos-plugin/src/common/find-target-product.js
function findTargetProduct(projectModuleModel) {
    // 从命令行 prop(p) 参数中获取 Product 配置 
    const configProduct = hvigor_base.vigorConfigInst.getExtraConfig().get("product");
    // 没有配置时默认是default
    const targetProduct = configProduct ? configProduct : "default";
    _log.debug(`Find product from build-profile.json: %s`, targetProduct);
    // 检查项目级别 build-profile.json5 配置 app.products 项中是否有该 product
    const mProduct = array_util_js.getElementFromArr(projectModuleModel.getProfileOpt().app.products, targetProduct);
    if (!mProduct) {
        _log._buildError(`Can not find product ${targetProduct}, please check!`)
            ._solution('Please check attribute products from build-profile.json5.')
            ._file(projectModuleModel.getProfilePath())
            ._printErrorAndExit(projectModuleModel.getName());
    }
    return mProduct;
}

AppPlugin

// hvigor-ohos-plugin/src/plugin/app-plugin.js
class AppPlugin {
    constructor(project, isFaMode) {
        this._module = project;
        this._moduleName = project.getName();
        // 判断是否为 FA 模型,创建不同的 ProjectModel 实例,参数:项目路径,项目名称
        this._projectModel = isFaMode ?
            legacy_project_model_impl_js.LegacyProjectModelImpl.getInstance(project.getModuleDir(), project.getName()) :
            project_model_impl_js.ProjectModelImpl.getInstance(project.getModuleDir(), project.getName());

        // 创建项目任务服务实例
        this._taskService = new project_task_service_js.ProjectTaskService(project, this._projectModel);

        // 创建具体任务实例
        this.assembleApp = new assemble_app_js.AssembleApp(this._taskService, isFaMode);
        this.clean = new clean_js.Clean(this._taskService);
        this.sync = new sync_project_js.SyncProject(project); 
    }
    getTaskService() {
        return this._taskService;
    }
}
创建 FA 模型 LegacyProjectModelImpl 实例

FA 模型的工程持久化数据模型,包含工程源码数据,配置数据等

// hvigor-ohos-plugin/src/model/project/legacy-project-model-impl.js
class LegacyProjectModelImpl extends core_project_model_impl_js.CoreProjectModelImpl {
    constructor(projectPath, name) {
        super(projectPath, name);
    }
    static getInstance(projectPath, name) {
        if (!LegacyProjectModelImpl.instance) {
            if (projectPath === null || projectPath === undefined) {
                throw new Error("工程模型还未初始化,请正确传递工程路径");
            }
            if (name === null || name === undefined) {
                throw new Error("工程模型还未初始化,请正确传递工程名称");
            }
            LegacyProjectModelImpl.instance = new LegacyProjectModelImpl(projectPath, name);
        }
        return LegacyProjectModelImpl.instance;
    }
    ···
}
创建 Stage 模型 ProjectModelImpl 实例

Stage 模型的工程持久化数据模型,包含工程源码数据,配置数据等

// hvigor-ohos-plugin/src/model/project/project-model-impl.js
class ProjectModelImpl extends core_project_model_impl_js.CoreProjectModelImpl {
    constructor(projectPath, name) {
        super(projectPath, name);
        // 创建 App 级资源实例
        this.appRes = new app_res_model_impl_js.AppResModelImpl(path.default.resolve(projectPath, "AppScope"));
    }
    static getInstance(projectPath, name) {
        if (!ProjectModelImpl.instance) {
            if (projectPath === null || projectPath === undefined) {
                throw new Error("工程模型还未初始化,请正确传递工程路径");
            }
            if (name === null || name === undefined) {
                throw new Error("工程模型还未初始化,请正确传递工程名称");
            }
            ProjectModelImpl.instance = new ProjectModelImpl(projectPath, name);
        }
        return ProjectModelImpl.instance;
    }
    ···
}
Project 模块的核心数据管理类
// hvigor-ohos-plugin/src/model/project/project-model-impl.js
class CoreProjectModelImpl {
    constructor(projectPath, name) {
        this._log = ohos_logger_js.OhosLogger.getLogger(CoreProjectModelImpl.name);
        this.subModels = new Map();
        // 项目路径
        this.projectPath = projectPath;
        // 项目名称
        this.name = name;
        // 设置 [项目路径]/build-profile.json5 文件路径
        this._profilePath = path.default.resolve(this.projectPath, common_const_js.CommonConst.PROFILE_JSON5);
        // 验证文件是否存在,是否符合规范
        this.projectBuildProfileCheck(this._profilePath);
        // 读取 [项目路径]/build-profile.json5 文件数据
        this._profileOptions = project_file_reader_js.ProjectFileReader.getJson5Obj(this.getProfilePath());
        // 检查配置文件中 SDK 版本信息,compileApiVersion 大于 8,compatibleApiVersion 小于等于 compileApiVersion
        this.projectStatusCheck();
        // 加载 [项目路径]/build-profile.json5 文件中 Modules 配置信息,创建 ModuleModelImpl 实例并设置进 subModels 中
        this.initSubProject();
    }
    ···
}
创建 Project 任务服务实例

基于持久化 project 的模型层提供的数据,经过处理后,提供给打包app任务流需要使用的服务和数据

// hvigor-ohos-plugin/src/tasks/service/project-task-service.js
class ProjectTaskService extends task_service.TaskService {
    constructor(project, projectModel) {
        super(projectModel);
        // 初始化 Project 中 Module 需要打包的目标项集合
        this.initProductData = () => {
            // 遍历所有模块
            this._project?.getSubProjects().forEach((value) => {
                const moduleName = value.getName();
                const plugin = value.getPlugin();
                if (plugin === undefined) {
                    throw new Error(`Cannot find build file 'hvigorfile.js' in module ${moduleName}`);
                }
                const moduleTargetDataArr = [];
                if (plugin instanceof hap_plugin.HapPlugin) {
                    // 获取项目级别 build-profile.json5 中 modules 里配置的 name 匹配的配置项
                    const appModuleOpt = this._projectModel?.getModuleProfileOpt(moduleName);
                    // 获取 module 配置中 targets 配置项
                    const appModuleConfigTargets = appModuleOpt?.targets;
                    // 获取 module 的 taskService 中 targetData 信息
                    const targetDataMap = plugin.getTaskService().getTargetDataMap();
                    // 遍历 Module 中需要打包的目标项
                    targetDataMap.forEach((targetData, targetName, targetMap) => {
                        // 该target需要打包,并且在项目级别 build-profile.json5 的 targets 中配置
                        if (targetData[1] && array_util.getElementFromArr(appModuleConfigTargets, targetName) !== undefined) {
                            moduleTargetDataArr.push(targetData[0]);
                        }
                    });
                    this._productDataMap.set(moduleName, moduleTargetDataArr);
                }
            });
        };
        this._project = project;
        // 获取命令行中配置的需要打包的 product 如果未配置则默认为 default
        this._targetProduct = find_target_product.findTargetProduct(projectModel);
        // 创建 SDK 工具实例,传入编译版本和 toolchains 工具名称
        this._sdkInfo = new sdk_info_js.SdkInfo(projectModel.getCompileApiVersion(), [sdkmanager_common.ComponentPath.TOOLCHAINS]);
        // 创建路径信息实例
        this._pathInfo = new project_path_info_iml.ProjectPathInfoIml(projectModel, this._targetProduct.name);
        this._productDataMap = new Map();
        this.initProductData();
    }
    ···
}

Hvigor 自定义任务

创建任务

继承 Task 类实现自定义任务。创建自定义任务 command.js 文件,输出工程名称。

// command.js
const hvigor_base = require("@ohos/hvigor-base");

class Command extends hvigor_base.Task {
    _taskService = null
    _logger = hvigor_base.HvigorLogger.getLogger(Command.name);

    constructor(taskService) {
        super();
        this._taskService = taskService;
    }
    registry = () => {
        return this.doTaskAction;
    }
    doTaskAction = (cb) => {
        this._logger.info(`CustomCommand`);
        this._logger.info(`ProjectName : ${this._taskService.getProjectModel().getName()}`);
        cb();
    }
}
exports.Command = Command;

修改任务脚本

修改任务脚本把自定义任务加入到任务流中。

// hvigorfile
const command_js = require('./command');
const hapTasks = require('@ohos/hvigor-ohos-plugin').hapTasks;

module.exports = (module) => {
    const tasks = hapTasks(module);
    const taskService = tasks['plugin'].getTaskService();
    tasks.command = new command_js.Command(taskService);
    return tasks;
}

执行任务

执行命令 node node_modules\@ohos\hvigor\bin\hvigor.js command

效果如下:


为了能让大家更好的学习鸿蒙 (Harmony OS) 开发技术,这边特意整理了《鸿蒙 (Harmony OS)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙 (Harmony OS)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. ……

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门?:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/300520.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV | 光流估计

光流估计 光流是空间运动物体在观测成像平面上的像素运动的“瞬时速度”&#xff0c;根据各个像素点的速度的速度矢量特征&#xff0c;可以对图像进行动态分析&#xff0c;例如目标跟踪 高度恒定&#xff1a;同一点随着时间的变化&#xff0c;其亮度不会发生改变。小运动&…

WEB 3D技术 three.js 顶点交换

本文 我们来说 顶点的转换 其实就是 我们所有顶点的位置发生转变 我们整个物体的位置也会随之转变 这里 我们编写代码如下 import ./style.css import * as THREE from "three"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.j…

在Raspberry Pi Zero W中配置TFT LCD Framebuffer驱动

TFT LCD Framebuffer驱动配置 文章目录 TFT LCD Framebuffer驱动配置1、硬件准备2、软件配置2.1 启用SPI驱动2.2 TFT LCD设备驱动树配置 本文将以ILI9341 LCD为例&#xff0c;将详细介绍如何配置TFT LCD的Framebuffer驱动。 1、硬件准备 Raspberry Pi Zero W开发板一个&#x…

密码学中的Hash函数

目录 一. 介绍 二. hash函数的五个基本性质 &#xff08;&#xff11;&#xff09;压缩性 &#xff08;&#xff12;&#xff09;正向计算简单性 &#xff08;&#xff13;&#xff09;逆向计算困难性 &#xff08;&#xff14;&#xff09;弱无碰撞性 &#xff08;&…

(九)One-Wire总线-DS18B20

文章目录 One-Wire总线篇复位和应答读/写0&#xff0c;1 DS18B20篇原理图概述最主要特性几个重要的寄存器&#xff08;部分要掌握&#xff09;存储有数字温度结果的2个字节宽度的温度寄存器寄存器描述&#xff1a;寄存器说明&#xff1a; 一个字节的过温和一个字节的低温&#…

[嵌入式AI从0开始到入土]10_yolov5在昇腾上应用

[嵌入式AI从0开始到入土]嵌入式AI系列教程 注&#xff1a;等我摸完鱼再把链接补上 可以关注我的B站号工具人呵呵的个人空间&#xff0c;后期会考虑出视频教程&#xff0c;务必催更&#xff0c;以防我变身鸽王。 第一章 昇腾Altas 200 DK上手 第二章 下载昇腾案例并运行 第三章…

window使用cpolar实现内网穿透

文章目录 cpolar下载和安装启动和配置cpolar卸载 cpolar下载和安装 进入spolar官网&#xff0c;完成注册&#xff0c;下载相应的cploar版本解压和运行安装文件 配置安装路径&#xff0c;然后选择next&#xff0c;完成即可 启动和配置 点击首页的快捷图标打开网页&#xf…

python学习:实现猜数游戏和汉诺塔问题的解决

实现猜数游戏 规则&#xff1a; 计算机随机产生一个0~100的预设数字&#xff0c;让用户通过键盘输入所猜的数&#xff0c;如果大于预设的数&#xff0c;显示“遗憾&#xff0c;太大了“&#xff1b;小于预设的数&#xff0c;显示”遗憾&#xff0c;太小了“&#xff0c;如此循…

【MySQL】数据库之MMM高可用

目录 一、什么是MMM 二、关于MMM架构的说明 三、实操MMM的高可用 步骤一&#xff1a;完成主主复制、主从复制 步骤二&#xff1a;所有节点服务器都安装mysql-mmm,并完成mmm_common.conf文件的配置 步骤三&#xff1a;完成monitor节点服务器的配置文件修改mmm_mon.conf 步…

基于SSM的基金投资交易管理网站的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

K210基础实验系列

CanMV K210 开发板: CanMV K210 是由 01Studio 设计研发&#xff0c;基于嘉楠科技边缘计算芯片 K210 &#xff08; RSIC V 架构&#xff0c; 64 位双核&#xff09;方案的一款开发板&#xff0c;采用硬件一体化设计&#xff08; K210 核心板、 摄像头、 LCD 集成在一个…

mysql进阶-重构表

目录 1. 原因 2. 如何重构表呢&#xff1f; 2.1 命令1&#xff1a; 2.2 命令2&#xff1a; 2.3 命令3&#xff1a; 1. 原因 正常的业务开发&#xff0c;为什么需要重构表呢&#xff1f; 原因1&#xff1a;某张表存在大量的新增和删除操作&#xff0c;导致表经历过大量的…

深入了解Snowflake雪花算法:分布式唯一ID生成器

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

JVM中对象的创建

一.JVM运行流程 JVM向操作系统申请内存&#xff0c;初始化运行时数据区&#xff0c;接下来装载使用的类&#xff0c;执行类里面相应方法的时候为当前虚拟机栈压入一个栈帧&#xff0c;方法执行完成后栈帧出栈&#xff0c;进行垃圾回收。 二.JVM中对象的创建过程 符号引用&…

MySQL数据库进阶-事务

事务 事务由单独单元的一个或多个SQL语句组成&#xff0c;在这 个单元中&#xff0c;每个MySQL语句是相互依赖的。而整个单独单 元作为一个不可分割的整体&#xff0c;如果单元中某条SQL语句一 旦执行失败或产生错误&#xff0c;整个单元将会回滚。所有受到影 响的数据将返回到…

端云协同,Akamai 与快手联合落地 QUIC 提升海外用户视频体验

10月10日&#xff0c;负责支持和保护数字化体验且深受全球企业信赖的解决方案提供商阿卡迈技术公司( Akamai Technologies, Inc.&#xff0c;以下简称&#xff1a;Akamai )( NASDAQ&#xff1a;AKAM )携手全球领先的短视频记录和分享平台快手(HK&#xff1a;1024)通过全面落地 …

【LeetCode】150. 逆波兰表达式求值(ASCII码)

今日学习的文章链接和视频链接 leetcode题目地址&#xff1a;150. 逆波兰表达式求值 代码随想录题解地址&#xff1a;代码随想录 题目简介 即将后缀表达式转换成中缀表达式并计算。 给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 …

【无线通信专题】NFC通信模式及可能的应用方式

在文章【无线通信专题】NFC基本原理中我们讲到了NFC工作模式。其中NFC工作模式主要有三种,读写模式、卡模拟模式、点对点模式。 NFC通信模式丰富,NFC Forum定义了三种NFC设备:通用NFCForum设备、读写器设备和标签设备。这些NFC设备可以在三种通信模式下运行,并对应用案例进…

【DevOps-05】Integrate工具

一、简要说明 持续集成、持续部署的工具很多,其中Jenkins是一个开源的持续集成平台。 Jenkins涉及到将编写完毕的代码发布到测试环境和生产环境的任务,并且还涉及到了构建项目等任务。 Jenkins需要大量的插件保证工作,安装成本较高,下面会基于Docker搭建Jenkins。 二、Jenk…

Java Base64简单介绍

1. Base64工具 工具链接 2. Base64示例代码 public class Base64Demo {// 请注意&#xff0c;在处理二进制数据时&#xff08;例如图片或文件&#xff09;&#xff0c;不需要将字节数组转换为字符串再进行编码或解码&#xff0c;// 可以直接对字节数组进行Base64操作。上述…