Android学习之路(29) Gradle初探
前言:
大家回想一下自己第一次接触Gradle
是什么时候? 相信大家也都是和我一样,在我们打开第一个AS项目的时候, 发现有很多带gradle字样的文件:setting.gradle, build.gradle,gradle.warpper
,以及在gradle
文件中各种配置, 这些都是啥wy啊。。
1.Gradle
定义:
很多开发喜欢把Gradle
简单定义为一种构建工具,和ant,maven
等作用类似, 诚然Gradle确实是用来做构建,但是如果简单得把Gradle拿来做构建,就太小看Gradle了.
笔者更愿意将Gradle看做一种编程框架
。在这个框架中,你可以做很多ant,maven等常用构建工具做不了的事情, 如将自己的任务task集成到构建生命周期中,完成文件拷贝,脚本编写等操作。
2.Gradle
优缺点:
相较早期的构建工具:ant,maven等。
优点如下:
- 1.使用DSL Grovvy语言来编写: :了解ant的同学应该都知道:ant使用的是xml配置的模式,而Gradle使用的是
表达性的Groovy
来编写, Groovy同时支持面向对象和面向过程
进行开发,这个特性让Groovy
可以写出一些脚本的任务,这在传统ant,maven上是不可能实现的 - 2.基于java虚拟机: :
Groovy
是基于jvm
的语言,groovy
文件编译后其实就是class文件,和我们的java
一样。
所以在gradle构建过程中,我们完全可以使用java/kotlin去编写我们的构建任务以及脚本,极大的降低我们学习的成本。
- 3.Gradle自定义task:可以构建自己的任务,然后挂接到gradle构建
生命周期
中去,这在ant,maven上也是不可能实现的, - 4.扩展性好:gradle将关键配置扔给我们开发者,开发者配置好任务后,无需关心gradle是如何构建的。
- 5.支持增量更新:增量更新可以大大加快我们的编译速度
关于Groovy的语法篇:可以参考这篇文章: Gradle筑基篇(二)-groovy语法详解
缺点:
用过gradle都知道,低版本gradle的项目在高版本的gradle中经常出现很多莫名其妙的错误,向后兼容性较差。
3.Gradle
工程结构:
gradle标准工程代码如下
java
复制代码
├── moduleA
│ └── build.gradle
├── moduleB
│ └── build.gradle
├── build.gradle
├── settings.gradle
├── gradle.properties
├── local.properties
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
- 1.
build.gradle
:可以理解为一个Project脚本,Project脚本中有自己的任务,最外层的Project为rootProject - 2.
settings.gradle
:主要用来配置我们项目中需要用到的模块。用include关键字给包裹进 - 3.
gradle.properties
:这个文件主要是设置一些全局变量,包括jvm运行以及自定义的一些全局参数 - 4.
local.properties
:这个文件主要配置一些本地的sdk和ndk版本信息以及路径 - 5.
gradle-wrapper.jar
:负责自动下载Gradle脚本运行环境 - 6.
gradle-wrapper.properties
:用来配置当前使用的Gradle的版本以及存储的路径
java
复制代码
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https://services.gradle.org/distributions/gradle-6.5-bin.zip
复制代码
distributionBase + distributionPath:指定Gradle安装路径;
zipStoreBase + zipStorePath:指定Gradle安装包的存储路径;
distributionUrl:Gradle版本的下载地址。
注意这里如果将bin改为all,则可以查看当前Gradle的源码信息。
- 7.
gradlew和gradlew.bat
:用来执行构建任务的脚本,可以在命令行使用gradlew xxxTask
4.Gradle生命周期
Gradle作为新兴的构建工具,其内部也有自己的生命周期阶段,每个阶段做的事情都层次分明, 了解Gradle生命周期,才能很好的使用我们的Gradle工具。
1.初始化阶段
做了哪些事情?:
- 1.初始化
Setting.gradle
文件,获取setting
实例, - 2.执行
setting
中的脚本,根据include
字段,创建对应的project
实例 - 3.设置构建需要的环境
注意:初始化阶段执行任何任务都会执行一次。
Project实例关系如下:
2.配置阶段
- 1.下载所有插件和构建脚本依赖项
- 2.执行
build.gradle
文件中的脚本信息 - 3.实现task任务的拓扑图,这个图是一个
有向无环图
,防止任务执行进入死循环。
注意:配置阶段执行任何任务都会执行一次。
3.执行阶段
执行阶段就是根据当前task拓扑图进行执行task任务。
需要注意以下几点:
- 1.在项目中配置的
doLast,doFirst
操作,都会在任务执行阶段执行,而不会在配置阶段执行, 而如果任务需要执行,需要挂接到gradle执行生命周期
中,笔者开始接触gradle时就踩过这个坑。。这块后面讲解task
的时候在来具体讲解 - 2.前面也说了初始化阶段和配置阶段在每个任务执行前都会执行,所以不要在前两个阶段进行一些耗时的操作,这样可能每次编译执行你都会崩溃的
5.Gradle生命周期监听:
要查找Gradle是如何监听生命周期,可以到Gradle
源码中看看:
- 1.监听初始化阶段 初始化阶段主要用来初始化
Setting.gradle
文件,获取setting
实例,创建Project
实例等,所以其可用下面代码监听:
java
复制代码
//开始初始化Setting.gradle前
this.gradle.beforeSettings {println "beforeSettings"
}
//Setting.gradle配置完毕后,创建了setting实例
this.gradle.settingsEvaluated {println "settingsEvaluated"
}
//执行解析Setting.gradle文件后,创建了project实例列表
this.gradle.projectsLoaded {println "projectsLoaded"
}
- 2.监听配置阶段
2.1:监听当前project的配置阶段前后:
在Project源码
中可以看到:
java
复制代码
/*** Adds an action to execute immediately before this project is evaluated.** @param action the action to execute.*/
void beforeEvaluate(Action<? super Project> action);/*** Adds an action to execute immediately after this project is evaluated.** @param action the action to execute.*/
void afterEvaluate(Action<? super Project> action);/*** <p>Adds a closure to be called immediately before this project is evaluated. The project is passed to the closure* as a parameter.</p>** @param closure The closure to call.*/
void beforeEvaluate(Closure closure);/*** <p>Adds a closure to be called immediately after this project has been evaluated. The project is passed to the* closure as a parameter. Such a listener gets notified when the build file belonging to this project has been* executed. A parent project may for example add such a listener to its child project. Such a listener can further* configure those child projects based on the state of the child projects after their build files have been* run.</p>** @param closure The closure to call.*/
void afterEvaluate(Closure closure);
看这两个方法的说明就是用来监听配置阶段,传入的是一个Action或者传入一个闭包,闭包的代理为当前Project
使用方式如下:
java
复制代码
//监听project被配置前
this.beforeEvaluate {Project project ->println "${project.name} :beforeEvaluate"
}
//监听project被配置后
this.afterEvaluate {Project project ->println "${project.name}:afterEvaluate"
}
注意:这个监听只是针对当前Project的配置阶段而不是所有Project的配置
你也可以使用:
java
复制代码
this.project.beforeEvaluate
this.project.afterEvaluate
那么有没有可以监听所有Project的配置阶段的api呢?安排
2.2:监听每个Project的配置前后:
使用this.gradle的内部方法,因为gradle是相对于整个工程作为作用域
java
复制代码
//监听所有的Project的被配置前
this.gradle.beforeProject {Project project ->println "${project.name}:beforeProject"
}
//监听所有的Project的被配置后
this.gradle.afterProject {Project project ->println "${project.name}:afterProject"
}
编译下看看:
java
复制代码
> Configure project :
gradle_source_plugin:afterProject> Configure project :app
app:beforeProject
do app evaluating
app:afterProject> Configure project :application
application:beforeProject
do application evaluating
application:afterProject
看到当前工程所有的project都调用了一次beforeProject和afterProject
那有同学又要问了,有没有监听整个project配置阶段的:当然有
2.3:监听全部project配置阶段的前后
java
复制代码
this.gradle.projectsEvaluated {println "all projectsEvaluated"
}
这个闭包可以监听整个项目的配置完毕后的事件
配置阶段还有一些监听如下:
2.4:监听任务的添加操作
java
复制代码
this.project.tasks.whenTaskAdded {Task task->println "${task.name}:whenTaskAdded"
}
2.5:监听任务拓扑图的执行
java
复制代码
//task拓扑图构造完毕
this.gradle.taskGraph.whenReady {TaskExecutionGraph graph->println "taskGraph:->"+graph
}
监听拓扑图完毕后其实才是真正的配置阶段完毕,瞧瞧源码:
在BasePlugin中:
java
复制代码
threadRecorder.record(ExecutionType.BASE_PLUGIN_PROJECT_CONFIGURE,project.getPath(),null,this::configureProject);threadRecorder.record(ExecutionType.BASE_PLUGIN_PROJECT_BASE_EXTENSION_CREATION,project.getPath(),null,this::configureExtension);threadRecorder.record(ExecutionType.BASE_PLUGIN_PROJECT_TASKS_CREATION,project.getPath(),null,this::createTasks);
看到配置阶段最后一步才是创建Task,所以可以使用this.gradle.taskGraph.whenReady监听整个配置阶段的结束
- 3.监听执行阶段
3.1:监听任务执行:
java
复制代码
gradle.taskGraph.beforeTask { Task task ->println "${task.name}:beforeTask"
}
gradle.taskGraph.afterTask {Task task ->println "${task.name}:afterTask"
}
执行下面任务:
task clean(type: Delete) {doFirst {println 'clean:doFirst'}doLast {println 'clean:doLast'}delete rootProject.buildDir
}
结果:
> Task :clean
clean:beforeTask
clean:doFirst
clean:doLast
clean:afterTask
可以看到在task执行前后调用了监听中的方法
3.2:监听执行任务阶段开始
其实可以使用配置阶段的this.gradle.taskGraph.whenReady,这个就是所有project配置完毕,且生成了task拓扑图 下一步就是开始执行任务了
3.3:监听执行任务阶段结束
this.gradle.buildFinished {} 这个可以监听所有任务执行完毕后事件回调:
6.Gradle Api
Gradle为我们提供了很多丰富的api操作 主要有几下几种:
- Project api
- Task api
- 属性 api
- 文件 api
- 以及一些其他api
由于api这块篇幅比较多,就不展开讲解了,后面会单独出一篇文章来讲解这块内容
7.Gradle插件
Gradle插件在我们的项目中使用的还是比较多的,在一些优秀的开源框架:
如鹅厂的Tinker
,滴滴的VirtualApk
,阿里的Arouter
等 内部都使用了Gradle
插件知识
笔者Gradle插件开始学习的时候,也是一脸懵逼,
其实你把Gradle插件理解为一个第三方jar包就可以了,只是这个jar包是用于我们apk构建的过程 内部其实也是使用一些Task,挂接到我们的apk构建生命周期中。 这里也不会过多讲解
下面我们来讲下Gradle一个特性:
8.增量更新
有没发现你在构建过程中,如果修改的地方对整个任务容器影响不大情况下,你的编译速度会很快,其实就是Gradle默认支持增量更新功能。
- 1.
定义
:
官方:
An important part of any build tool is the ability to avoid doing work that has already been done. Consider the process of compilation. Once your source files have been compiled, there should be no need to recompile them unless something has changed that affects the output, such as the modification of a source file or the removal of an output file. And compilation can take a significant amount of time, so skipping the step when it’s not needed saves a lot of time.
简单点说就是Gradle目前对Task的输入和输出做了判断,如果发现文件的输入和输出没有变化, 就直接使用之前缓存的输入输出数据,不再重新执行,缩短编译时间
这里就涉及到了Task的一些知识点: Task是我们apk构建过程中给的最少单位,每个任务都有输入和输出,将输入的信息传递给下一个任务作为下一个任务的输入,这就是整个构建体系正常运行的核心。
- 2.
Task
输入和输出
任务的执行离不开输入和输出,和我们方法执行一样,依赖输入参数和输出返回值
Gradle
中使用:
TaskInputs
:来管理输入
TaskOutputs
:来管理输出
我们来看下这个两个类的内部代码:
java
复制代码
TaskInputs.java
public interface TaskInputs {/*** Returns true if this task has declared the inputs that it consumes.** @return true if this task has declared any inputs.*/boolean getHasInputs();/*** Returns the input files of this task.** @return The input files. Returns an empty collection if this task has no input files.*/FileCollection getFiles();/*** Registers some input files for this task.** @param paths The input files. The given paths are evaluated as per {@link org.gradle.api.Project#files(Object...)}.* @return a property builder to further configure the property.*/TaskInputFilePropertyBuilder files(Object... paths);/*** Registers some input file for this task.** @param path The input file. The given path is evaluated as per {@link org.gradle.api.Project#file(Object)}.* @return a property builder to further configure the property.*/TaskInputFilePropertyBuilder file(Object path);/*** Registers an input directory hierarchy. All files found under the given directory are treated as input files for* this task.** @param dirPath The directory. The path is evaluated as per {@link org.gradle.api.Project#file(Object)}.* @return a property builder to further configure the property.*/TaskInputFilePropertyBuilder dir(Object dirPath);/*** Returns a map of input properties for this task.** The returned map is unmodifiable, and does not reflect further changes to the task's properties.* Trying to modify the map will result in an {@link UnsupportedOperationException} being thrown.** @return The properties.*/Map<String, Object> getProperties();/*** <p>Registers an input property for this task. This value is persisted when the task executes, and is compared* against the property value for later invocations of the task, to determine if the task is up-to-date.</p>** <p>The given value for the property must be Serializable, so that it can be persisted. It should also provide a* useful {@code equals()} method.</p>** <p>You can specify a closure or {@code Callable} as the value of the property. In which case, the closure or* {@code Callable} is executed to determine the actual property value.</p>** @param name The name of the property. Must not be null.* @param value The value for the property. Can be null.*/TaskInputPropertyBuilder property(String name, @Nullable Object value);/*** Registers a set of input properties for this task. See {@link #property(String, Object)} for details.** <p><strong>Note:</strong> do not use the return value to chain calls.* Instead always use call via {@link org.gradle.api.Task#getInputs()}.</p>** @param properties The properties.*/TaskInputs properties(Map<String, ?> properties);/*** Returns true if this task has declared that it accepts source files.** @return true if this task has source files, false if not.*/boolean getHasSourceFiles();/*** Returns the set of source files for this task. These are the subset of input files which the task actually does work on.* A task is skipped if it has declared it accepts source files, and this collection is empty.** @return The set of source files for this task.*/FileCollection getSourceFiles();
}
源文件中我们可以看出: 输入可以有以下种类:
- 1.文件,文件夹以及一个文件集合
- 2.普通的key value属性
- 2.Map:传递一个Map的属性集合
TaskInputs
还可以通过getHasInputs
判断是否有输入
同理我们来看下TaskOutputs
的源码,篇幅原因,这里直接看下TaskOutputs的方法框架:
大部分情况和inputs类似,可以输出为文件,属性properties等
注意到这里有几个关键的方法: upToDateWhen和cacheIf
这两个方法就是用来对构建中的是否对输出操作进行缓存的点,用于增量构建
使用
总结
本篇文章主要是讲解了Gradle一些基础认识,Gradle工程项目的概括以及Gradle构建生命周期管理和监听等操作。 后面文章会陆续推出关于GradleApi,Gradle插件以及AGP插件的详细介绍,希望大家能从中会有一些收获。
相关文章:

