jenkins pipeline打包流程

news/2024/12/15 15:09:49/

Jenkins Pipeline 是 Jenkins 提供的一种用于持续集成和持续交付(CI/CD)的脚本化流程工具。它允许你通过编写一个 Jenkinsfile 文件来定义整个构建、测试和部署的流程。本文介绍打包springcloud项目,react项目为docker镜像

文章目录

    • 1.项目结构
    • 2.项目打包改造
      • 2.1. lhm-emp 模块pom.xml
      • 2.2. lhm-emp 模块Dockerfile
      • 2.3. lhm-web模块Dockerfile
    • 3. Jenkins docker pipeline编写
      • 3.1.GIT_CREDENTIALS_ID: git 认证参数
        • your-credentials-id
      • 3.2.pipeline编写
      • 3.3.执行打包任务
      • 3.4. 导入镜像
    • 4. 打包不同架构的基础image
      • 4.1. java
      • 4.2. nginx
    • 5. 注意
      • 5.1. 拉取http仓库 connect: connection refused"
      • 5.2. Jenkinsfile Pipeline 打包镜像缓慢解决办法
      • 5.3. unauthorized to access repository
    • 6. 打包增量

1.项目结构

├─Jenkinsfile    jenkinsfile打包脚本文件             
├─lhm-emp        微服务模块 emp服务
├─lhm-eureka     微服务模块 eureka服务
├─lhm-gateway    微服务模块 gateway服务
├─lhm-order      微服务模块 order服务
└─lhm-web        前端服务

项目地址 https://gitee.com/liuhaomin/springcloud

测试请求

## eureka
http://localhost:8100
## order
http://localhost:9000/api/order/info
## emp
http://localhost:9000/api/emp/info
## web
http://localhost:3000

2.项目打包改造

根pom.xml

<build><finalName>${project.artifactId}</finalName><pluginManagement><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version></plugin><plugin><groupId>com.spotify</groupId><artifactId>dockerfile-maven-plugin</artifactId><version>${docker.plugin.version}</version><configuration><username>${docker.username}</username><password>${docker.password}</password><repository>${docker.registry.url}/${docker.namespace}/${project.artifactId}</repository><!--<tag>${os_tag}-${project.version}</tag>--><tag>${os_tag}${project.version}</tag><useMavenSettingsForAuth>true</useMavenSettingsForAuth><buildArgs><JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE></buildArgs><skip>${dockerfile.skip}</skip></configuration></plugin></plugins></pluginManagement></build>

2.1. lhm-emp 模块pom.xml

lhm-gateway,lhm-eureka,lhm-order 类似

  <build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><mainClass>${emp.main.class}</mainClass><layout>ZIP</layout></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin><plugin><groupId>com.spotify</groupId><artifactId>dockerfile-maven-plugin</artifactId><configuration><username>${docker.username}</username><password>${docker.password}</password><repository>${docker.registry.url}/${docker.namespace}/${project.artifactId}</repository><tag>${os_tag}${lhm.project.version}</tag><useMavenSettingsForAuth>true</useMavenSettingsForAuth><buildArgs><OS_ARCH>${os_arch}</OS_ARCH><CACHEFROM>${last_version}</CACHEFROM><TARGETPLATFORM>${os_arch.dockerimage}</TARGETPLATFORM><JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE><EXPOSE_PORT>${emp.port}</EXPOSE_PORT><MAINCLASS>${emp.main.class}</MAINCLASS></buildArgs><skip>${dockerfile.skip}</skip></configuration><executions><execution><id>default</id><phase>package</phase><goals><goal>build</goal><goal>push</goal></goals></execution></executions></plugin></plugins></build>

2.2. lhm-emp 模块Dockerfile

lhm-gateway,lhm-eureka,lhm-order 类似

