Android Gradle开发、应用、插件发布(六)—实现打包自动复制文件插件

1. 前言

项目中遇到了一个问题 :

其中一个模块MyLibrary的assets文件夹中,需要存放很多文件(每个文件对应一个功能)。

这样导致的问题是MyLibrary打出的这个aar包体积特别大。

如果把MyLibrary严谨地拆解成若干个Module又比较费时,对于现在业务现状来说也显得没那么必要。

那么能不能在上传MyLibrary这个aar的时候,自动复制相应的文件到assets目录下,打出不同功能的aar呢 ?

这就需要自己开发一个Gradle插件来完成这个功能了。

本文环境

  • Android Studio 版本 : Android Studio Hedgehog | 2023.1.1
  • Gradle版本 : gradle-8.2
  • AGP版本 : 8.2.0
  • 项目结构 : 项目有app模块和MyLibrary模块,使用build.gradle (Groovy语言),app的assets目录下,有test1.so、test2.so、test3.so这三个文件

2. 配置上传Maven仓库

首先我们把MyLibrary配置上传Maven的插件,也就是maven-publish。

对于这部分功能不了解的同学可以先看我的这篇博客 : Android Module上传到Maven仓库 及 实现同时上传到多个Maven仓库

下面我们简单讲述一下

复制maven_upload.gradle到项目根目录下

apply plugin: 'maven-publish'

//TODO 这里填你Maven仓库的地址
def RELEASE_REPOSITORY_URL = "https://devops-maven.xxxxx.com/repository/yyyyy/"
//TODO 这里填你Maven仓库的账号
def NEXUS_USERNAME = "*********"
//TODO 这里填你Maven仓库的密码
def NEXUS_PASSWORD = "*********"

afterEvaluate {
   
    publishing {
   
        repositories {
   
            maven {
   
                name("ReleaseMaven")
                url = RELEASE_REPOSITORY_URL
                credentials {
   
                    username = NEXUS_USERNAME
                    password = NEXUS_PASSWORD
                }
            }
        }
        publications {
   
            Production(MavenPublication) {
   
                from components.release
                groupId = rootProject.ext.GROUP
                artifactId = rootProject.ext.POM_ARTIFACT_ID
                version = rootProject.ext.VERSION_NAME
            }
        }
    }
}

在MyLibrary中依赖

rootProject.ext.GROUP = "com.heiko.mytest"
rootProject.ext.POM_ARTIFACT_ID = "mylibrary"
rootProject.ext.VERSION_NAME = "1.0.0"
apply from: "${
     project.rootDir}/maven_upload.gradle"

Sync一下,可以看到gradle中多了publishing这个文件夹,里面的publishProductionPublicationToReleaseMavenRepository就是用来将MyLibrary打包并上传到Maven仓库的Gradle命令了。

3. Gradle相关操作

3.1 复制文件

from是原目录,into是目标目录,include可以指定需要复制的文件,onlyIf可以用来判断是否执行复制任务。

task copyFiles(type: Copy) {
   
    from 'src/main/assets'
    into 'build/outputs/assets'
    //include 'test1.txt','test2.txt' //指定文件名
    include '**/*.txt' //根据*匹配符合要求的文件
    onlyIf {
   
         true
    }
}
3.2 删除文件
task myDeleteFilesInDir(type: Delete) {
   
	//delete 'src/main/assets/test1.txt'  //删除test1.txt
	//delete 'src/main/assets/test1.txt' //删除文件夹下所有的文件,assets文件也会被删除
    delete fileTree('src/main/assets') //删除文件夹下所有的文件,assets文件不会被删除
}

3.3 dependsOn

dependsOn表示一个任务需要另一个任务先完成,可以理解为依赖于或需要先做,这意味着在执行这个任务之前,它所依赖的任务必须首先执行。

例如,如果你有一个任务叫做compile,它需要在clean任务之后执行,就可以像这样声明依赖关系:

task clearTask {
   
    doLast {
   
        println("执行 clear.doLast")
    }
}

task compileTask {
   
    doLast {
   
        println("执行 compile.dolast")
    }
}

compileTask.dependsOn(clearTask)

这样,每当你运行compile任务时,Gradle会首先运行clean任务。


执行compile任务的日志如下

> Task :app:clearTask
执行 clear.doLast

> Task :app:compileTask
执行 compile.dolast

BUILD SUCCESSFUL in 510ms
 

