鸿蒙实战开发学习:【HiView插件开发】

概述

Hiview是一个跨平台的终端设备维测服务集,其中是由插件管理平台和插件实现的各自功能构成整套系统。 本文描述了hiview插件开发的全部流程。

插件的概念

整节部分包括了插件的概念,事件源的概念,流水线的概念等基本概念

插件的定义

hiview插件可以理解为运行在hiview进程中,随hiview二进制一同交付的完成独立功能的维测、故障管理模块。

插件可以是自驱动的事件源,也可以是由外部事件驱动的一般插件。其中事件驱动有两种方式:流水线驱动和订阅查询驱动。

单个插件可以静态加载(永远不会被卸载)和插件代理加载,代理加载其中包含:开机不加载运行时动态加载卸载、开机加载之后再动态卸载加载。

插件选择被代理时插件可以被动态卸载和加载,在已经被卸载的情况下,当外部驱动事件到来以后被加载。

特定的插件是通过继承Plugin这个类实现特定的插件功能,其相对应的插件由多态的形式存在并且流转在插件管理平台中。

事件处理或者驱动插件根据不同需求,需要继承Plugin并且实现如下方法或者使用如下的接口:

class Plugin {
public:/* 插件生命周期中,当前插件被平台加载起来后调用,可以用于初始化一些数据 */virtual void OnLoad();/* 插件生命周期中,当前插件被平台卸载前调用,可以用于回收一些数据 */virtual void OnUnload();/* 运行期判断当前插件是否被加载,返回true是被需要被平台加载;false是将永远不会被平台加载。Plugin默认返回true */virtual bool ReadyToLoad();/* 当前插件在流水线上时,接收流水线上外部驱动事件,或者当前插件自己设置的延时事件,并且处理事件。其处理过程是当前插件定义是的线程中,若没有指定则在当前线程进行 */virtual bool OnEvent(std::shared_ptr<Event>& event) override;/* 当前插件在流水线上,且是当前流水线上第一个插件时,判断是否流转整条流水线。返回true是流转整条流水线;false是不流转整条流水线 */virtual bool CanProcessEvent(std::shared_ptr<Event> event) override;/* 当前插件在流水线上时,判断当前事件源是否进入暂停状态。返回true是进行处理,将会继续调用插件的OnEvent方法;false将进入事件源PauseDispatch方法,进行处理,然后继续执行后续操作 */virtual bool CanProcessMoreEvents() override;/* 可以通过hidumper命令行查询当前插件的dump信息,该接口就是实现能dump出来那些内容 */virtual void Dump(int fd, const std::vector<std::string>& cmds);/* 当前插件作为一个事件动态订阅者,接收通过动态订阅来的外部驱动事件,并且处理事件 */virtual void OnEventListeningCallback(const Event &msg);// 以下为接口/* 当前插件作为动态订阅者时,添加需要订阅的事件。订阅的事件分为依靠eventName订阅和EventIdRange(eventId范围也可以是全范围或者单一id)订阅。type是事件类型 */void AddEventListenerInfo(uint32_t type, const EventListener::EventIdRange &range = EventListener::EventIdRange(0));void AddEventListenerInfo(uint32_t type, const std::set<EventListener::EventIdRange> &listenerInfo);void AddEventListenerInfo(uint32_t type, const std::string& eventName);void AddEventListenerInfo(uint32_t type, const std::set<std::string> &eventNames);/* 设置延时执行事件 等时间到了以后在OnEvent中执行 */void DelayProcessEvent(std::shared_ptr<Event> event, uint64_t delay);/* 得到hiview插件管理平台接口的上下文 */HiviewContext* GetHiviewContext();const std::string& GetName();const std::string& GetVersion();void SetVersion(const std::string& version);
}

插件的生命周期

插件目前只有OnLoad和OnUnload两个生命周期回调。

这里的dynamic插件仅仅表示单一插件实体是以so的形式体现,名称为libxxx.z.so。

dynamic插件与上文的动态加载卸载插件没有关系。dynamic插件的so也与[插件包]的so也没有关系。

