当前位置: 首页 > news >正文

鸿蒙跨平台框架ArkUI-X

01

引言

目前,移动端主流跨平台方案有Flutter、React Native、uni-app等等,还有刚推出不久的Compose-Multiplatform,真所谓是百花齐放。这些框架各有特点,技术实现各有差异,比如Flutter通过Dart编写的UI描述对接Flutter渲染引擎,React Native 则是借助大前端成熟的发展背景,利用JS引擎生成UI描述,渲染时转化为原生控件,复用了原生渲染能力。至于选择哪种框架实现跨平台取决于项目的具体需求、开发团队的技能和偏好。今天我们探索一个新的框架——ArkUI-X。

02

ArkTS、ArkUI、ArkUI-X

在探索ArkUI-X之前,先了解一下ArkTS、ArkUI、ArkUI-X三者关系:

  • ArkTS 是华为基于TypeScript自研的开发语言,主要用于Harmony应用层开发。ArkTS在保持原 TS 基本语法风格的基础上,对 TS 的动态类型特性施加了更严格的约束,引入静态类型,减少运行时的类型检查,有助于性能提升;

  • ArkUI 是一套声明式UI开发框架。包含一系列UI组件(Text、Image)、状态管理(State、LocalStorage)、界面绘制、交互事件以及实时界面预览工具;

  • ArkUI-X 进一步将 ArkUI 扩展到多个 OS 平台,目前支持 OpenHarmony、HarmonyOS、Android、iOS,后续会逐步增加更多平台支持。

简单来说,开发者基于ArkUI框架,使用ArkTS语言进行编码,构建OpenHarmony/HarmonyOS应用,为了让应用运行到Android iOS上,利用 ArkUI-X 框架实现各OS平台的适配和构建。接下来我们将创建一个 ArkUI-X 简易Demo,通过Demo来探索ArkUI如何绘制组件到屏幕,ArkUI-X如何扩展ArkUI到Android平台,ArkUI-X如何与Android系统能力交互。

03

快速上手

3.1 环境搭建

  1. DevEco Studio

首先从DevEco Studio官方网站(https://developer.huawei.com/consumer/cn/deveco-studio/)下载并安装DevEco Studio,需要选择4.0.0以上版本,以支持 ArkUI-X 套件。然后在DevEco Studio内部,分别下载HarmonyOS SDK 和 ArkUI-X,非合作企业开发者下载OpenHarmony SDK 和 ArkUI-X。

下载OpenHarmony SDK示例:

1.png

下载ArkUI-X示例:2.png

  1. Android Studio

因为最终要打出Android的apk实现跨平台,所以需要安装Android Studio。安装步骤对客户端小伙伴都已经轻车熟路了,强调一点:需要额外配置ANDROID_HOME(Android SDK安装路径)到系统环境变量中,这是步骤一中DevEco Studio编译项目的必备条件之一。

  1. Xcode 同理,使用Xcode导入iOS项目,打IPA格式的安装包。

3.2 创建工程

完成上述的环境搭建后,就可以创建工程了。可以通过 ArkUI-X 基础模板进行创建。 3.png
创建工程

创建完成后,得到这样一个工程结构:

4.png
项目结构
  • entry:存放的是应用程序入口、核心业务逻辑以及资源文件。跨平台的公共源代码放在entry下,这点和纯Harmony项目结构一致;

  • .arkui-x/android:是一个标准的Android项目结构。但目前文件还不完整,需要Build App/Hap之后,才会生成更为完整的Android项目;

  • .arkui-x/iOS: 是一个标准的iOS项目结构,包含project.pbxproj。

Build App/Hap之后,在build目录下生成的后缀名.hap文件,可安装到HarmonyOS平台上。其它平台需要单独打包,.arkui-x下的Android项目导入到Android Studio中打出.apk;.arkui-x下的iOS项目导入到Xcode中打出.IPA。至此完成三个平台的打包工作。也就是说,在Build App过程中,ArkUI-X框架会把entry里公用ArkTS代码和resource,打入到各平台的安装包或项目文件中。

应用调试方面,目前只支持HarmonyOS调试,Android和iOS暂不支持ArkTS调试。

其实上述的环境搭建、新建项目、编译打包和安装调试,还有另一种方式实现——ACE Tools,是一套为ArkUI-X开发者提供的命令行工具。详情参见:ACE Tools快速指南(https://gitee.com/arkui-x/docs/blob/master/zh-cn/application-dev/quick-start/start-with-ace-tools.md)

04

窥探ArkUI的绘制过程

在了解 ArkUI-X 实现跨平台之前,我们先简单过一遍ArkTS编写的UI界面,是如何绘制到OpenHarmony/HarmonyOS上的,以上面的Demo为例:

// Index.ets
@Entry
@Component
struct Index {@State message: string = '今天星期五'build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)}.width('100%')}.height('100%')}
}