更通俗的理解 :
dependsOn就像是做饭的顺序:你首先需要准备食材,然后才能开始烹饪。同样地,如果你有一个任务依赖于另一个任务,那么你需要在开始当前任务之前先完成那个依赖任务。如果没有这种依赖关系,那么任务可能会在错误的时机执行,导致结果不正确或者出现错误。

//准备食材任务
task prepareFood(){
   }

//做饭任务
task cooking(){
   }

//做饭任务 依赖于 准备食材任务
cooking.dependsOn(prepareFood)
3.4 finalizedBy

finalizedBy用于指定一个任务另一个任务完成之后执行,可以理解为在完成后执行

Gradle中,finalizedBy用于指定一个或多个任务,这些任务将在关联任务执行完毕后执行,无论关联任务是否成功。可以把这个理解为一种清理或收尾的工作。

举个例子,我们有一个任务A,它被finalizedBy任务B,那就意味着在任务A执行完之后,无论任务A是否成功,任务B都会被执行。

这就好比一个厨师在做完一道菜(任务A)之后,无论这道菜是否做得成功,他都需要清理厨房(任务B),那么清理厨房这个步骤就是做菜这个任务的finalizedBy

3.5 mustRunAfter和shouldRunAfter

Gradle中,mustRunAfter 是用来定义任务执行的顺序的。如果你有两个任务,比如说任务A任务B,你希望无论何时,只要这两个任务都被执行任务A都必须在任务B之后执行,那么你就可以在任务A上调用 mustRunAfter 方法并传入任务B

举个例子,如下代码:

taskA.mustRunAfter taskB

这段代码的意思就是,如果这两个任务都在执行队列中,那么无论何时,任务A都必须在任务B之后执行。

需要注意的是,mustRunAfter 不会强制执行任务B。如果任务B没有被加入到执行队列中,那么任务A也可以独立执行。同样,如果任务A没有被加入到执行队列中,那么任务B也可以独立执行。

这与 dependsOn 不同。dependsOn 会创建一个强制的依赖关系,也就是说,任务A依赖于任务B,那么只要任务A需要执行,任务B就必须要先执行。而 mustRunAfter 只是定义了一个任务的执行顺序,并不会创建一个依赖关系。

shouldRunAftermustRunAfter是一样的,只不过mustRunAfter如果导致了循环依赖,Gradle 会抛出一个错误,并导致构建失败。而shouldRunAfter不会抛出异常,Gradle会忽略这个顺序要求,不会导致构建失败。

4. 新建插件

4.1 创建插件类

MyLibrarybuild.gradle中,增加如下代码,创建插件类 : MyPlugin

class MyPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        println "---- MyPlugin.apply ----"

		project.afterEvaluate { 
			//在Gradle项目配置阶段完成后会被调用
			//后续代码放在这里...
		}
    }
}
4.2 应用这个插件

MyLibrarybuild.gradle中添加这行代码,表示应用这个插件

apply plugin : MyPlugin

 在Sync一下,可以看到会打印出日志,表明我们配置这个插件成功了

---- MyPlugin.apply ----

5. 实现初步的依赖

5.1 在app的assets中添加文件

appassets目录下,添加test1.sotest2.sotest3.so这三个文件

5.2 MyLibrary新建assets目录

MyLibrary下新建assets文件夹

5.3 在MyPlugin中实现复制文件的Task

注意 : 后面的这些操作,代码都放在MyPluginapply方法的project.afterEvaluate {}

def myCopyFiles = project.task("myPublishCopyTask", type: Copy) {
        from project.fileTree('../app/src/main/assets')
        include 'test1.so', 'test2.so'
        into 'src/main/assets'
}
myCopyFiles.group = "publishingV2"

Sync一下项目,可以在Android StudioGradle Tab中,看到publishingV2文件夹,里面有myPublishCopyTask这个Task。


我们点击这个Task,可以发现,appassets目录下的test1.sotest2.so已经被复制到MyLibraryassets目录下了

5.4 在MyPlugin中实现删除文件的Task
//原本task名叫做myPublishDeleteTask,但是后面关联了其他Task后,这个task的名称起做publishAutoCopyTask是比较合适的
def myDeleteFilesInDir = project.task("publishAutoCopyTask", type: Delete) {
    delete project.fileTree('src/main/assets')
}
myDeleteFilesInDir.group = "publishingV2"