ARG TARGETPLATFORM
ARG OS_ARCH# 指定基础镜像,这是分阶段构建的前期阶段
FROM 192.168.56.10/lhm/openjdk:8-jdk-alpine as builder
# 执行工作目录
WORKDIR target
# 配置参数
ARG JAR_FILE=target/*.jar
# 将编译构建得到的jar文件复制到镜像空间中
COPY ${JAR_FILE} application.jar
RUN unzip application.jarFROM --platform=$OS_ARCH $TARGETPLATFORMARG EXPOSE_PORT
ARG JAR_FILE
ARG MAINCLASSARG DEPENDENCY=target
COPY --from=builder ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=builder ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=builder ${DEPENDENCY}/BOOT-INF/classes /appEXPOSE ${EXPOSE_PORT}ARG CACHEFROM
LABEL description=" cache from ${CACHEFROM}"ENV JAVA_OPTS ""
ENV AGENT_AFTER_JAR ""
ENV MAINCLASS ${MAINCLASS}CMD java ${JAVA_OPTS} -Dspring.profiles.active=docker -Dservice_name=${MAINCLASS} -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/opt/logs/jvm/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/logs/jvm/dump.hprof -Djava.security.egd=file:/dev/./urandom -Denv=dev -Duser.timezone=GMT+08 ${AGENT_AFTER_JAR}  -cp app:app/lib2/*:app/lib/*  ${MAINCLASS}

2.3. lhm-web模块Dockerfile

ARG TARGETPLATFORM
ARG OS_ARCHFROM --platform=$OS_ARCH $TARGETPLATFORMENV HTML_PATH=/usr/share/nginx/htmlWORKDIR $HTML_PATHCOPY ./build $HTML_PATHEXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

3. Jenkins docker pipeline编写

3.1.GIT_CREDENTIALS_ID: git 认证参数

your-credentials-id

在这里插入图片描述

在这里插入图片描述

3.2.pipeline编写

pipeline {agent anyoptions {// 最长保留90天,最多保留3个构建buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '90', numToKeepStr: '3')}environment{GIT_CREDENTIALS_ID="fd76875f-1862-43d4-adf4-6462e3b3c7f4"SERVER_GIT_URL="https://gitee.com/liuhaomin/springcloud.git"WEB_GIT_URL="https://gitee.com/liuhaomin/springcloud.git"HARBOR_URL="192.168.56.10"HARBOR_USERNAME="admin"HARBOR_PASSWORD="Harbor12345"// 192.168.56.10/lhm/lhm-webHARBOR_NAMESPACE="lhm"JAVACOMPILEIMAGE="192.168.56.10/lhm/openjdk:8-jdk-alpine"JAVA_OS_ARCH_DOCKERIMAGE="192.168.56.10/lhm/adoptopenjdk/openjdk8-openj9:alpine-slim"NGINX_OS_ARCH_DOCKERIMAGE="192.168.56.10/lhm/nginx:1.26.1-alpine"OS_ARCH="linux/amd64"OS_TAG="x86"}parameters {gitParameter(name: 'SERVER_BRANCH_NAME',type: 'branch', branchFilter: "origin/(.*)",defaultValue: 'master',selectedValue: "DEFAULT",useRepository: '${env.SERVER_GIT_URL}',description: '后端服务选择分支版本')gitParameter(name: 'WEB_BRANCH_NAME',type: 'branch', branchFilter: "origin/(.*)",defaultValue: 'master',selectedValue: "DEFAULT",useRepository: '${env.WEB_GIT_URL}',description: 'WEB服务选择分支版本')string(defaultValue: '1.0.1', description: '版本号', name: 'VERSION', trim: true)booleanParam(defaultValue: false, description: '是否跳过构建', name: 'SKIP_BUILD')extendedChoice(description: '选择需要打包的服务模块(多选)', multiSelectDelimiter: ',', name: 'SERVICES', quoteValue: false, saveJSONParameterToFile: false,type: 'PT_CHECKBOX', value: 'lhm-eureka,lhm-gateway,lhm-emp,lhm-order,web-lhm-web',defaultValue: '',visibleItemCount: 5)}stages {stage('SERVER拉取代码') {when {expression {if (Boolean.valueOf("${SKIP_BUILD}")){echo "跳过SERVER拉取代码,直接打包镜像文件"return !Boolean.valueOf("${SKIP_BUILD}")}def services = "${SERVICES}"def index = services.indexOf('web-')if (index > 0) {services = services.substring(0, index - 1)}if( services.size() > 0 && index != 0) {echo "包含服务,SERVER拉取代码"return true}echo "没有包含SERVER 跳过SERVER拉取代码"return false}}steps {script {deleteDir()sh "ls"echo "SERVER检出代码..."def scmVars = checkout([$class:"GitSCM",branches:[[name:"${SERVER_BRANCH_NAME}"]],doGenerateSubmoduleConfigurations: false,gitTool: "Default",submoduleCfg: [],userRemoteConfigs:[[credentialsId:"${env.GIT_CREDENTIALS_ID}",url:"${env.SERVER_GIT_URL}"]]])sh "echo '${scmVars}'"sh "ls"}}}stage('SERVER构建打包') {agent {docker{image 'maven:3.8-adoptopenjdk-8-openj9'reuseNode trueargs  '-v /root/.docker/config.json:/root/.docker/config.json -v /home/jenkins/repository:/root/.m2 -v /var/run/docker.sock:/var/run/docker.sock'}}when {expression {if (Boolean.valueOf("${SKIP_BUILD}")){echo "跳过SERVER构建打包,直接打包镜像文件"return !Boolean.valueOf("${SKIP_BUILD}")}def services = "${SERVICES}"def index = services.indexOf('web-')if (index > 0) {services = services.substring(0, index - 1)}if( services.size() > 0 && index != 0) {echo "包含服务,SERVER构建打包"return true}echo "没有包含SERVER 跳过SERVER构建打包"return false}}steps {script {def moduleListStr = "${SERVICES}"echo "$moduleListStr"def index = moduleListStr.indexOf('web-')if (index > 0) {moduleListStr = moduleListStr.substring(0, index - 1)}def moduleList = moduleListStr.split(',')def count = moduleList.size()if (count <= 0) {echo "请选择构建模块..."sh 'exit 1'}def JAVACOMPILEIMAGE="${env.JAVACOMPILEIMAGE}"def JAVA_OS_ARCH_DOCKERIMAGE="${env.JAVA_OS_ARCH_DOCKERIMAGE}"def OS_ARCH="${env.OS_ARCH}"def OS_TAG="${env.OS_TAG}"if("${OS_TAG}"=="x86"){OS_TAG=""}def moduleStr = moduleList.join(",:")echo "打包构建Version: ${VERSION}"sh "mvn -T 1C -pl :${moduleStr} -am clean package -Dmaven.test.skip=true -Ddockerfile.skip=false -Dlhm.project.version=${VERSION} -Dos_arch=${OS_ARCH} -Dos_tag=${OS_TAG} -Dos_arch.dockerimage=${JAVA_OS_ARCH_DOCKERIMAGE} -Djava.compileimage=${JAVACOMPILEIMAGE}"for (int i = 0; i < count; ++i) {def serviceName = "${moduleList[i]}"sh "docker tag ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION} ${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION}"}}}}stage('WEB拉取代码') {when {expression {if (Boolean.valueOf("${SKIP_BUILD}")){echo "跳过WEB拉取代码,直接打包镜像文件"return !Boolean.valueOf("${SKIP_BUILD}")}def services = "${SERVICES}"def index = services.indexOf('web-lhm-web')if (index != -1 ) {echo "包含web-lhm-web 打包WEB镜像"return true}echo "没有包含web-lhm-web 跳过WEB拉取代码"return false}}steps {script {deleteDir()sh "ls"echo "WEB检出代码..."def scmVars = checkout([$class:"GitSCM",branches:[[name:"${WEB_BRANCH_NAME}"]],doGenerateSubmoduleConfigurations: false,gitTool: "Default",submoduleCfg: [],userRemoteConfigs:[[credentialsId:"${env.GIT_CREDENTIALS_ID}",url:"${env.WEB_GIT_URL}"]]])sh "echo '${scmVars}'"sh "ls"}}}stage('WEB构建打包') {agent {docker{image 'node:16-alpine'reuseNode trueargs  ''}}when {expression {if (Boolean.valueOf("${SKIP_BUILD}")){echo "跳过WEB构建打包,直接打包镜像文件"return !Boolean.valueOf("${SKIP_BUILD}")}def services = "${SERVICES}"def index = services.lastIndexOf('web-lhm-web')if (index != -1 ) {echo "包含web-lhm-web 打包WEB镜像"return true}echo "没有包含web-lhm-web 跳过WEB构建打包"return false}}steps {script {echo "依赖安装..."sh "cd lhm-web && yarn config set registry https://registry.npmmirror.com/ && yarn install --network-concurrency 4 && yarn build"echo "打包构建Version: ${VERSION}"def NGINX_OS_ARCH_DOCKERIMAGE="${env.NGINX_OS_ARCH_DOCKERIMAGE}"def OS_ARCH="${env.OS_ARCH}"def OS_TAG="${env.OS_TAG}"if("${OS_TAG}"=="x86"){OS_TAG=""}//                     try {
//                         // 默认先打包增量,不成功再直接打包
//                         sh "docker pull ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${LAST_VERSION}"
//                         echo "基于[${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${LAST_VERSION}]缓存构建"
//                         sh "cd lhm-web && docker build --cache-from ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${LAST_VERSION} --build-arg TARGETPLATFORM=${NGINX_OS_ARCH_DOCKERIMAGE} --build-arg OS_ARCH=${OS_ARCH} -t ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION_NO} . && docker images"
//                     } catch (err) {
//                         def error = "${e.toString()}"
//                         echo "不走缓存构建,没有找到[${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${LAST_VERSION}]镜像"
//                         if(error.contains("not found")){
// 					        sh "cd lhm-web && docker build --build-arg TARGETPLATFORM=${NGINX_OS_ARCH_DOCKERIMAGE} --build-arg OS_ARCH=${OS_ARCH} -t ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION} .&& docker images"
//                         } else {
//                             throw err
//                         }
//                     }sh "cd lhm-web && docker build --build-arg TARGETPLATFORM=\'${NGINX_OS_ARCH_DOCKERIMAGE}\' --build-arg OS_ARCH=${OS_ARCH} -t ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION} .&& docker images"sh "docker tag ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION} ${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION}"// 登录harbor docker 仓库中心echo "----<<<< 登录harbor docker 仓库中心 >>>>-----"sh "docker login ${env.HARBOR_URL}  -u ${env.HARBOR_USERNAME} -p ${env.HARBOR_PASSWORD}"// 推送镜像到harhor docker 仓库中心echo "----<<<< 推送镜像到harhor docker 仓库中心 >>>>-----"sh "docker push ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION}"}}}stage('镜像打包') {steps {script {def OS_TAG="${env.OS_TAG}"if("${OS_TAG}"=="x86"){OS_TAG=""}echo "${SERVICES}"def serviceListStr = "${SERVICES}"if (serviceListStr.isEmpty()) {echo "请选择构建模块..."sh 'exit 1'}def batchImages = []serviceListStr = serviceListStr.replace("web-","")def serviceList = serviceListStr.split(',')def count = serviceList.size()def fullFolderstr = "${WORKSPACE}/full/${VERSION}/"sh "mkdir -p ${fullFolderstr} && cd ${fullFolderstr}"echo "use version: ${VERSION}"echo "OS_TAG: ${OS_TAG}"sh "cd ${fullFolderstr} && touch images-load.sh && touch images-list.txt && echo '#!/bin/sh' > images-load.sh && echo 'echo \"==== starting to load images ====\"' >> images-load.sh && echo 'echo \"====  服务导入镜像 ====\"' >> images-load.sh"sh "cd ${fullFolderstr} && echo ' ' > images-list.txt"sh "docker login ${env.HARBOR_URL}  -u ${env.HARBOR_USERNAME} -p ${env.HARBOR_PASSWORD}"for (int i = 0; i < count; ++i) {def serviceName = "${serviceList[i]}"sh "docker pull ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION}"sh "docker tag ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION} ${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION}"batchImages.add("lhm/${serviceName}:${OS_TAG}${VERSION}")sh "cd ${fullFolderstr} && echo '- ${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION}' >> images-list.txt"}def batchImagesStr = batchImages.join(" ")def grepVersion = "${VERSION}".replaceAll(".", ".")sh "cd ${fullFolderstr} && echo 'docker load -i app.tar.gz' >> images-load.sh"sh "cd ${fullFolderstr} && echo 'echo \"==== 查看服务镜像 ====\"' >> images-load.sh && echo 'docker images | grep -E ${grepVersion}' >> images-load.sh && echo 'echo \"==== end to load images ====\"' >> images-load.sh"sh "docker images | grep -E ${grepVersion}"// 打包全量sh "docker save ${batchImagesStr}|gzip > ${fullFolderstr}/app.tar.gz"//sh "cd ${WORKSPACE}/full && ls && rm -rf ${OS_TAG}${VERSION}.zip && zip -r ${OS_TAG}${VERSION}.zip ${VERSION}/* && cp ${WORKSPACE}/full/${OS_TAG}${VERSION}.zip ${WORKSPACE}"sh "cd ${WORKSPACE}/full && ls && rm -rf ${OS_TAG}${VERSION}.zip && tar -czvf ${OS_TAG}${VERSION}.tar.gz ${VERSION}/* && cp ${WORKSPACE}/full/${OS_TAG}${VERSION}.tar.gz ${WORKSPACE}"// -f :显示时进行过滤  “dangling=true”: 表示过滤并显示悬挂状态的镜像,即没有被标签引用或者被其他层依赖的镜像  -q :只显示image id//sh "docker images -q -f dangling=true | xargs docker rmi -f"// 清理打包的镜像sh "docker images --format '{{.Repository}}:{{.Tag}}' | grep 'lhm/' | xargs docker rmi -f"archiveArtifacts artifacts: '*.tar.gz', onlyIfSuccessful: trueecho "==== starting to export images ===="echo "请下载Last Successful Artifacts构建成品!!!"}}}}
}

在这里插入图片描述

3.3.执行打包任务

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.4. 导入镜像

下载后执行

tar -xzvf 1.0.0.tar.gz
cd 1.0.0
bash images-load.sh

4. 打包不同架构的基础image

推荐一部分

4.1. java

# x86_64
adoptopenjdk/openjdk8-openj9:alpine-slim
# aarch64
arm64v8/adoptopenjdk:8-jre-openj9

4.2. nginx

# x86_64
nginx:1.26.1-alpine
# aarch64
nginx:1.26.1-alpine

5. 注意

5.1. 拉取http仓库 connect: connection refused"

[ERROR] Failed to execute goal com.spotify:dockerfile-maven-plugin:1.4.13:build (default) on project lhm-eureka: Could not pull cache-from image: Request error: POST unix://localhost:80/images/create?fromImage=192.168.56.10%2Flhm%2Flhm-eureka&tag=x86_641.0.0: 500, body: {"message":"Get https://192.168.56.10/v2/: dial tcp 192.168.56.10:443: connect: connection refused"} -> [Help 1]

在这里插入图片描述

解决办法:

设置docker的仓库地址

vi /etc/docker/daemon.json
新增 registry-mirrors:["http://192.168.56.10"],
"insecure-registries" : ["192.168.56.10:5000", "0.0.0.0/0"]

insecure-registries是Docker中的一个配置选项,涉及容器镜像的安全拉取。

  • 定义与用途‌:
    指允许Docker从不安全(即未使用HTTPS协议的)注册中心拉取镜像的列表。默认情况下,Docker为了安全考虑,只允许从使用HTTPS的注册中心拉取镜像。
  • 配置方式‌:
    在Docker配置文件(如/etc/docker/daemon.json)中,通过添加"insecure-registries"字段并指定一个或多个注册中心地址来配置。例如,"insecure-registries" : ["my-registry.local:5000"]允许从不安全的my-registry.local:5000拉取镜像。

5.2. Jenkinsfile Pipeline 打包镜像缓慢解决办法

  • 将所有使用的docker镜像推送至自己的私有仓库(防止拉取其他镜像缓慢)

DockerFile 文件

FROM openjdk:8-jdk-alpine as builder
# 执行工作目录
WORKDIR target

在这里插入图片描述

上述文件比下面的文件缓慢接近20多倍

FROM 192.168.56.10/lhm/openjdk:8-jdk-alpine
# 执行工作目录
WORKDIR target

在这里插入图片描述

5.3. unauthorized to access repository

[ERROR] Failed to execute goal com.spotify:dockerfile-maven-plugin:1.4.13:build (default) on project lhm-eureka: Could not build image: unauthorized: unauthorized to access repository: lhm/openjdk, action: pull: unauthorized to access repository: lhm/openjdk, action: pull -> [Help 1]

解决办法

docker login 192.168.56.10 admin Harbor12345
或者 
新增 -v /root/.docker/config.json:/root/.docker/config.json docker{image '192.168.56.10/lhm/maven:3.8-adoptopenjdk-8-openj9'reuseNode trueargs  '-v /root/.docker/config.json:/root/.docker/config.json -v /home/jenkins/repository:/root/.m2 -v /var/run/docker.sock:/var/run/docker.sock'
}

6. 打包增量

打包增量详细解释


http://www.ppmy.cn/news/1555325.html

相关文章

外卖开发(九)——Excel数据报表ApachePOI

外卖开发&#xff08;九&#xff09;——Excel数据报表 一、ApachePOI二、入门案例三、导出运营数据报表1、ReportController2、ReportService 一、ApachePOI Apache POl是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是&#xff0c;我们可以使用POI在Java程…

LabVIEW面向对象编程有什么特点?

LabVIEW面向对象编程&#xff08;OOP&#xff09;的特点主要体现在它如何结合传统面向对象编程&#xff08;OOP&#xff09;的理念与LabVIEW的图形化编程模式&#xff0c;提供灵活的抽象和模块化的功能。以下是LabVIEW面向对象编程的几个主要特点&#xff1a; ​ 1. 类&#x…

在ArcGISPro中创作精美地图

建议从数据下载到最后的出图都跟着走一下&#xff0c;提供了一个完整且全面的教程&#xff0c;建议从数据下载开始&#xff0c;这样可以对ArcGISPro制图流程有一个全面的感触和认知。 1. 绘制北极海冰地图 20 世纪&#xff0c;气候变化导致极地海冰迅速减少。 自 1978 年以来…

Jetpack Compose赋能:以速破局,高效打造非凡应用

Android Compose 是谷歌推出的一种现代化 UI 框架&#xff0c;基于 Kotlin 编程语言&#xff0c;旨在简化和加速 Android 应用开发。它以声明式编程为核心&#xff0c;与传统的 View 系统相比&#xff0c;Compose 提供了更直观、更简洁的开发体验。以下是对 Android Compose 的…

android studio kotlin 本地c++工程添加oboe库的方法

1.新建本地c++的kotlin工程hellohao 如图 2.把开源的oboe源文件src、include、CMakelists.txt、debug-utils复制hellohao目录下 3.修改hellohao\app目录下的CMakelists.txt cmake_minimum_required(VERSION 3.22.1) project(hellohao LANGUAGES C CXX)get_filename_component…

从YOLOv5到训练实战:易用性和扩展性的加强

文章目录 前言一、模型介绍二、YOLOv5网络结构1.Input&#xff08;输入端&#xff09;&#xff1a;智能预处理与优化策略2.Backbone&#xff08;骨干网络&#xff09;&#xff1a;高效特征提取3.NECK&#xff08;颈部&#xff09;&#xff1a;特征增强与多尺度融合4.Prediction…

latex设置引用顺序

在 LaTeX 中&#xff0c;引用的顺序通常是由所选择的 参考文献样式&#xff08;bibliographystyle&#xff09; 决定的。如果你希望根据引用的顺序排列参考文献&#xff0c;可以选择合适的参考文献样式&#xff0c;并按照以下步骤进行设置。 常见的几种引用顺序设置方式有&…

3D 生成重建033-对3D-head进行风格化编辑

3D 生成重建033-对3D-head进行风格化编辑 文章目录 0 论文工作1 论文方法2 实验结果 0 论文工作 今天想分享的是跟我们之前在做的一个任务比较像的工作&#xff0c;对avatar生成不同风格的外观。 三维头部风格化将逼真的面部特征转换成艺术化的表达形式&#xff0c;从而提升了…