dynamic插件与静态插件没有功能上面的区别,只区别插件的实现形式。

其生命周期调用逻辑如下

插件的注册方式:

插件的注册方式有三种:

名称说明实例
静态注册通过使用宏定义REGISTER(xxx);注册,该种注册方式的插件不能被卸载实例详见test/plugins/examples/event_processor_example1中的插件
代理注册通过使用宏定义REGISTER_PROXY(xxx);注册,开机不加载运行时动态加载卸载实例详见test/plugins/examples/event_processor_example3中的插件
代理注册且开机加载通过使用宏定义REGISTER_PROXY_WITH_LOADED(xxx);注册,开机加载之后再动态卸载加载实例详见test/plugins/examples/event_processor_example4中的插件

事件源的定义

事件源是继承于EventSource实现特定的事件源功能。事件源也是一种特殊的插件,EventSource继承于[Plugin]可以实现或者使用其中公共的方法。事件源作为事件的驱动者,不能被代理加载,只能静态加载。

事件源需要继承EventSource这个类实现如下方法或者使用如下的接口,根据需求也需实现[Plugin]类中的虚方法:

class EventSource : public PipelineEventProducer, public Plugin {
public:/* 开启事件源,当平台加载完成,加载事件源之后执行,可以用于初始化或者开启一些事件源数据或者事件 */virtual void StartEventSource();/* 通知事件生产者事件已完成其传递 */virtual void Recycle(PipelineEvent* event);/* 暂停调度并安排恢复调度 */virtual void PauseDispatch(std::weak_ptr<Plugin> plugin);//以下为接口/* 将事件下发到各个流水线,开始当前事件源下流水线的运作 */bool PublishPipelineEvent(std::shared_ptr<PipelineEvent> event);/* 动态添加流水线到当前事件源 */void AddPipeline(std::shared_ptr<Pipeline> pipeline);
};

事件源实例详见test/plugins/examples/event_source_example或者test/plugins/examples_bundle/bundle_event_source_example

插件包的概念

根据业务的需求,插件可以不用全部编译在hiview二进制中。支持将若干个插件编译成为独立的插件包,以独立so的形式提供,并且一个so对应于一个plugin_config文件。 例如libxxx.z.so,对应于xxx_plugin_config配置文件。

hiview插件管理平台插件包做如下说明:

1、插件包作为一个独立的个体在插件管理平台中运行,内部插件、流水线或者事件源都与在hiview二进制中的插件没有功能上的差异。

2、插件包中的插件可以插入到hiview二进制中的流水线中。

3、无论在何处的订阅者,根据其订阅的规则都可以收到全平台发送出来的相应事件。

插件跨包注册流水线实例详见test/plugins/examples_bundle/bundle_plugin_example2中的插件

流水线的概念

流水线是用于处理多个插件协同完成任务。流水线上面可以对应多个插件,流水线上面的执行插件的顺序是根据配置文件上写的先后顺序执行。

事件源与流水线的概念对应,关系为一对多关系。一个事件源可以有n个流水线,一个流水线可以有n个插件。

事件源在平台初始化后可以循环的调用PublishPipelineEvent方法将事件源新建的事件向绑定的流水线分发,分发前会向流水线询问是否处理该事件,这里默认使用流水线中的第一个插件的CanProcessEvent接口。如果返回true处理该事件,则会进行后续的处理;如果返回false则当前整条流水线不处理该事件。

流水线处理流程详见整个test/plugins/examples模块。

hiview配置文件

hiview插件管理平台通过“[平台配置文件]”配置相关“[插件配置文件]”的路径信息、生成日志路径等信息。

平台配置文件

“平台配置文件”默认在设备/system/etc/hiview/下面的hiview_platform_config,具体内容如下:

DEFAULT_PLUGIN_CONFIG_NAME = "plugin_config"
PLUGIN_CONFIG_FILE_DIR = "/system/etc/hiview/"
DYNAMIC_LIB_SEARCH_DIR = "/system/lib/"
DYNAMIC_LIB64_SEARCH_DIR = "/system/lib64/"
WORK_DIR = "/data/log/hiview/"
COMMERCIAL_WORK_DIR = "/log/LogService/"
PERSIST_DIR = "/log/hiview/"
名称说明
DEFAULT_PLUGIN_CONFIG_NAME表示默认的插件配置文件名字,plugin_config表示编译在hiview二进制中插件的配置文件名字,基于此名字
DEFAULT_PLUGIN_CONFIG_NAMExxx_plugin_config表示编译在libxxx.z.so中插件的配置文件名字
PLUGIN_CONFIG_FILE_DIR表示插件配置文件所在的位置
DYNAMIC_LIB_SEARCH_DIR表示插件包在32位系统中的位置
DYNAMIC_LIB64_SEARCH_DIR表示插件包在64位系统中的位置
WORK_DIR工作日志路径
PERSIST_DIR持久日志路径

插件配置文件

在hiview二进制中的插件需要通过编写build/plugin_build.json文件来配置相关信息,编译的时候会自动生成plugin_config文件。

插件配置需要考虑下面几个部分的内容:

名称说明
插件信息包含插件类的名称、插件对应的gn的路径、gn中编译的名称
平台配置规则包含对应的rom、ram、加载时间等信息
插件加载的顺序平台将安装配置的先后顺序加载。优先根据加载时间排序,若加载时间相同、则按照配置的顺序加载。
流水线配置如果插件参与流水线,将插件配置到相应的流水线下
流水线组(事件源)的配置将流水线配置到对应的事件源下面
线程配置模型配置插件是否需要线程

实例plugin_build.json如下:

{"plugins": {                                # 插件列表信息"SysEventSource": {                     # 插件名,必须为对应的类名(事件源也是插件,也需要在此配置)"path": "core",                     # 插件位置,插件gn所在的位置"name": "sysevent_source"           # 插件的编译名,在gn中插件所对应的部分的编译名},"SysEventService": {"path": "plugins/eventservice","name": "sys_event_service"},"Faultlogger": {"path": "plugins/faultlogger","name": "faultlogger"},"EventLogger": {"path": "plugins/eventlogger","name": "eventlogger"},},"rules": [                                  # 平台平台配置规则列表{"rule": [{"rom": "1G",                # rom规则"ram": "1G"                 # ram规则}],"info": {"loadorder": {                  # 插件的载入时间配置列表"SysEventSource": {         # 插件名称,与上面"plugins"列表中的插件名称对应"loadtime": 0           # 当前插件延时多少时间之后加载,为0就是立即加载},                          # 插件加载顺序,先按加载时间先后加载,然后按照当前列表顺序加载。"Faultlogger": {"loadtime": 0},"EventLogger": {"loadtime": 0},"SysEventService": {"loadtime": 0}},"pipelines": {                  # 流水线配置列表"SysEventPipeline": [       # 流水线名称"SysEventService",      # 流水线中的插件,与上面"plugins"列表中的插件名称对应"Faultlogger",          # 流水线中插件的执行顺序按照列表中先后顺序执行"EventLogger"]},"pipelinegroups": {             # 流水线组(事件源)配置列表"SysEventSource": [         # 事件源名称,与上面"plugins"列表中的插件名称对应"SysEventPipeline"]},"threads": {                                    # 流线程配置模型列表"singledthread": {                          # 独享线程模式"SysEventSource": "sysevent_source",    # 将对应插件配置,名为"sysevent_source"的线程"SysEventService": "sysevent_service",  # 独享线程模式},"sharedthread": {"sharedthread1": ["Faultlogger", "EventLogger"]    #当前两个插件共享一个名为"sharedthread1"的线程}}}}]
}

在独立插件包的插件配置文件,由于插件实体可以不参与整体hiview的编译,所以直接编写xxx_plugin_config配置文件。不编写plugin_build.json

plugin_config实例完整文件可以参考test/resource/base/plugin_config,文件讲解如下:

plugins:7                                                       // 插件总数是七个,与下面插件个数必须对应
EventProcessorExample4[thread:t-example-2]:0 static             // 插件名称[thread:线程名称]:加载时间 插件模式
EventProcessorExample1[thread:t-example-4]:0 static             // 插件名称必须是类名
EventProcessorExample2[thread:t-example-1]:0 static             // 线程名称一样表示这两个插件共享线程,中括号内为空表示不配置线程
EventProcessorExample3[thread:t-example-4]:0 static             // 加载时间,延时多长时间以后加载,0为立马加载
EventProcessorExample5[]:0 static                               // 插件加载的顺序先按照加载时间顺序,然后在按照当前配置顺序加载
EventSourceExample[thread:t-example-3]:0 static                 // 插件模式,static表示当前插件实体静态的编译在hiview二进制中
DynamicLoadPluginExample[thread:t-example-5]:0 dynamic          // 插件模式,dynamic表示当前插件实体是以so的形式体现,名称为libDynamicLoadPluginExample.z.so
pipelines:3                                                     // 表示当前流水线有三个,与下面流水线个数必须对应
NormalPipeline:EventProcessorExample1 EventProcessorExample2 EventProcessorExample3 EventProcessorExample4      // 流水线名称:插件1 插件2 插件3 插件4(插件名需要与上面定义的插件对应)
ReversePipeline:EventProcessorExample4 EventProcessorExample3 EventProcessorExample2 EventProcessorExample1     // 流水线上插件的执行顺序是按照这个的先后顺序
SimplePipeline:EventProcessorExample3 EventProcessorExample2                                                    // 流水线名称注意不要跟其他包的流水线重复
pipelinegroups:1                                                    // 流水线组(事件源)列表,该表示,事件源有1个
EventSourceExample:NormalPipeline ReversePipeline SimplePipeline    // 事件源:流水线1 流水线2 流水线3 (流水线名需要与上面定义的流水线对应)// 事件源执行流程,按照先后顺序执行流水线,每个流水线按其在的插件的顺序执行// 前一个插件流水线中所以的插件执行完毕,才能只能下一个流水线

插件平台提供的能力

事件

插件与插件之间,插件与平台之间的交互均采用事件驱动的模式,事件定义请参考文件base/include/event.h,这里不详细讲解。

事件预定义的若干类型,在Event::MessageType中,业务也可以在\include\public_defines.h中定义自己的事件类型。

事件订阅

插件与插件之间,或者插件与单独的订阅者模块之间,可以通过事件订阅的方式进行交互。

事件订阅分为两种:

一种是单独的订阅者模式EventListener,该模式可以单独形成一个EventListener模块专门订阅事件、处理事件的小模块;也可以被插件继承用于插件监听事件,当插件选择这种监听模式将不能被动态加载和卸载

需要继承EventListener并且实现如下方法或者使用如下的接口:

class EventListener {
public:/* 该方法已经弃用,为了兼容以前的代码没有删除,新代码中不需要实现 */virtual bool OnOrderedEvent(const Event &msg);/* 该方法为接收订阅来的外部驱动事件,并且处理事件 */virtual void OnUnorderedEvent(const Event &msg) = 0;/* 该方法为定义当前订阅者的名字 */virtual std::string GetListenerName() = 0;// 以下为接口/* 添加需要订阅的事件。订阅的事件分为依靠eventName订阅和EventIdRange(eventId范围也可以是全范围或者单一id)订阅。type是事件类型 */void AddListenerInfo(uint32_t type, const EventListener::EventIdRange &range = EventListener::EventIdRange(0));void AddListenerInfo(uint32_t type, const std::set<EventListener::EventIdRange> &listenerInfo);void AddListenerInfo(uint32_t type, const std::string& eventName);void AddListenerInfo(uint32_t type, const std::set<std::string> &eventNames);/* 可以获取当前订阅者订阅的事件信息 */bool GetListenerInfo(uint32_t type, std::set<std::string> &eventNames);  bool GetListenerInfo(uint32_t type, std::set<EventListener::EventIdRange> &listenerInfo);
};

EventListener订阅者实例详见test/plugins/examples/event_processor_example2中的插件

另一种是在插件中动态订阅者,可以被动态卸载和加载。当插件是在代理模式的时候,订阅的事件到来可以动态加载起来插件进行执行,过一点时间以后动态插件再被卸载。

插件中动态订阅者实例详见test/plugins/examples/event_processor_example4中的插件

注意:区分两种订阅模式的接口,不要混合调用了,混合调用添加的订阅事件将不会被另一种方式监听到。

平台提供的功能接口

平台提供的功能在插件中可以通过调用GetHiviewContext()获得平台接口上下文。平台提供的主要的功能接口如下:

class HiviewContext {
public:/* 将继承了EventListener类的订阅者,注册到平台中,开启订阅 */void RegisterUnorderedEventListener(std::weak_ptr<EventListener> listener);/* 将作为插件中的动态订阅者,注册到平台中,开启订阅。动态插件和静态插件只要是想通过插件内部接口和方法定义的订阅者都可以使用这个注册 */void RegisterDynamicListenerInfo(std::weak_ptr<Plugin> listener);/* 发送事件到订阅者,该接口发送的事件,无论那种订阅方式都可以按规则被订阅到 */void PostUnorderedEvent(std::shared_ptr<Plugin> plugin, std::shared_ptr<Event> event);/* 卸载并从平台移除插件,之后再也不能被加载起来,慎用 */void RequestUnloadPlugin(std::shared_ptr<Plugin> caller);/* 获得平台中共享的EventLoop线程 */std::shared_ptr<EventLoop> GetSharedWorkLoop();/* 按照流水线名称获得流水线当中的插件 */std::list<std::weak_ptr<Plugin>> GetPipelineSequenceByName(const std::string& name);/* 检测平台是否已经全部加载完毕,返回true是全部初始化和加载完毕,返回false就是平台没有初始化和加载完成 */bool IsReady();/* 按照key值获得平台参数 */std::string GetHiviewProperty(const std::string& key, const std::string& defaultValue);/* 可以向平台中设置平台参数 key-value形式 */bool SetHiviewProperty(const std::string& key, const std::string& value, bool forceUpdate);/* 向平台的特定流水线中添加特定插件 */void AppendPluginToPipeline(const std::string& pluginName, const std::string& pipelineName);/* 通过插件名称获取插件或者插件的代理 */std::shared_ptr<Plugin> GetPluginByName(const std::string& name);
};

实例解析

这里以test/plugins/examples/event_source_example事件源为例讲解其注册、流转、监控等机制。

//event_source_example.h
// 该类继承FileDescriptorEventCallback,可以监听文件夹文件的变化,使用该类方法必须配置线程
class EventSourceExample : public FileDescriptorEventCallback, public EventSource {
public:EventSourceExample();~EventSourceExample();/* 为了记录当前全局的插件构造和析构的数据结构 */static std::set<std::string> count;/* 重载事件源相关的虚方法,详情参考[事件源的定义]一节 */void OnLoad() override;void OnUnload() override;void Recycle(PipelineEvent* event) override;void PauseDispatch(std::weak_ptr<Plugin> plugin) override;void StartEventSource() override{/* 添加文件监听到eventLoop线程 */GetWorkLoop()->AddFileDescriptorEventCallback("Example",std::static_pointer_cast<EventSourceExample>(shared_from_this()));}/* 监听的文件夹文件发生变化的回调 */bool OnFileDescriptorEvent(int fd, int type) override;/* 初始化需要监听的文件夹文件 */int32_t GetPollFd() override;/* 返回Poll类型 */int32_t GetPollType() override;const static inline int PIPELINE_EVENT_ID_AAA = 901000000;const static inline int PIPELINE_EVENT_ID_BBB = 901000001;const static inline int PIPELINE_EVENT_ID_CCC = 901000002;const static inline int PIPELINE_EVENT_ID_TAA = 901000010;private:/* 文件创建方法 */void CreateWatchFile(const std::string& path);/* 创建事件方法 */void CreateAndPublishEvent(const std::string& file);const static inline std::string SYSTEM_FAULT_LOG_PATH = "/data/test/faultlog";int inotifyFd_;std::map<std::string, int> fileMap_;
};

鸿蒙OpenHarmony知识已更新←前往
Android全套VIP课程免费送docs.qq.com/doc/DUmN4VVhBd3NxdExK
c4239e28a4e48de5e1bbf2ecb8e239cd.jpeg

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

低功耗高端蓝牙智能跳绳解决方案

一、方案概述 跳绳运动作为轻量、燃脂、便捷的运动之一&#xff0c;拥有庞大的人群基础。在这样的趋势下&#xff0c;智能跳绳的智能化及精细化也就顺理成章。 芯联深入智能运动健康器材市场&#xff0c;最新开发了蓝牙智能跳绳方案&#xff0c;采用双霍尔高精准计数方案&…

Nodejs 18.19.0 基本环境搭建(无版本管理)

简介 记录一次安装Nodejs的过程, 及设置install 安装路径等… 基本安装及设置步骤 1. 下载Nodejs并安装 NodeJs 18.19.0 LTS Windows安装msi, 下载后安装在自己想要的路径, 例如 D:\Softwares\Dev_Tools\nodejs cmd 中输入 nodejs version 可以查看版本 2. 设置 npm config ls…

Java17 --- SpringCloud初始项目创建

目录 一、cloud项目创建 1.1、项目编码规范 1.2、注解生效激活 1.3、导入父工程maven的pom依赖 二、创建子工程并导入相关pom依赖 2.1、相关配置文件 2.1.1、数据库配置文件内容 2.1.2、自动生成文件配置内容 三、创建微服务8001子工程 3.1、导入相关pom依赖 3.…

全面认识计算机操作系统(二)

目录 一、操作系统的诞生 相关概念&#xff1a; 1. 手工操作阶段 2. 脱机输入 / 输出阶段 &#xff08;1&#xff09;脱机输入技术 &#xff08;2&#xff09;脱机输出技术 3. 单道批处理阶段 4. 多道批处理阶段 5. 分时技术产生 6. 实时系统产生 二、现代操作系统的…

植物病害识别:YOLO水稻病害识别数据集(3000多张,3个类别,yolo标注)

YOLO水稻病害识别数据集&#xff0c;包含细菌性枯萎病&#xff0c;水稻瘟疫&#xff0c;褐斑病3个常见病害类别&#xff0c;共3000多张图像&#xff0c;yolo标注完整&#xff0c;可直接训练。 适用于CV项目&#xff0c;毕设&#xff0c;科研&#xff0c;实验等 需要此数据集或…

隐私与创新的交汇点:Partisia Blockchain 重绘技术蓝图

正当我们在这个信息泛滥的时代寻找稳固的信任锚点时&#xff0c;区块链技术应运而生&#xff0c;然而&#xff0c;正如任何科技革命都会遇到的挑战&#xff0c;一个重要的问题摆在了我们面前&#xff1a;如何在不牺牲个人隐私的前提下&#xff0c;享受区块链技术带来的好处&…

ChatGLM:CPU版本如何安装和部署使用

前段时间想自己部署一个ChatGLM来训练相关的物料当做chatgpt使用&#xff0c;但是奈何没有gpu机器&#xff0c;只能使用cpu服务器尝试使用看看效果 我部署的 Chinese-LangChain 这个项目&#xff0c;使用的是LLM&#xff08;ChatGLM&#xff09;embedding(GanymedeNil/text2vec…

深克隆和浅克隆

1.克隆 克隆是指创建对象的一个副本&#xff0c;使副本具有与原始对象相同的属性和状态。 而克隆又分深克隆和浅克隆。 2.浅克隆 浅克隆&#xff1a;只会把原对象中类型为值的属性复制一份&#xff0c;然后引用数据类型就把它们在内存中的地址复制过去 比如一个对象有 public …

我终于解决MathPage.wll文件找不到问题|(最新版Word上亲测)运行时错误,53’: 文件未找到:athPage.WLL

1、问题症状&#xff1a; 运行时错误&#xff0c;53’: 文件未找到:athPage.WLL 2、 解决方案 第一步 首先我们要先找到MathType安装目录下MathPage.wll文件&#xff0c;直接在此电脑中搜索MathPage.wll&#xff0c;找到文件所在位置。 第二步 打开Word文件&#xff0c…

Matplotlib进阶学习:基于Matplotlib实现设置Times New Roman字体

Matplotlib进阶学习&#xff1a;基于Matplotlib实现设置Times New Roman字体 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f4…

实现简易可视化拖拽生成页面

1.背景 由于一些简单、重复的页面&#xff0c;需要开发&#xff0c;会耗费大量人力和物力。因此一般会利用低代码平台&#xff0c;进行拖拽生成&#xff0c;下面就是实现简易可视化拖拽生成页面 2 简易可视化拖拽生成页面具体实现 2.1 整体页面布局 整个页面分为顶栏、内容区…

递增三元组 刷题笔记

题意为 若存在 a中的数小于b中的数&#xff0c;b中的数小于c中的数 则该数算一种方案 思路 暴力模拟优化 两层循环遍历即可 从b到c的过程我们发现 第三层并不需要循环 直接加上 大于b的数量即可 那么第一层和第三层是对称的 我们有没有可能再去掉一层循环 只做一次遍历 …

238.除自身以外数组的乘积

题目&#xff1a;给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且…

高项--价值驱动的项目管理知识体系

说要参加软考已经过去两周了&#xff0c;到现在也没啥成果&#xff0c;今天决定还是动手记录小一些东西吧&#xff0c;也方便下次打开手机的时候可以查看记忆。总体上看可以分为三个部分&#xff1a;信息技术、项目管理、法律法规&#xff0c;而项目管理里面最终要的就是这个项…

JMM(Java Memory Model)内存模型

Java内存模型&#xff0c;规范了计算机内存与java虚拟机之间的协调工作&#xff0c;即规定了 将java 虚拟机中的变量存储到内存中和从内从中取出来的内存细节。 Java内存模型中规定了所有的变量都存储在内存中&#xff0c;每条线程还有自己的工作内存&#xff0c;线程对变量的…

【SpringCloud微服务实战03】Nacos 注册中心

一、Nacos安装 官方文档安装Nacos教程:Nacos 快速开始 这里安装的是1.4.7版本,安装之后访问http://127.0.0.1:8848/nacos 管理界面如下:(用户名:nacos,密码:nacos) 二、Nacos服务注册和发现 1、在父工程中配置文件pom.xml 中添加spring-cloud-alilbaba的管理依赖:…

如何快速制作电商效果图?

我们都知道&#xff0c;做电商&#xff0c;你的产品效果是很重要&#xff0c;有些产品只要看一眼&#xff0c;消费者就想着加入购物车或者购买。 想快速制作电商效果图&#xff0c;我们可以借助以下几个方法。 1、使用电商模版 网上有很多免费和付费的电商效果图模板,&#x…

基于RK3588+Codesys+Xenomai的ARM+LINUX实时硬件平台的软PLC解决方案

产品概述 公司推出基于瑞芯微RK3588架构的AI边缘计算主板&#xff0c;RK3588是新一代国产旗舰高性能64位八核处理器&#xff0c;采用8nm工艺&#xff0c;具有高算力、低功耗、超强多媒体、丰富数据接口等特点。搭载四核A76四核A55的八核CPU和ARM G610MP4 GPU&#xff0c;内置6…

顺序表详解

顺序表是最简单的一种数据结构之一&#xff0c;它的本质上就是数组&#xff0c;我们将顺序表分为静态和动态的两种形式&#xff0c;静态顺序表只能存储有限个数据&#xff0c;而动态顺序表可以根据自己的需求来开辟空间存储数据。 下面是静态顺序表的结构&#xff0c;本质上就是…

一文了解Spring的SPI机制

文章目录 一文了解Spring的SPI机制Java SPIServiceLoader Spring SPISpringboot利用Spring SPI开发starter 一文了解Spring的SPI机制 Java SPI SPI 全称 Service Provider Interface &#xff0c;是 Java提供的一套用来被第三方实现或者扩展的接口&#xff0c;它可以用来启用…