Sync一下项目,可以在Android StudioGradle Tab中,看到publishingV2文件夹,里面有publishAutoCopy这个Task
我们点击publishAutoCopy这个Task,可以发现,MyLibraryassets目录下的test1.sotest2.so已经被删除了

5.5 关联Task

这里,我们调用packageReleaseAssets.mustRunAfter(myCopyFiles),表明调用packageReleaseAssets之前,必定会调用myCopyFiles,否则就会抛出异常。
然后调用myDeleteFilesInDir.dependsOn(myCopyFiles, publishTask),表示运行myDeleteFilesInDir的时候,会先去执行myCopyFilespublishTask

def packageReleaseAssets = project.tasks.findByName("packageReleaseAssets")
def publishTask = "publishProductionPublicationToReleaseMavenRepository"
packageReleaseAssets.mustRunAfter(myCopyFiles)
myDeleteFilesInDir.dependsOn(myCopyFiles, publishTask)
5.6 来看一下整体的代码

这部分代码都在MyLibrarybuild.gradle

rootProject.ext.GROUP = "com.heiko.mytest"
rootProject.ext.POM_ARTIFACT_ID = "mylibrary"
rootProject.ext.VERSION_NAME = "1.0.0"
apply from: "${project.rootDir}/maven_upload.gradle"

class MyPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        println "---- MyPlugin.apply ----"

        project.afterEvaluate {
            def myCopyFiles = project.task("myPublishCopyTask", type: Copy) {
                from project.fileTree('../app/src/main/assets')
                include 'test1.so', 'test2.so'
                into 'src/main/assets'
            }
            myCopyFiles.group = "publishingV2"

            //原本task名叫做myPublishDeleteTask,但是后面关联了其他Task后,这个task的名称起做publishAutoCopy是比较合适的
            def myDeleteFilesInDir = project.task("publishAutoCopyTask", type: Delete) {
                delete project.fileTree('src/main/assets')
            }
            myDeleteFilesInDir.group = "publishingV2"

            def packageReleaseAssets = project.tasks.findByName("packageReleaseAssets")
            def publishTask = "publishProductionPublicationToReleaseMavenRepository"
            packageReleaseAssets.mustRunAfter(myCopyFiles)
            myDeleteFilesInDir.dependsOn(myCopyFiles, publishTask)
        }
    }
}

apply plugin: MyPlugin
5.7 调用myPublishDeleteTask

这个时候,我们在Gradle中执行myPublishDeleteTask这个任务,会发现 自动复制文件 -> 打包 -> 上传到Maven仓库 -> 删除文件这个链路,都自动执行了。

5.7.1 查看Maven仓库

我们来查看下Maven仓库,可以看到已经上传到maven仓库了
在这里插入图片描述

5.7.2 下载aar文件

下载这个版本的aar文件

接着拖到Android Studio中,就可以看到assets里已经有test1.sotest2.so

6. 实现动态配置

接着我们就可以来实现动态的配置了。
要实现动态配置,就需要在appbuild.gradle中配置名称和要复制的文件名称,像下面这样 :

apply plugin: MyPlugin

publishAutoCopy {
    //要复制的源文件目录
    sourcePath = '../app/src/main/assets'
    //要复制的目标文件目录
    targetPath = 'src/main/assets'

    publishItem {
        //名称
        name = 'weather'
        //要复制的文件名
        sourceFiles = ['test1.so', 'test2.so']
    }
    publishItem {
        //名称
        name = 'ocr'
        //要复制的文件名
        sourceFiles = ['test3.so']
    }
}

 那么需要怎么将这些配置传递到MyPlugin插件中呢 ?

6.1 编写传参类
class PublishAutoCopyItem {
    String name
    ArrayList<String> sourceFiles
}

class PublishAutoCopyExtension {
    private Project project
    String targetPath
    String sourcePath
    List<PublishAutoCopyItem> items = []

    PublishAutoCopyItem publishItem(Closure closure) {
        PublishAutoCopyItem myItem = new PublishAutoCopyItem()
        project.configure(myItem, closure)
        items << myItem
        return myItem
    }

    PublishAutoCopyExtension(Project project) {
        this.project = project
    }
}
6.2 在Plugin中获取传参
class MyPlugin implements Plugin<Project> {