Build App / Hap 之后得到hap包,和Android apk类似,对hap包进行解压得到modules.abc字节码文件 (Ark Byte Code),再对字节码文件进行16进制解析,可以看到这样一段代码:

class Index extends ViewPU {constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) {super(parent, __localStorage, elmtId, extraInfo);this.__message = new ObservedPropertySimplePU('今天星期五', this, "message");this.setInitiallyProvidedValue(params);}setInitiallyProvidedValue(params: Index_Params) {if (params.message !== undefined) {this.message = params.message;}}aboutToBeDeleted() {this.__message.aboutToBeDeleted();SubscriberManager.Get().delete(this.id__());}private __message: ObservedPropertySimplePU<string>;initialRender() {   // 编译器根据ArkTS中对组件的描述部分,重新生成jsthis.observeComponentCreation2((elmtId, isInitialRender) => {Row.create();Row.height('100%');}, Row);this.observeComponentCreation2((elmtId, isInitialRender) => {Column.create();Column.width('100%');}, Column);this.observeComponentCreation2((elmtId, isInitialRender) => {Text.create(this.message);Text.fontSize(50);Text.fontWeight(FontWeight.Bold);}, Text);Text.pop();Column.pop();Row.pop();}
}

可以看出,我们用@Component编写的组件,经过ArkCompiler编译后,会生成一个继承自ViewPU的js类,这个过程和kotlin经过kotlin编译器生成java有点类似。ViewPU位于ArkUI框架的arkui_ace_engine

(https://gitee.com/openharmony/arkui_ace_engine/tree/master)仓, 部分代码如下:

// 位于 frameworks/bridge/declarative_frontend/state_mgmt/src/lib/partial_update/pu_view.ts
abstract class ViewPU extends PUV2ViewBase implements IViewPropertiesChangeSubscriber, IView {constructor(parent: IView, localStorage: LocalStorage, elmtId: number = UINodeRegisterProxy.notRecordingDependencies, extraInfo: ExtraInfo = undefined) {super(parent, elmtId, extraInfo); this.id_ = SubscriberManager.MakeId() : elmtId;if (localStorage) {this.localStorage_ = localStorage;}SubscriberManager.Add(this);}public initialRenderView(): void {this.obtainOwnObservedProperties();this.initialRender();...}// 编译器生成的js类会实现该方法,主要是对组件的描述protected abstract initialRender(): void;// implements IMultiPropertiesChangeSubscriber UI状态变化回调viewPropertyHasChanged(varName: PropertyInfo, dependentElmtIds: Set<number>): void {if (dependentElmtIds.size && !this.isFirstRender()) { // 第一次走initialRenderView()if (!this.dirtDescendantElementIds_.size && !this.runReuse_) {this.markNeedUpdate(); // 更新UI 最终调到C++}...}let cb = this.watchedProps.get(varName);if (cb && typeof cb === 'function') {// ArkTS中@Prop @Watch('xxx') value 的invokecb.call(this, varName);}}
}

从上面ViewPU的部分代码,可以提取几点信息:

  1. 构造方法第一个参数parent: IView,说明整个UI结构中存在子父组件概念,其内部维护一个子父组件关系链,这点和其它UI框架一样;

  2. 构造逻辑依次是:

  • 生成element Id,用于局部刷新;

  • 定义localStorage对象,用于页面状态共享;

  • SubscriberManager.Add(this),添加订阅,监听状态变化。

状态变化后,回调viewPropertyHasChanged,更新UI并执行@Watch装饰器逻辑。

更多实现可查看源码,当然只需要看看流程即可,因为代码的commit频次比较高,每次打开看细节都可能有所变化,而且还存在很多同名v2类、v2方法。总之,ViewPU 是所有组件的基类,ViewPU 继承自 PUV2ViewBasePUV2ViewBase 继承自 NativeViewPartialUpdate。从下面代码块的注释得知,UI渲染将在 C++ 里面实现,将通过NAPINAPIJNI 类似)完成jsC++的交互。

// 位于 frameworks/bridge/declarative_frontend/state_mgmt/src/lib/puv2_common/puv2_view_base.ts 和 puv2_view_native_base.d.ts
// implemented in C++  for release
abstract class PUV2ViewBase extends NativeViewPartialUpdate {...
}/*** NativeViewPartialUpdate aka JSViewPartialUpdate C++ class exposed to JS*  all definitions in this file are framework internal*/
declare class NativeViewPartialUpdate {constructor();markNeedUpdate(): void; // 更新UIfinishUpdateFunc(elmtId: number): void;...static create(newView: NativeViewPartialUpdate): void;
}

markNeedUpdate()主要负责UI刷新,追踪一下它的实现逻辑:

// 位于 frameworks/bridge/declarative_frontend/jsview/js_view.cpp
void JSViewPartialUpdate::JSBind(BindingTarget object)
{JSClass<JSViewPartialUpdate>::Declare("NativeViewPartialUpdate");JSClass<JSViewPartialUpdate>::StaticMethod("create", &JSViewPartialUpdate::Create, opt);JSClass<JSViewPartialUpdate>::Method("markNeedUpdate", &JSViewPartialUpdate::MarkNeedUpdate);
}void JSViewPartialUpdate::MarkNeedUpdate()
{needsUpdate_ = ViewPartialUpdateModel::GetInstance()->MarkNeedUpdate(viewNode_);
}
// 位于 frameworks/bridge/declarative_frontend/jsview/models/view_partial_update_model_impl.cpp
bool ViewPartialUpdateModelImpl::MarkNeedUpdate(const WeakPtr<AceType>& node)
{auto weakElement = AceType::DynamicCast<ComposedElement>(node);auto element = weakElement.Upgrade();if (element) {element->MarkDirty();}return true;
}

ComposedElementpipeline 记录组件信息的对象,weakElement.Upgrade()ComposedElement 放入 pipeline 中,最终通过图形渲染引擎(OpenGL ES、Skia)完成显示。详细代码参考frameworks/core/pipeline/base/composed_element.cpp

05

Android跨平台的实现

以上是一个hap包通过ArkUI完成渲染的大致过程,回到跨平台ArkUI-X,相同的ArkTS代码是如何运行在Android设备上的呢?

打开Android项目,看到assets下存放着ArkCompiler编译产物,和上一节中对.hap包解压后得到的文件一模一样。这是在编译环节中,编译脚本copy一份modules.abc字节码和resource到Android工程下,作为Android应用资源,打包时将以assets形式打入apk。

src/main/assets/arkui-x├── entry|   ├── ets|   |   ├── modules.abc|   |   └── sourceMaps.map|   ├── resouces.index|   ├── resouces|   └── module.json└── systemres

如何在Android上执行modules.abc字节码呢?打开libs,发现ArkUI相关的so动态库和jar包。

libs├── armabi-v7a|   ├── libarkui_android.so|   └── libhilog.so└── arkui_android_adapter.jar

其中libarkui_android.soarkui_ace_enginearkui_napifoundation/appframeworkarkui_for_android... 所编译出的动态库,是运行和界面渲染的必要环境。另一个arkui_android_adapter.jar的功能是:Android Application需要继承arkui_android_adapter.jar包所提供的StageApplicationStageApplication用于初始化资源路径以及加载配置信息。Activity需要继承arkui_android_adapter.jar包所提供的StageActivityStageActivity主要功能是将Android中Activity的生命周期与Harmony中Ability的生命周期进行映射。除此之外,arkui_android_adapter.jar适配了系统平台能力,如粘贴板、软键盘、字体、存储、日志。这里有个疑问:ArkUI-X是如何实现与Android系统之间的交互呢?ArkTSJava没有相互调用的能力,为了实现ArkTSJava交互,需要ArkTSC++交互,C++再与Java交互,调用链为ArkTS  -> NAPI -> C++ -> JNI -> Java,反之亦然,看起来十分复杂。ArkUI-X提供一套桥接能力,对于开发者来说,并不用关心这些封装逻辑,实际开发过程中,就像是ArkTSJava直接交互。

下面通过粘贴板的例子,探究它的具体实现过程。我们给系统粘贴板设置数据——'明天星期六',使用ArkTS实现如下,看看最终是如何调到Android Framework 给粘贴板设置数据的api:ClipboardManager#setPrimaryClip()

import pasteboard from '@ohos.pasteboard';Button('拷贝到粘贴板').onClick(() => {let pasteData: pasteboard.PasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, '明天星期六')let systemPasteboard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard()systemPasteboard.setData(pasteData)})

这段代码对接ArkUI的调用链和上一节中渲染UI类似,就不再赘述了。粘贴板相关api 在 arkui_ace_engine中定义成抽象接口,如下:

// 位于 arkui_ace_engine/frameworks/core/common/clipboard/clipboard.h
namespace OHOS::Ace {class Clipboard : public AceType {DECLARE_ACE_TYPE(Clipboard, AceType);public:~Clipboard() override = default;virtual void SetData(const std::string& data, CopyOptions copyOption = CopyOptions::InApp, bool isDragData = false) = 0;}
}

ArkUI-X 分平台对接口进行不同实现,Android的实现在arkui_for_android仓库中,其定义和实现如下,可见粘贴板设置数据api SetData()最终到ClipboardJni::SetData(data)