Android学习之路(29) Gradle初探
前言: 大家回想一下自己第一次接触Gradle是什么时候? 相信大家也都是和我一样,在我们打开第一个AS项目的时候, 发现有很多带gradle字样的文件:setting.gradle, build.gradle,gradle.warpper,以及在gradle文件中各种配置ÿ…...

python-自动化篇-运维-语音识别
文章目录 理论文本转换为语音使用 pyttsx使用 SAPI使用 SpeechLib 语音转换为文本 代码和效果01使用pyttsx实现文本_语音02使用SAPI实现文本_语音03使用SpeechLib实现文本_语音04使用PocketSphinx实现语音转换文本 理论 语音识别技术,也被称为自动语音识别…...

ElasticSearch-ElasticSearch实战-仿京东商城搜索(高亮)
注:此为笔者学习狂神说ElasticSearch的实战笔记,其中包含个人的笔记和理解,仅做学习笔记之用,更多详细资讯请出门左拐B站:狂神说!!! 七、ElasticSearch实战 仿京东商城搜索(高亮) 1、工程创建…...
解释 Python 中的描述符(Descriptor)是什么?如何在 Python 中实现一个简单的 ORM(对象关系映射)?
解释 Python 中的描述符(Descriptor)是什么?举例说明其用法。 在 Python 中,描述符(Descriptor)是一种对象属性的扩展机制,它允许你在访问或修改属性时执行自定义的操作。描述符是实现了特定协…...