    @Override
    void apply(Project project) {
        def extension = project.extensions.create("publishAutoCopy", PublishAutoCopyExtension)

        project.afterEvaluate {
            def sourcePath = extension.sourcePath ?: ""
            def targetPath = extension.targetPath ?: ""
            def items = extension.items

            println("sourcePath:" + sourcePath + " targetPath:" + targetPath)
            for (final def item in items) {
                println("item.name:" + item.name+" sourceFiles:"+item.sourceFiles)
            }

			//省略了之前写的代码...
        }
    }
}
6.3 在Build.gradle中配置

这个配置在MyLibrarybuild.gradle中即可

publishAutoCopy {
    //要复制的源文件目录
    sourcePath = '../app/src/main/assets'
    //要复制的目标文件目录
    targetPath = 'src/main/assets'

    publishItem {
        //名称
        name = 'weather'
        //要复制的文件名
        sourceFiles = ['test1.so', 'test2.so']
    }
    publishItem {
        //名称
        name = 'ocr'
        //要复制的文件名
        sourceFiles = ['test3.so']
    }
    //更多的配置可以在这里增加...
}
6.4 Sync下项目

然后我们Sync下项目,可以看到Gralde打印出了如下的日志

> Configure project :NcnnLibrary
sourcePath:../app/src/main/assets targetPath:src/main/assets
item.name:weather sourceFiles:[test1.so, test2.so]
item.name:ocr sourceFiles:[test3.so]

这样,我们就将配置传递给我们自定义的MyPlugin插件了

6.5 将参数传递给Task

将这些参数传递给Task,最终代码如下

project.afterEvaluate {
    def names = new ArrayList<>()
    def sourcePath = extension.sourcePath ?: ""
    def targetPath = extension.targetPath ?: ""

    for (final def item in extension.items) {
        def name = item.name ?: ""
        def sourceFiles = item.sourceFiles ?: [""]
        if (name.isEmpty()) return

        names.add(name)
        def nameCapitalize = name.capitalize()

        def myCopyFiles = project.task("myPublish${nameCapitalize}CopyTask", type: Copy) {
            from project.fileTree(sourcePath)
            include sourceFiles
            into targetPath
        }

        def myDeleteFilesInDir = project.task("publish${nameCapitalize}AutoCopy", type: Delete) {
            delete project.fileTree(targetPath)
        }
        myDeleteFilesInDir.group = "publishingv2"

        def packageReleaseAssets = project.tasks.findByName("packageReleaseAssets")
        def publishTask = "publish${nameCapitalize}PublicationToReleaseMavenRepository"
        packageReleaseAssets.mustRunAfter(myCopyFiles)
        myDeleteFilesInDir.dependsOn(myCopyFiles, publishTask)
    }

    project.rootProject.ext["${project.name}_NAMES"] = names
}

Sync项目后,可以发现有publishOcrAutoCopypublishWeatherAutoCopy两个Task

在这里插入图片描述

6.6 修改 maven_upload.gradle

myArtifactId现在通过rootProject.ext.POM_ARTIFACT_ID进行传递,所以我们现在需要修改maven_upload.gradle

apply plugin: 'maven-publish'

//TODO 这里填你Maven仓库的地址
def RELEASE_REPOSITORY_URL = "https://devops-maven.xxxxx.com/repository/yyyyy/"
//TODO 这里填你Maven仓库的账号
def NEXUS_USERNAME = "*********"
//TODO 这里填你Maven仓库的密码
def NEXUS_PASSWORD = "*********"

afterEvaluate {
    publishing {
        repositories {
            maven {
                name("ReleaseMaven")
                url = RELEASE_REPOSITORY_URL
                credentials {
                    username = NEXUS_USERNAME
                    password = NEXUS_PASSWORD
                }
            }
        }
        publications {
            List<String> names
            def namesKey = "${project.name}_NAMES"
            if (rootProject.ext.has(namesKey)) {
                names = rootProject.ext[namesKey]
            } else {
                names = new ArrayList()
                names.add("")
            }
            for (final def itemName in names) {
                def publicationName = itemName.capitalize()
                create(publicationName, MavenPublication) {
                    def myArtifactId
                    if (name.isEmpty()) myArtifactId = rootProject.ext.POM_ARTIFACT_ID
                    else myArtifactId = rootProject.ext.POM_ARTIFACT_ID + "-" + itemName

                    from components.release
                    groupId = rootProject.ext.GROUP
                    artifactId = myArtifactId
                    version = rootProject.ext.VERSION_NAME
                }
            }
        }
    }
}
6.7 动态配置完成