// 位于 arkui_for_android/capability/java/jni/clipboard/clipboard_impl.h
#include "core/common/clipboard/clipboard.h"namespace OHOS::Ace::Platform {class ClipboardImpl final : public Clipboard {public:explicit ClipboardImpl(const RefPtr<TaskExecutor>& taskExecutor) : Clipboard(taskExecutor) {}~ClipboardImpl() override = default;void SetData(const std::string& data, CopyOptions copyOption = CopyOptions::InApp, bool isDragData = false) override;}
}
// 位于 arkui_for_android/capability/java/jni/clipboard/clipboard_impl.cpp
#include "adapter/android/capability/java/jni/clipboard/clipboard_impl.h"
#include "adapter/android/capability/java/jni/clipboard/clipboard_jni.h"namespace OHOS::Ace::Platform {void ClipboardImpl::SetData(const std::string& data, CopyOptions copyOption, bool isDragData){// 对 SetData 实现,最终ClipboardJni::SetData(data)taskExecutor_->PostTask([data] { ClipboardJni::SetData(data); }, TaskExecutor::TaskType::PLATFORM, "ArkUI-XClipboardImplSetData");}
}

再通过JNI实现C++Java交互:

// 位于 arkui_for_android/capability/java/jni/clipboard/clipboard_jni.cpp
static const char CLIPBOARD_PLUGIN_CLASS_NAME[] = "ohos/ace/adapter/capability/clipboard/ClipboardPluginBase";
static const JNINativeMethod METHODS[] = {{ .name = "nativeInit", .signature = "()V", .fnPtr = reinterpret_cast<void*>(ClipboardJni::NativeInit) },
};
static const char METHOD_SET_DATA[] = "setData";
static const char SIGNATURE_SET_DATA[] = "(Ljava/lang/String;)V";// JNI_OnLoad
bool ClipboardJni::Register(std::shared_ptr<JNIEnv> env)
{// 动态注册 nativeInit 方法,java侧调用jclass clazz = env->FindClass(CLIPBOARD_PLUGIN_CLASS_NAME);bool ret = env->RegisterNatives(clazz, METHODS, ArraySize(METHODS)) == 0;return true;
}void ClipboardJni::NativeInit(JNIEnv* env, jobject object)
{jclass clazz = env->GetObjectClass(object);g_pluginMethods.setData = env->GetMethodID(clazz, METHOD_SET_DATA, SIGNATURE_SET_DATA);
}bool ClipboardJni::SetData(const std::string& data)
{auto env = JniEnvironment::GetInstance().GetJniEnv();jstring jData = env->NewStringUTF(data.c_str());// 反射调用 ClipboardPluginAosp.java setData方法env->CallVoidMethod(g_clipboardObj.get(), g_pluginMethods.setData, jData);if (jData != nullptr) {env->DeleteLocalRef(jData);}return true;
}

最终通过ClipboardManager#setData() ,将ArkTS中设置的内容,给到Android的系统粘贴板。

// 位于 arkui_for_android 仓库打出的 arkui_android_adapter.jar 包中
public class ClipboardPluginAosp extends ClipboardPluginBase {private final ClipboardManager clipManager;public ClipboardPluginAosp(Context context) {this.clipManager = (ClipboardManager context.getSystemService(Context.CLIPBOARD_SERVICE);nativeInit();}@Overridepublic void setData(String data) {if (clipManager != null) {ClipData clipData = ClipData.newPlainText(null, data);clipManager.setPrimaryClip(clipData);}}
}

iOS平台的实现和Android平台类似,原理都是相通的。

06

小结

通过这一章,我们学到了ArkUI-X的环境搭建、项目创建和打包流程,探索了ArkTS编写的项目,编译后字节码文件如何与ArkUI对接,了解了ArkUI-X在Android平台上的实现方案,以及ArkUI-X如何适配系统平台能力。ArkUI-X 属于后来者,设计之初应该借鉴过其它跨平台方案,汲取了优秀设计,才形成目前的形态。今后在跨平台的实现上,我们又多了一种选择。

参考
  • ArkUI-X仓库地址:https://gitee.com/arkui-x

  • ArkUI-arkui_ace_engine仓库地址:https://gitee.com/openharmony/arkui_ace_engine