IP数据云识别真实IP与虚假流量案例
随着互联网的普及,企业在数字领域面临着越来越复杂的网络威胁。为了保护网站免受虚假流量和恶意攻击的影响,许多企业正在采用IP数据云。本文将结合一个真实案例,深入探讨IP数据云如何成功准确地识别真实用户IP和虚假流量IP,提高网…...
signalR+websocket:实现消息实时通讯——技能提升
signalR 解决步骤1:npm install microsoft/signalr6.0.6 安装指定版本的microsoft/signalr,我这边安装的版本是6.0.6 解决步骤2:引入import * as signalR from microsoft/signalr; import * as signalR from microsoft/signalr; 下面第三…...

机器学习入门-----sklearn
机器学习基础了解 概念 机器学习是人工智能的一个实现途径 深度学习是机器学习的一个方法发展而来 定义:从数据中自动分析获得模型,并利用模型对特征数据【数据集:特征值+目标值构成】进行预测 算法 数据集的目标值是类别的话叫做分类问题;目标值是连续的数值的话叫做回…...

双非本科准备秋招(15.3)—— 力扣二叉树
今天学了二叉树结点表示法,建树代码如下。 public class TreeNode {public int val;public TreeNode left;public TreeNode right;public TreeNode(int val) {this.val val;}public TreeNode(int val, TreeNode left, TreeNode right) {this.val val;this.left …...

20240203在WIN10下使用GTX1080配置stable-diffusion-webui.git不支持float16精度出错的处理
20240203在WIN10下使用GTX1080配置stable-diffusion-webui.git不支持float16精度出错的处理 2024/2/3 21:23 缘起:最近学习stable-diffusion-webui.git,在Ubuntu20.04.6下配置SD成功。 不搞精简版本:Miniconda了。直接上Anacoda! …...

京东微前端框架MicroApp简介
一、MicroApp 1.1 MicroApp简介 MicroApp是由京东前端团队推出的一款微前端框架,它从组件化的思维,基于类WebComponent进行微前端的渲染,旨在降低上手难度、提升工作效率。MicroApp无关技术栈,也不和业务绑定,可以用于任何前端框架。 官网链接:https://micro-zoe.gith…...

SpringBoot 使用定时任务(SpringTask)
Spring3.0以后自带的task,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。 使用步骤: 1.导入坐标 在spring-boot-starter-web坐标中,就包含了SpringTask,所以一般的Web项目都包含了。 <depende…...

国标GB/T 28181详解:设备视音频文件检索消息流程
目 录 一、设备视音频文件检索 二、设备视音频文件检索的基本要求 三、命令流程 1、流程图 2、流程描述 四、协议接口 五、产品说明 六、设备视音频文件检索的作用 七、参考 在国标GBT28181中,定义了设备视音频文件检索消息的流程,主…...
openssl自签名CA根证书、服务端和客户端证书生成并模拟单向/双向证书验证
1. 生成根证书 1.1 生成CA证书私钥 openssl genrsa -aes256 -out ca.key 2048 1.2 取消密钥的密码保护 openssl rsa -in ca.key -out ca.key 1.3 生成根证书签发申请文件(csr文件) openssl req -new -sha256 -key ca.key -out ca.csr -subj "/CCN/STFJ/LXM/ONONE/OU…...

NIO Selector简介
1.Selector和Channel关系 Selector一般称为选择器,也叫多路复用器,NIO的核心组件,用于检查一个或多个Channel的状态是否处于可读、可写的状态。 2.可选择通道 (1)不是所有的channel都能被selector复用,…...
2023-12蓝桥杯STEMA考试 C++ 中高级试卷解析
蓝桥杯STEMA考试 C++ 中高级试卷(12月) 一、选择题 第一题 定义字符串 string a = "Hello C++",下列选项可以获取到字符 C 的是(B)。 A、a[7] B、a[6] C、a[5] D、a[4] 第二题 下列选项中数值与其它项不同的是( C)。 A、 B、 C、 D、 第三题 定义变量 int i =…...

设计模式——2_1 命令(Command)
文章目录 定义图纸一个例子:空调和他的遥控器只有控制面板的空调遥控器可以撤销的操作 碎碎念命令和Runnable命令和事务 定义 把请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持…...
HP数组面试题
PHP数组面试题 问题: 如何创建一个空数组和一个带有初始值的数组? 答案: 创建空数组:可以使用array()函数或空数组语法[]来创建一个空数组,例如$arr array();或$arr [];。创建带有初始值的数组:可以在创建…...
机器学习5-线性回归之损失函数
在线性回归中,我们通常使用最小二乘法(Ordinary Least Squares, OLS)来求解损失函数。线性回归的目标是找到一条直线,使得预测值与实际值的平方差最小化。 假设有数据集 其中 是输入特征, 是对应的输出。 线性回归的…...

vulhub中Adminer ElasticSearch 和 ClickHouse 错误页面SSRF漏洞复现(CVE-2021-21311)
Adminer是一个PHP编写的开源数据库管理工具,支持MySQL、MariaDB、PostgreSQL、SQLite、MS SQL、Oracle、Elasticsearch、MongoDB等数据库。 在其4.0.0到4.7.9版本之间,连接 ElasticSearch 和 ClickHouse 数据库时存在一处服务端请求伪造漏洞(…...

浅谈Zookeeper及windows下详细安装步骤
1. Zookeeper介绍 1.1 分布式系统面临的问题 分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。 面临的问题:系统每个节点之间信息同步及共享 以一个小团队为例,面临的问题 通过网络进行信息…...
CCPC chongqing 2025 H
题目链接:https://codeforces.com/gym/105887 题目背景: 方框上有上下两排小球,下面的紧贴框底,上面的部分贴框顶,每牌小球上都有一个一个数字(1~n),将相同的小球连接到一起,是否在不交叉的情况…...
.net webapi http参数自定义绑定模型
.NET Web API 中 HTTP 参数自定义绑定模型的深度解析 在 .NET Web API 开发里,常规的参数绑定往往能满足大部分需求。不过,当遇到一些特殊情况时,就需要自定义将 HTTP 参数绑定到 action 特定模型参数了。接下来,我们就深入探讨如…...

智能对联网页小程序的仓颉之旅
#传统楹联遇上AI智能体:我的Cangjie Magic开发纪实 引言:一场跨越千年的数字对话 "云对雨,雪对风,晚照对晴空"。昨天晚上星空璀璨,当我用仓颉语言写下第一个智能对联网页小程序的Agent DSL代码时࿰…...

Moldflow充填分析设置
1. 如何选择注塑机: 注塑机初选按注射量来选择: 点网格统计;选择三角形, 三角形体积就是产品的体积 47.7304 cm^3 点网格统计;选择柱体, 柱体的体积就是浇注系统的体积2.69 cm^3 所以总体积产品体积浇注系统体积 47.732.69 cm^3 材料的熔体密度与固体…...

如何理解机器人课程的技术壁垒~壁垒和赚钱是两件不同的事情
答疑: 有部分朋友私聊说博客内容,越来越不适合人类阅读习惯…… 可以做这种理解,我从23年之后,博客会不会就是写给机器看的。 或者说我在以黑盒方式测试AI推荐的风格。 主观-客观-主观螺旋式发展过程。 2015最早的一篇博客重…...

Neovim - 打造一款属于自己的编辑器(一)
文章目录 前言(劝退)neovim 安装neovim 配置配置文件位置第一个 hello world 代码拆分 neovim 配置正式配置 neovim基础配置自定义键位Lazy 插件管理器配置tokyonight 插件配置BufferLine 插件配置自动补全括号 / 引号 插件配置 前言(劝退&am…...

【数据结构】详解算法复杂度:时间复杂度和空间复杂度
🔥个人主页:艾莉丝努力练剑 ❄专栏传送门:《C语言》、《数据结构与算法》 🍉学习方向:C/C方向 ⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平 前言&…...
分页查询的实现
第一步:导入pom依赖 <!--配置PageHelper分页插件--><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.6</version><exclusions>…...
gh hugging face使用
install sudo dpkg -i gh_2.74.0_linux_amd64.deb gh auth login gh auth login ? Where do you use GitHub? GitHub.com ? What is your preferred protocol for Git operations on this host? HTTPS ? Authenticate Git with your GitHub credentials? Yes ? How wo…...

爆炸仿真的学习日志
今天学习了一下【Workbench LS-DYNA中炸药在空气中爆炸的案例-哔哩哔哩】 https://b23.tv/kmXlN29 一开始 如果你的 ANSYS Workbench 工具箱(Toolbox)里 只有 SPEOS,即使尝试了 右键刷新、重置视图、显示全部 等方法仍然没有其他分析系统&a…...