到这里,我们就完成动态配置了 : 我们想要打包带某些文件的aar,就点击对应的Task进行打包就行了

  • 想打带test1.sotest2.so的包,那么就点击publishWeatherAutoCopy
  • 想打带test3.so的包,那么就点击publishOcrAutoCopy

7. 将MyPlugin移到独立的gradle文件中

我们还可以将MyPlugin相关的代码移到一个单独的gradle文件中,比如publish_auto_copy.gradle,具体方式和maven_upload.gradle类似。

这样,我们就可以在build.gradle中,使用apply from: "${project.rootProject.rootDir}/publish_auto_copy.gradle"这一句代码,就应用MyPlugin插件了。接着,在build.gradle中配置好MyPlugin的参数就行了。

apply from: "${project.rootProject.rootDir}/publish_auto_copy.gradle"

publishAutoCopy {
    //要复制的源文件目录
    sourcePath = '../app/src/main/assets'
    //要复制的目标文件目录
    targetPath = 'src/main/assets'

    publishItem {
        //名称
        name = 'weather'
        //要复制的文件名
        sourceFiles = ['test1.so', 'test2.so']
    }
    publishItem {
        //名称
        name = 'ocr'
        //要复制的文件名
        sourceFiles = ['test3.so']
    }
    //更多的配置可以在这里增加...
}

至此,我们就完成了打包自动复制文件插件的全部功能了。

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

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

相关文章

MFC密码对话框之间数据传送实例(源码下载)

新建一个login工程项目对话框&#xff0c;主对话框IDD_LOGIN_DIALOG中一个显示按钮IDC_BUTTON1、一个密码按钮IDC_BUTTON2。添加一个密码对话框IDD_DIALOG1&#xff0c;添加类password&#xff0c;在对话框中添加一个编辑框IDC_EDIT1、一个确定按钮IDC_BUTTON1。 程序功能&…

「数据结构」队列

目录 队列的基本概念 队列的实现 头文件queue.h 实现函数接口 1.初始化和销毁 2.出队列和入队列 3.获取队头元素和队尾元素 4.队列长度判空 后记 前言 欢迎大家来到小鸥的博客~ 个人主页&#xff1a;海盗猫鸥 本篇专题&#xff1a;数据结构 多谢大家的支持啦&#xff…

存储+调优:存储-memcached

存储调优&#xff1a;存储-memcached 什么是memcached? 高性能的分布式内存缓存服务器。通过缓存数据库的查询结果&#xff0c;减少数据库访问次数&#xff0c;以提高动态Web应用的速度、提高可扩展性。 在memcached中存什么&#xff1f; 尽快被保存 访问频率高 1.数据保…

web前端框架设计第十课-组件

web前端框架设计第十课-组件 一.预习笔记 组件&#xff1a;Vue最强大的功能之一 1.局部组件注册 注意事项&#xff1a;template标签中只能有一个根元素 2.全局组件的注册 注意事项&#xff1a;组件名的大小写需要注意&#xff08;实践&#xff09; 3.案例&#xff08;查询框…

【计算机毕业设计】030英语学习交流平台微信小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

在Github上寻找安装ROS软件包

1、创建一个功能包 并下载git sudo apt install git 2、找到自己想在github上要克隆的包 复制此链接 3、克隆到本地 git clone 链接 4.scripts目录用于放置脚本文件和python程序 使用脚本安装编译需要的依赖库 5、下载完成后&#xff0c;在~catkin_ws目录下运行catkin_make进…

DFS:解决二叉树问题

文章目录 了解DFS1.计算布尔二叉树的值思路代码展示 2.求根节点到叶节点数字之和思路代码展示 3.二叉树剪枝思路代码展示 4.验证二叉搜索树思路分析代码展示 5.二叉搜索树中第k小元素思路&#xff1a;代码展示 6.二叉树的所有路径思路分析代码展示 总结 了解DFS 所谓DFS就是就…

python数据类型之元组、集合和字典

目录 0.三者主要作用 1.元组 元组特点 创建元组 元组解包 可变和不可变元素元组 2.集合 集合特点 创建集合 集合元素要求 集合方法 访问与修改 子集和超集 相等性判断 集合运算 不可变集合 3.字典 字典特点 字典创建和常见操作 字典内置方法 pprin模块 0.…

详解http协议