  • 深入理解arkui_ace_engine:https://juejin.cn/post/7305235970286485515

相关文章:

鸿蒙跨平台框架ArkUI-X

01 引言 目前&#xff0c;移动端主流跨平台方案有Flutter、React Native、uni-app等等&#xff0c;还有刚推出不久的Compose-Multiplatform&#xff0c;真所谓是百花齐放。这些框架各有特点&#xff0c;技术实现各有差异&#xff0c;比如Flutter通过Dart编写的UI描述对接Flutte…...

群晖DS223 Docker搭建为知笔记

群晖DS223 Docker搭建为知笔记&#xff0c;打造你的专属知识宝库 一、引言 在数字化信息爆炸的时代&#xff0c;笔记软件成为了我们管理知识、记录灵感的得力助手。为知笔记&#xff0c;作为一款专注于工作笔记和团队协作的云笔记产品&#xff0c;以其丰富的功能和便捷的使用体…...

FPGA入门教程

引言 FPGA&#xff08;Field-Programmable Gate Array&#xff0c;现场可编程门阵列&#xff09;是一种灵活且强大的硬件设备&#xff0c;广泛应用于数字电路设计、信号处理、嵌入式系统等领域。与传统的ASIC&#xff08;专用集成电路&#xff09;不同&#xff0c;FPGA允许用户…...

DR和BDR的选举规则

在 OSPF&#xff08;开放最短路径优先&#xff09;协议中&#xff0c;DR&#xff08;Designated Router&#xff0c;指定路由器&#xff09; 和 BDR&#xff08;Backup Designated Router&#xff0c;备份指定路由器&#xff09; 的选举是为了在广播型网络&#xff08;如以太网…...

无需环境,直接用 Docker 来启动你的 Python 项目

大家好 我是洪峰 想象这样一种场景&#xff0c;你写好了代码&#xff0c;准备部署在新的服务器上&#xff0c;这台服务器只有 Python2 和 Python3.6&#xff0c;没有你代码适配好的 Python3.12&#xff0c;那怎么办&#xff1f; 1、编译安装 Python&#xff0c;我不推荐这种方…...

STM32之BKP

VBAT备用电源。接的时候和主电源共地&#xff0c;正极接在一起&#xff0c;中间连接一个100nf的电容。BKP是RAM存储器。 四组VDD都要接到3.3V的电源上&#xff0c;要使用备用电池&#xff0c;就把电池正极接到VBAT&#xff0c;负极跟主电源共地。 TEMPER引脚先加一个默认的上拉…...

【08】单片机编程核心技巧:变量命名规范

【08】单片机编程核心技巧&#xff1a;变量命名规范 &#xff08;基于单片机开发实践&#xff0c;适用于 C/C 语言&#xff09; &#x1f4cc; 核心原则 1️⃣ 清晰性&#xff1a;通过前缀、后缀、大小写区分变量类型、作用域、数据宽度等。 2️⃣ 一致性&#xff1a;同一项…...

JVM、MySQL常见面试题(尽力局)

JVM篇 一.谈一谈JDK、JRE、JVM分别是什么&#xff0c;有什么联系&#xff1f; 1.JDK是Java工具包&#xff0c;里面包含了JRE、Javac编译器等。 2.JRE是java运行环境&#xff0c;里面包含了JVM、JavaSE标准库类等。 3.JVM是Java虚拟机&#xff0c;运行编译后的.class的文件&am…...

Pytorch 转向TFConv过程中的卷积转换

转换知识基础 图像中使用的卷积一般为&#xff0c;正方形卷积核针对一个同等面积邻域的&#xff0c;进行相乘后邻域叠加到中心&#xff0c;相当于考虑中心像素的周围信息&#xff0c;做了一定的信息融合。 卷积相关参数 卷积前: input c1 卷积中: kernel 卷积核 stride 步…...

基于LabVIEW的伺服阀高频振动测试闭环控制系统

为实现伺服阀在设定位置上下快速移动&#xff08;1kHz控制频率&#xff09;的振动测试目标&#xff0c;需构建基于LabVIEW的闭环控制系统。系统需满足高速数据采集、实时控制算法&#xff08;如PID或自适应控制&#xff09;、高精度电流驱动及传感器反馈处理等需求。结合用户提…...

QQuick3D-Camera的介绍

QQuick3D-Camera的介绍 Camera的概述 Camera类继承于 Node&#xff1b;Camera定义了怎样将一个3D场景&#xff08;Scene&#xff09;投影到2D的表面上&#xff1b;一个场景至少需要一个Camera来可视化其内容。 Camera 可以像场景中任何节点一样&#xff0c;被定位和旋转&…...

django下防御race condition漏洞(竞争型漏洞)

目录 竞争型漏洞 概念 常见类型及示例 环境搭建 ​编辑漏洞复现 ucenter/1/ ucenter/2/ ucenter/3/ ucenter/4/ 总结 悲观锁 乐观锁 竞争型漏洞 概念 竞争型漏洞&#xff0c;也称为竞态条件漏洞&#xff08;Race Condition Vulnerability&#xff09;&#xff0c;…...

【测试框架篇】单元测试框架pytest(4):assert断言详解

一、前言 用例三要素之一就是对预期结果的断言。 何为断言&#xff1f;简单来说就是实际结果和期望结果去对比&#xff0c;符合预期就测试pass&#xff0c;不符合预期那就测试 failed。断言内容就是你要的预期结果。断言包含对接口响应内容做断言、也包含对落DB的数据做断言。…...

多视图几何--结构恢复--三角测量

三角测量 1. 核心公式推导 假设两个相机的投影矩阵为 P P P 和 P ′ P P′&#xff0c;对应的匹配图像点(同名点)为 ( u , v ) (u, v) (u,v) 和 ( u ′ , v ′ ) (u, v) (u′,v′)&#xff0c;目标是求解三维点 X [ X x , X y , X z , 1 ] T X [X_x, X_y, X_z, 1]^T X…...

【大模型】WPS 接入 DeepSeek-R1详解,打造全能AI办公助手

目录 一、前言 二、WPS接入AI工具优势​​​​​​​ 三、WPS接入AI工具两种方式 3.1 手动配置的方式 3.2 Office AI助手 四、WPS手动配置方式接入AI大模型 4.1 安装VBA插件 4.1.1 下载VBA插件并安装 4.2 配置WPS 4.3 WPS集成VB 4.4 AI助手效果测试 4.5 配置模板文…...

⭐算法OJ⭐N-皇后问题 II【回溯剪枝】(C++实现)N-Queens II

⭐算法OJ⭐N-皇后问题【回溯剪枝】&#xff08;C实现&#xff09;N-Queens 问题描述 The n-queens puzzle is the problem of placing n n n queens on an n n n \times n nn chessboard such that no two queens attack each other. Given an integer n, return the num…...

解锁 AI 量化新境界:Qbot 携手 iTick

在量化投资的汹涌浪潮中&#xff0c;你是否渴望拥有一个强大且便捷的工具&#xff0c;助你乘风破浪&#xff0c;驶向财富的彼岸&#xff1f;如今&#xff0c;Qbot 与 iTick 强强联合&#xff0c;为广大投资者和开发者打造出一个前所未有的 AI 量化生态系统。 Qbot&#xff1a;量…...

vue2设置横向滚动指令

图片横向滑动展示效果 创建directives.js文件 // 横向列表拖拽 const draggleScrollX {inserted(el, binding) {let isDragging false;let startX 0;let scrollLeft 0;el.classList.add("draggle-horizontal");// 添加监听事件-鼠标按下const onMouseDown (eve…...

Git和GitHub基础教学

文章目录 1. 前言2. 历史3. 下载安装Git3.1 下载Git3.2 安装Git3.3 验证安装是否成功 4. 配置Git5. Git基础使用5.1 通过Git Bash使用5.1.1 创建一个新的仓库。5.1.1.1 克隆别人的仓库5.1.1.2 自己创建一个本地仓库 5.1.2 管理存档 5.2 通过Visual Studio Code使用 6. Git完成远…...

【Linux docker】关于docker启动出错的解决方法。

无论遇到什么docker启动不了的问题 就是 查看docker状态sytemctl status docker查看docker日志sudo journalctl -u docker.service查看docker三个配置文件&#xff08;可能是配置的时候格式错误&#xff09;&#xff1a;/etc/docker/daemon.json&#xff08;如果存在&#xf…...

程序化广告行业(2/89):从程序化广告深挖数据处理技巧

程序化广告行业&#xff08;2/89&#xff09;&#xff1a;从程序化广告深挖数据处理技巧 大家好&#xff01;我一直希望能和大家在技术学习的道路上携手共进&#xff0c;这也是我写这一系列博客的初衷。上次我们一起学习了Python基础的数据处理知识&#xff0c;这次咱们借助一…...

第七课:Python反爬攻防战:Headers/IP代理与验证码

在爬虫开发过程中&#xff0c;反爬虫机制成为了我们必须面对的挑战。本文将深入探讨Python爬虫中常见的反爬机制&#xff0c;并详细解析如何通过随机User-Agent生成、代理IP池搭建以及验证码识别来应对这些反爬策略。文章将包含完整的示例代码&#xff0c;帮助读者更好地理解和…...

时序数据库TimescaleDB基本操作示例

好的&#xff01;以下是使用 TimescaleDB 的 Java 示例&#xff08;基于 JDBC&#xff0c;因为 TimescaleDB 是 PostgreSQL 的扩展&#xff0c;官方未提供独立的 Java SDK&#xff09;&#xff1a; 1. 添加依赖&#xff08;Maven&#xff09; <dependency><groupId&g…...

【CSS 】Class Variance Authority CSS 类名管理工具库

1.背景、什么是 CVA&#xff1f; Class Variance Authority (CVA) 是一个用于管理 CSS 类名 的工具库&#xff0c;特别适合在 React 或 Vue 等前端框架中使用。它可以帮助你更轻松地处理组件的 样式变体&#xff08;Variants&#xff09;&#xff0c;比如按钮的不同状态&#…...

【Linux】36.简单的TCP网络程序

文章目录 1. TCP socket API 详解1.1 socket():打开一个网络通讯端口1.2 bind():绑定一个固定的网络地址和端口号1.3 listen():声明sockfd处于监听状态1.4 accept():接受连接1.5 connect():连接服务器 2. 实现一个TCP网络服务器2.1 Log.hpp - "多级日志系统"2.2 Daem…...

Win 转 MacBook Pro 踩坑指南

前言 Window 和 macOS 系统的差异还是很大的&#xff0c;我从 Thinkpad 转用 M1 的 Macbook pro 已经一年了&#xff0c;几乎没有任何不适应&#xff0c;整体感受那是真的牛&#x1f443;&#xff0c;速度和续航惊艳到我了&#xff0c;同时开启 6个 vscode 加几十个浏览器标签…...

【模拟CMOS集成电路设计】带隙基准(Bandgap)设计与仿真(基于运放的电流模BGR)

【模拟CMOS集成电路设计】带隙基准&#xff08;Bandgap&#xff09;设计与仿真 前言工程文件&部分参数计算过程&#xff0c;私聊~ 一、 设计指标指标分析&#xff1a; 二、 电路分析三、 仿真3.1仿真电路图3.2仿真结果(1)运放增益(2)基准温度系数仿真(3)瞬态启动仿真(4)静态…...

手写一个Tomcat

Tomcat 是一个广泛使用的开源 Java Servlet 容器&#xff0c;用于运行 Java Web 应用程序。虽然 Tomcat 本身功能强大且复杂&#xff0c;但通过手写一个简易版的 Tomcat&#xff0c;我们可以更好地理解其核心工作原理。本文将带你一步步实现一个简易版的 Tomcat&#xff0c;并深…...

QT显示网页控件QAxWidget、QWebEngineView及区别

一.QT种显示网页控件QAxWidget 1.介绍 QAxWidget 属于 QtAxContainer 模块&#xff0c;ActiveX 是微软提出的一种组件对象模型&#xff08;COM&#xff09;技术&#xff0c;允许不同的软件组件在 Windows 操作系统上进行交互和集成。QAxWidget 为开发者提供了在 Qt 应用程序中…...

【AI智能体报告】开源AI助手的革命:OpenManus深度使用报告

一、引言&#xff1a;当开源智能体走进生活 2025年3月&#xff0c;MetaGPT团队用一场"开源闪电战"改写了AI Agent的竞争格局。面对商业产品Manus高达10万元的邀请码炒作&#xff0c;他们仅用3小时便推出开源替代品OpenManus&#xff0c;首日即登顶GitHub趋势榜。 …...

VS Code连接服务器教程

VS Code是什么 VS Code&#xff08;全称 Visual Studio Code&#xff09;是一款由微软推出的免费、开源、跨平台的代码编辑神器。VS Code 支持 所有主流操作系统&#xff0c;拥有强大的功能和灵活的扩展性。 官网&#xff1a;https://code.visualstudio.com/插件市场&#xff1…...

装饰器模式的C++实现示例

核心思想 装饰器设计模式是一种结构型设计模式&#xff0c;它允许动态地为对象添加额外的行为或职责&#xff0c;而无需修改其原始类。装饰器模式通过创建一个装饰器类来包装原始对象&#xff0c;并在保持原始对象接口一致性的前提下&#xff0c;扩展其功能。 装饰器模式的核…...

C 语言数据结构(二):顺序表和链表

目录 1. 线性表 2. 顺序表 2.1 概念及结构 2.1.1 静态顺序表&#xff08;不常用&#xff09; 2.1.2 动态顺序表&#xff08;常用&#xff09; ​编辑 2.2 练习 2.2.1 移除元素 2.2.2 删除有序数组中的重复项 2.2.3 合并两个有序数组 2.3 顺序表存在的问题 3. 链表 …...

TDengine 服务无法启动常见原因

taosd 是 TDengine 的核心服务进程&#xff0c;如果无法启动将导致整个数据库无法使用&#xff0c;了解常导致无法启动的原因&#xff0c;可以帮你快速解决问题。 1. 如何查找日志 无法启动的原因记录在日志中&#xff0c;日志文件默认在 /var/log/taos 的 taosdlog.0 或者 t…...

在 UniApp 中实现stream流式输出 AI 聊天功能,AI输出内容用Markdown格式展示

在 UniApp 中实现流式 AI 聊天功能 介绍 在现代 Web 开发中&#xff0c;流式 API 响应能够显著提升用户体验&#xff0c;尤其是在与 AI 聊天接口进行交互时。本文将介绍如何在 UniApp 中使用 Fetch API 实现一个流式响应的 AI 聊天功能&#xff0c;包括实时更新聊天内容和滚动…...

数据库SQL的配置和练习题

一、MySQL的安装 1.安装包下载 下载地址&#xff1a;https://downloads.mysql.com/archives/community/ 2.解压软件包 将MySQL软件包解压在没有中文和空格的目录下 3.设置配置文件 在解压目录下创建my.ini文件并添加内容如下&#xff1a; ​ [client] #客户端设置&…...

Pytorch的一小步,昇腾芯片的一大步

Pytorch的一小步&#xff0c;昇腾芯片的一大步 相信在AI圈的人多多少少都看到了最近的信息&#xff1a;PyTorch最新2.1版本宣布支持华为昇腾芯片&#xff01; 1、 发生了什么事儿&#xff1f; 在2023年10月4日PyTorch 2.1版本的发布博客上&#xff0c;PyTorch介绍的beta版本…...

AI+办公 Task1

作业 题目1&#xff1a;提示词除了三要素“角色”、“背景”、“要求”之外&#xff0c;还有哪些关键要素 提示词有一个框架叫CO-STAR框架&#xff0c;还有的关键要素有风格、任务、响应格式等。 要素适用场景实际案例​Context需要限定领域或场景的任务"作为医学助手&…...

文件系统调用─── linux第17课

目录 linux 中man 2和man 3的区别 文件内容介绍 C语言文件接口 示例: 输出信息到显示器&#xff0c;你有哪些方法 总结: 系统文件I/O 文件类的系统调用接口介绍 示例 open 函数具体使用哪个,和具体应用场景相关&#xff0c; write read close lseek ,类比C文件相关接…...

概念|RabbitMQ 消息生命周期 待消费的消息和待应答的消息有什么区别

目录 消息生命周期 一、消息创建与发布阶段 二、消息路由与存储阶段 三、消息存活与过期阶段 四、消息投递与消费阶段 五、消息生命周期终止 关键配置建议 待消费的消息和待应答的消息 一、待消费的消息&#xff08;Unconsumed Messages&#xff09; 二、待应答的消息…...

Javaweb后端文件上传@value注解

文件本地存储磁盘 阿里云oss准备工作 阿里云oss入门程序 要重启一下idea&#xff0c;上面有cmd 阿里云oss案例集成 优化 用spring中的value注解...

DeepSeek技术演进与发展前瞻

如果喜欢可以订阅专栏哟(^U^)ノ~YO,至少更新6年 以下DeepSeek未来发展的技术分析框架及核心内容示范 # -*- coding: utf-8 -*- """ DeepSeek技术演进模拟器(概念验证代码) 本代码展示动态架构调整的核心逻辑 """class DynamicArchitect…...

Java常见面试技术点整理讲解——后端框架(整理中,未完成)

前言&#xff1a; 对于后端常用框架的技术整理&#xff0c;其实框架在平时就是会用就行&#xff0c;但面试时多半需要描述实现原理&#xff0c;这个要靠自己理解&#xff0c;不推荐死记硬背。 这篇和另外几篇文章区分开&#xff0c;主要用于规整Java后端各种框架&#xff0c;…...

目标检测YOLO实战应用案例100讲-基于毫米波雷达的多目标检测 (续)

目录 3.2 改进的CFAR目标检测算法 3.3 算法步骤描述 3.4 实验结果与分析 基于VGG16-Net的毫米波雷达目标检测算法 4.1 VGG16-Net网络模型 4.2 改进VGG16-Net网络的目标检测算法 4.3 算法步骤描述 4.4 实验结果与分析 知识拓展 基于毫米波雷达的多目标检测:使…...

python爬虫:Android自动化工具Auto.js的详细使用

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 1. Auto.js 简介2. 安装与配置2.1 安装 Auto.js2.2 安装 Python 环境2.3 安装 ADB 工具3. Python 与 Auto.js 结合3.1 通过 ADB 执行 Auto.js 脚本3.2 通过 Python 控制 Auto.js3.3 通过 Python 与 Auto.js 交互4. 常用…...

MyBatis-Plus 注解大全

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 MyBatis-Plus 注解大全 MyBatis-Plus 是基于 MyBatis 的增强工具&#xff0c;通过注解简化了单表 CRUD 操作和复杂查询的配置。以下是常用注解的分类及详细说…...

牛客周赛 Round 84——小红的陡峭值(四)

牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ 小红的陡峭值&#xff08;四&#xff09; 题目&#xff1a; 思路&#xff1a; 题目告诉我们关于树的陡峭值的定义&#xff0c;那一开始看起来无从下手&#xff0c;但是当我们选取某一个节点为根节点时&#…...

Redis 内存淘汰策略深度解析

Redis 作为高性能的内存数据库&#xff0c;其内存资源的高效管理直接关系到系统的稳定性和性能。当 Redis 的内存使用达到配置的最大值&#xff08;maxmemory&#xff09;时&#xff0c;新的写入操作将触发内存淘汰机制&#xff08;Eviction Policy&#xff09;&#xff0c;以释…...

微前端之 Garfish.js 的基础使用教程和进阶配置

前言 在现代前端开发中&#xff0c;微前端架构逐渐成为一种流行的解决方案。它允许将大型应用拆分成多个小型独立的子应用&#xff0c;从而提高开发效率和可维护性。Garfish.js 是一个强大的微前端框架&#xff0c;可以帮助我们轻松实现这一架构。在本文中&#xff0c;通过一个…...

Rabbitmq--延迟消息

13.延迟消息 延迟消息&#xff1a;生产者发送消息时指定一个时间&#xff0c;消费者不会立刻收到消息&#xff0c;而是在指定时间之后才会收到消息 延迟任务&#xff1a;一定时间之后才会执行的任务 1.死信交换机 当一个队列中的某条消息满足下列情况之一时&#xff0c;就会…...