什么是HTTP协议 定义 Http协议即超文本传送协议 (HTTP-Hypertext transfer protocol) 。 它定义了浏览器&#xff08;即万维网客户进程&#xff09;怎样向万维网服务器请求万维网文档&#xff0c;以及服务器怎样把文档传送给浏览器。从层次的角度看&#xff0c;HTTP是面向&am…

缩进在编程中的重要性及正确使用方法

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 前言 缩进不当引发的问题 缩进的正确使用方法 缩进错误的调试与修复 总结 前言 在编程世…

微信小程序连接阿里云快速入门【物联网】

一、前言 1.1 项目背景 随着5G的逐渐普及&#xff0c;万物互联的浪潮已经席卷而来。在万物互联的场景下&#xff0c;如何实现设备之间的互联互通&#xff0c;成为了一个亟待解决的问题。 微信小程序作为一款轻量级的小程序开发框架&#xff0c;以其简洁的语法和丰富的组件库…

【ZYNQ】AXI-Quad-SPI SDK 开发记录 测试

前人工作 如前人工作&#xff0c;在Navigate to BSP Settings中找到历例程 file:///F:/Xilinx/Vitis/2019.2/data/embeddedsw/XilinxProcessorIPLib/drivers/spi_v4_5/doc/html/api/example.html使用XSpi_LowLevelExample例子&#xff0c;源代码的AI解析 int XSpi_LowLeve…

Java虚拟机揭秘-底层驱动力,性能保障!

Java虚拟机作为Java技术体系的核心组成部分&#xff0c;其重要性不言而喻。它不仅为Java提供了跨平台的能力&#xff0c;更是Java程序运行的基石。本文将为您深入解析Java虚拟机的工作原理、作用和应用场景&#xff0c;并通过生动的实例让您彻底理解这一关键技术。 一、Java虚拟…

温故而知新-MySQL篇【面试复习】

温故而知新-数据库篇【面试复习】 前言版权推荐温故而知新-Mysql篇Mysql常见面试题Mysql事务Mysql索引Mysql锁Mysql日志Mysql中的Buffer 数据库的三范式是什么MySQL对于LRU的优化InnoDB三大特性自适应哈希索引&#xff08;Adaptive Hash Index&#xff09;插入缓存&#xff08;…

驱动与系统学习网址

DRM&#xff08;Direct Rendering Manager&#xff09;学习简介-CSDN博客 Android Qcom Display学习(零)-CSDN博客 https://blog.csdn.net/hexiaolong2009/category_9705063.htmlhttps://blog.csdn.net/hexiaolong2009/category_9705063.htmlRender Hell —— 史上最通俗易懂…

【ECharts】数据可视化

目录 ECharts介绍ECharts 特点Vue2使用EChats步骤安装 ECharts引入 ECharts创建图表容器初始化图表更新图表 示例基本柱状图后台代码vue2代码配置 组件代码运行效果 基本折线图示例代码组件 基础饼图示例代码后台前端配置组件运行效果 其他 ECharts介绍 ECharts 是一个由百度开…

DataGear 制作服务端分页的数据可视化看板

DataGear 2.3.0 版本新增了附件图表数据集特性&#xff08;在新建图表时将关联的数据集设置为 附件 &#xff0c;具体参考官网文档定义图表章节&#xff09;&#xff0c;在制作看板时&#xff0c;可以基于此特性&#xff0c;结合dg-chart-listener&#xff0c;利用服务端数据扩…

使用VCPKG编译并使用Qt5

一、背景 Qt就不介绍了。VCPKG可以看这里VCPKG资料记录_vcpkg boost 多久-CSDN博客 为什么搞Qt5而不是Qt6&#xff1f;因为Qt5比较稳定吧。而且我公司也是用的Qt5。 为什么要自己编译而不是去下载Qt5&#xff1f; 第一&#xff0c;因为Qt5在Qt在线安装版本只提供到Qt5.15.2&…

C语言内存函数超详细讲解

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 C语言内存函数超详细讲解 收录于专栏【C语言学习】 本专栏旨在分享学习C语言学习的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. m…

linux文件权限常用知识点,基于Linux(openEuler、CentOS8)

目录 知识点常用实例 知识点 真实环境文件显示 解读 常用实例 文件所有者 chown -R nginx:nginx /home/source目录权限(R选填必须大写<遍历子文件夹及文件>) chmod -R 755 /home/sourcechmod -R 777 /home/source