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

Android Gradle权威指南读书笔记

第一章 Gradle入门

  1. 生成Gradle Wrapper
    命令:gradle wrapper --gradle-version 版本号
  2. 自定义Gradle Wrapper

task wrapper(type : Wrapper) { 
gradleVersion = '2.4'
archiveBase = 'GRADLE USER HOME'
archivePath = 'wrapper/dists'
distributionBase = 'GRADLE USER HOME' 
distributionPath = 'wrapper/dists'
distributionUrl = 'http\://services.gradle.org/distributions/gradle-2.4-all.zip' 
  1. 打印日志
  • print或者logger
  • print 定向为 QUIET 级别日志

logger.quiet('quiet日志信息')
logger.error('error日志信息’)
logger.warn('warn日志信息')
logger.lifecycle(`lifecycle日志信息')
logger.info('info 日志信息.')
logger.debug('debug 日志信息.')
  1. 命令行
  • 查看帮助:./gradlew tasks,./gradlew -h,/gradlew help --task [task]
  • 强制刷新依赖:./gradlew --refresh-dependencies assemble

第二章 Groovy语法

Groovy完全兼容 Java ,这就意味着你可以在 build 本文件里写任何的 Java 代码。

字符串

  1. 单引号和双引号都可以定义字符串常量
  2. 单引号不支持字符串表达运算,但双引号支持

task printStringVar <{
def name 张三
println '单引号的变量计算:${name}'
println "双引号的变量计算:${name}"
}

./gradlew printStringVar 运行后输出:

单引号的变量计算: ${name}
双引号的变量计算:张三

双引号可以直接进行表达式计算的这个能力非常好用,我们可以用这种方式进行字符串连接运算,再也不用 Java 中烦琐的“+”号了。记住这个嵌套的规则,一个美元符号紧跟着一对花括号,花括号里放表达式,比如${name}${1+1}等,只有一个变量的时候可以省略花括号,如$name.

集合

  1. List

task printList {def numList =[1,2,3,4,5,6]; println numList. getClass().name //ArrayListprintln numList[1]//访问第二个元素println numList[-1]//访问最后一个元素println numList[-2]//访问倒数第二个元素println numList[1..3]//访问第二个到第四个元素numList.each { //遍历println it //it 变量就是正在迭代的元素}
}
  1. Map

task printlnMap << {def mapl =[ 'width': 1024 ,'height': 768]println mapl.getClass() . name //LinkedHashMapprintln mapl ['width' ] println mapl.heightmapl.each { println "Key:${it . key),Value:${it.value}"}
}
  1. 方法
    3.1 括号可以省略

task invokeMethod << {method1(1,2) method1 1,2 
}def method1(int a,int b) { println a+b
}

3.2 return 是可以不写的
当没有return时,Groovy 会把方法执行过程中的最后一句代码作为其返回值:

task printMethodReturn << {def add1 = method2 1 , 2 def add2 = method2 5 , 3 println "addl1:${add1},add2:${add2 }"
} def method2{int a,int b) { if (a>b){a }else{ b}
}

3.3 代码块可作为参数传递
以集合的each方法为例:

//呆板的写法其实是这样
numList.each({println it}) 
//我们格式化一下,是不是好看一些
numList.each({ println it 
}) 
//好看一些, Groovy 规定,如果方法的最后一个参数是闭包 ,可以放到方法外面
numList.each() { println it 
}
//然后方法可以省略括号,就变成我们经常看到的样式。这种写法其实是个方法调用
numList.each { println it
}

JavaBean

并不是一定要定义成员变量才能作为类的属性访问,直接用getter/setter方法,也一样可以当作属性访问:

task helloJavaBean <{Person p = new Person()println "名字是:${p.name}"p.name = "张三"println "名字 ${p.name}"println "年龄是${p.age}"
}class Person { private String namepublic int getAge() { 12}
}

上面的例子发现,有定会getAge()方法时,一样可以使用点运算把getter方法当成属性运算。但是不能修改age的值,因为我们并没有定义setter

闭包

闭包,其实就是一个代码块。

task helloClosure <<{customEach{//调用闭包print it//it关键字代表传入的参数}
}def customEach(closure){for(int i in 1..10){closure(i)//闭包的调用,传入闭包需要接收的参数。如果只有一个参数,那么就是我们的it变量了}
}
  1. 向闭包传递参数

task helloClosure << {//多个参数eachMap {k,v -> //"->"符号用于把闭包的参数和主体区分开来。println "${k} is ${v}"}
}def eachMap(closure) { def map1 = ["name": "张三", "age":18]map1.each{ closure(it.key,it.value)}
}
  1. 闭包委托
    Groovy 闭包的强大之处在于它支持闭包方法的委托 Groovy 的闭包有 thisObject, owner,delegate 三个属性当你在闭包内调用方法时,由它们来确定使用哪个对象来处理。默认情况下delegate, owner是相等的,但是 delegate 是可以被修改的,这个功能是非常强大的,Gradle中的闭包的很多功能都是通过修改 delegate 实现的:

task helloDelegate << {new Delegate().test { println "this0bject:${thisObject. getClass()} "println "owner:${owner.getClass ()}"println "delegate:${delegate.getClass() }"method1()it.method()}
}def method1() { println "Context this:${this.getClass()) in root" println "method1 in root"
}class Delegate { def method1() { println "Delegate this:${this.getClass()) in Delegate"println "methodl in Delegate" }def test(Closure<Delegate> closure) {closure(this)}
}

输出结果:

thisObject:class build_e27c427w88bo0afju9niqltzf
owner:class build_e27c427w88bo0afju9niqltzf$_run_closure2
delegate : class build_e27c427w88bo0afju9niqltzf$_ run_closure2 
Context this : class build_e27c427w88bo0afju9niqltzf in root 
method1 in root 
Delegate this:class Delegate in Delegate 
method1 in Delegate

通过上面的例子我们发现,thisObject的优先级最高,默认情况下,优先使用 thisObect来处理闭包中调用 的方法,如果有则执行从输出中我们也可以看到,这个thisObject 其实就是这个构建脚本的上下文,它和脚本中this对象是相等的。
从例子中也证明了 delegateowner 是相等的,它们两个的优先级是: owner 要比 delegate高。所以闭包内方法的处理顺序是 thisObject > owner> delegate
DSL (Domain Specific Language,领域特定语言)中,比如 Gradle ,我们一般会指定delegate为当前的 it ,这样我们在闭包内就可以对该进行配置,或者调用其方法:

task configClosure << {person { personName = "张三"personAge = 20 dumpPerson () }
}class Person {String personName int personAge def dumpPerson() { println " name is ${personName} age is ${personAge}"}
}def person(Closure<Person> closure) { Person p =new Person() ; closure.delegate = p //委托模式优先closure.setResolveStrategy(Closure.DELEGATE_FIRST); closure(p)
}

例子中我们设置了委托对象为当前创建的 Person 实例,并且设置了委托模式优先,所以,我们在使用 person方法创建一个 Person 的实例时,可以在闭包里直接对该 Person 实例配置有没有发现和我们在 Gradle中使用 task 创建一个 Task 的用 法很像,其实在 Gradle 中有很多类似的用法,在 Gradle 中也基本上都是使用 delegate 的方式使用闭包进行配置等操作。

第三章 Gradle构建脚本基础

Settings文件

一个设置文件,用于初始化以及工程树的配置。

rootProject.name = 'android-gradle-book-code'
include ':example02'
project(':example02').projectDir = new File(rootDir, 'chapter01/example02')

Projects和tasks

一个Project 包含很多Task,也就是说每个 Project 是由多个 Task组成的。而Task 就是个操作,一个原子性的操作。task其实是Project的一个函数:

/*** <p>Creates a {@link Task} with the given name and adds it to this project. Calling this method is equivalent to* calling {@link #task(java.util.Map, String)} with an empty options map.</p>** <p>After the task is added to the project, it is made available as a property of the project, so that you can* reference the task by name in your build file.  See <a href="#properties">here</a> for more details</p>** <p>If a task with the given name already exists in this project, an exception is thrown.</p>** @param name The name of the task to be created* @return The newly created task object* @throws InvalidUserDataException If a task with the given name already exists in this project.*/Task task(String name) throws InvalidUserDataException;

创建task有以下两种方式,一个是task,一个是tasks方法创建TaskContainer

task customTask1 {doFirst {println 'customTask1:doFirst'}doLast {println 'customTask1:doLast'}
}tasks.create("customTask2") {doFirst {println 'customTask2:doFirst'}doLast {println 'customTask2:doLast'}
}

任务依赖

通过dependsOn方法。dependsOnTask类的一个方法,可以接受多个依赖的任务作为参数

task ex35Hello << {//<<符号是doLast方法的缩写println 'hello'
}task ex35World << {println 'world'
}task ex35Main(dependsOn: ex35Hello) {doLast {println 'main'}
}task ex35MultiTask {dependsOn ex35Hello,ex35WorlddoLast {println 'multiTask'}
}

任务间通过API控制和交互

创建一个任务和我们定义一个变量是一样的,变量名就是我们定义的任务名,类型Task。所以我们可以通过任务名,使用 Task API 访问它的方法属性或者对任务重新配置等。
对于直接通过任务名操纵任务的原理是:Project 在创建该任务的时候,同时把该任务对应的任务名注册Project 个属性,类型是 Task

task ex36Hello << {println 'dowLast1'
}ex36Hello.doFirst {println 'dowFirst'
}ex36Hello.doLast {println project.hasProperty('ex36Hello')//trueprintln 'dowLast2'
}

自定义属性

ProjectTask 都允许用户添加额外的自定义属性,要添加额外的属性,通过 ext 属性即可实现。相比局部变量,自定义属性有更为广泛的作用域,你可以跨 Project,跨 Task访问这些自定义属性。只要你能访问这些属性所属的对象,那么这些属性都可以被访问到。

apply plugin: "java"//自定义一个Project的属性
ext.age = 18//通过代码块同时自定义多个属性
ext {phone = 1334512address = ''
}sourceSets.all {ext.resourcesDir = null
}sourceSets {main {resourcesDir = 'main/res'}test {resourcesDir = 'test/res'}
}task ex37CustomProperty << {println "年龄是:${age}"println "电话是:${phone}"println "地址是:${address}"sourceSets.each {println "${it.name}的resourcesDir是:${it.resourcesDir}"}
}

第四章Gradle任务

创建Task

  1. 方法一,调用Projecttask(string)方法

def Task ex41CreateTask1 = task(ex41CreateTask1)
ex41CreateTask1.doLast {println "创建方法原型为:Task task(String name) throws InvalidUserDataException"
}
  1. 方法二,调用Projecttask(name,map),以一个任务名字+一个对该任务配置的 Map 对象。Map可配置项有限(type,overwrite,dependsOn,action,description,group),参考TaskContainer#Task create(Map<String, ?> options)
配置项描述默认值
type基于一个存在 Task 来创建,和我们类继承 不多DefaultTask
overwrite是否替换存在 Task ,这个和 type 合起来用false
dependsOn用于配直任务的依赖[]
action添加到任务中的 Actio 或者一个闭包null
description用于配置任务的描述null
group用于配置任务的分组null

def Task ex41CreateTask2 = task(ex41CreateTask2,group:BasePlugin.BUILD_GROUP)ex41CreateTask2.doLast {println "创建方法原型为:Task task(Map<String, ?> args, String name) throws InvalidUserDataException"println "任务分组:${ex41CreateTask2.group}"
}
  1. 方法三,Projecttask(Closure)。闭包里的委托对象就是Task ,所以你可以使用Task对象的任何方法、属性等信息进行配置。

task ex41CreateTask3 {description '演示任务创建'doLast {println "创建方法原型为:Task task(String name, Closure configureClosure)"println "任务描述:${description}"}
}# 核心都是调用TaskContainer 对象中的 create 方法。
tasks.create('ex41CreateTask4') {description '演示任务创建'doLast {println "创建方法原型为:Task create(String name, Closure configureClosure) throws InvalidUserDataException"println "任务描述:${description}"}
}

访问Task

  1. 我们创建的任务都会作为项目(Project )的一个属性,属性名就是任务名,所以我们可以直接通过该任务名称访问和操纵该任务:

task ex42AccessTask1
ex42AccessTask1.doLast {println 'ex42AccessTask1.doLast'
}
  1. Project 中我们可以通过 tasks 属性访问 TaskContainer,以访问集合元素的方式访问我们创建的任务:

task ex42AccessTask2
tasks['ex42AccessTask2'].doLast {println 'ex42AccessTask2.doLast'
}

访问时,任务名就是 Key(关键索引)。 其实这里说 Key 不恰当,因为 tasks 并不是Map 。这里再顺便扩展一下 Groovy 的知识,[]Groovy中是操作符,我们知道 Groovy的操作符有对应方法让我们重载, a[b]对应的是a.getAt(b )这个方法,对应的例tasks[''42AccessTask2'] 其实就是调用 tasks.getAt('ex42AccessTask2')这个方法 如果我们查看Gradle代码的源码最后发现调用 findByName(String name)实现的。

  1. 通过路径访问。get找不到任务就会抛出 UnknownTaskException异常 ,而 find找不到该任务的时候会返 null:

task ex42AccessTask3
tasks['ex42AccessTask3'].doLast {println tasks.findByPath(':example42:ex42AccessTask3')println tasks.getByPath(':example42:ex42AccessTask3')println tasks.findByPath(':example42:asdfasdfasdf')
}
  1. 通过名称访问。getfind区别同上。值得强调的是,通过路径访问的时候, 参数值可以是任务路径也可以是任务的名字。但通过名字访问,参数值只能是任务的名称,不能为路径。

task ex42AccessTask4
tasks['ex42AccessTask4'].doLast {println tasks.findByName('ex42AccessTask4')println tasks.getByName('ex42AccessTask4')}

任务分组和描述

任务是可以分组和添加描述的,任务的分组其实就是对任务的分类,便于我们对任务进行归类整理,这样清晰明了 任务的描述就是说明这个任务有什么作用,是这个任务的大概说明。这在使用tasks命令行或Android Studio工具查看时会更直观。

def Task myTask = task ex43GroupTask
myTask.group = BasePlugin.BUILD_GROUP
myTask.description = '这是一个构建的引导任务'myTask.doLast {println "group:${group},description:${description}"
}

<< 操作符

<<操作符在 Gradle Task 上是 doLast 方法的短标记形式,也就是说<<可以代替doLast<< 对应的是 a.leftShift(b)

/*** <p>Adds the given closure to the end of this task's action list.  The closure is passed this task as a parameter* when executed. You can call this method from your build script using the &lt;&lt; left shift operator.</p>** @param action The action closure to execute.* @return This task.** @deprecated Use {@link #doLast(Closure action)}*/@DeprecatedTask leftShift(Closure action);

任务执行顺序

def Task myTask = task ex45CustomTask(type: CustomTask)
myTask.doFirst{println 'Task执行之前执行 in doFirst'
}
myTask.doLast{println 'Task执行之后执行 in doLast'
}class CustomTask extends  DefaultTask {@TaskActiondef doSelf() {println 'Task自己本身在执行 in doSelf'}}

Gradle会解析其带有TaskAction 注解的方法作为 Task要执行的 Action。然后通过 TaskprependParallelSafeAction方法将 Action 加到 actionsList中。而doFirstdoLast的原理其实就是将Action插入到List的最前面和最后面。

任务排序

通过Task的属性,shouldRunAftermustRunAfter干预任务的执行顺序而并非我们理解的任务排序。

task ex46OrderTask1 << {println 'ex46OrderTask1'
}task ex46OrderTask2 << {println 'ex46OrderTask2'
}ex46OrderTask1.mustRunAfter ex46OrderTask2

任务的启用和禁用

使用Taskenabledisable属性

task ex47DisenabledTask << {println 'ex47DisenabledTask'
}ex47DisenabledTask.enabled =false

任务的onlyIf断言

断言就是一个条件表达式。 Task 一个onlyIf方法,它接受一个闭包作为参数如果该闭包返回 true该任务执行,否则跳过。这有很多用途,比如控制程序哪些情况下打什么包,什么时候执行单元测试,什么情况下执行单元测试的时候不执行网络测试等。

final String BUILD_APPS_ALL="all";
final String BUILD_APPS_SHOUFA="shoufa";
final String BUILD_APPS_EXCLUDE_SHOUFA="exclude_shoufa";task ex48QQRelease << {println "打应用宝的包"
}
task ex48BaiduRelease << {println "打百度的包"
}
task ex48HuaweiRelease << {println "打华为的包"
}task ex48MiuiRelease << {println "打Miui的包"
}task build {group BasePlugin.BUILD_GROUPdescription "打渠道包"
}build.dependsOn ex48QQRelease,ex48BaiduRelease,ex48HuaweiRelease,ex48MiuiReleaseex48QQRelease.onlyIf {def execute = false;if(project.hasProperty("build_apps")){Object buildApps = project.property("build_apps")if(BUILD_APPS_SHOUFA.equals(buildApps)|| BUILD_APPS_ALL.equals(buildApps)){execute = true}else{execute = false}}else{execute = true}execute
}ex48BaiduRelease.onlyIf {def execute = false;if(project.hasProperty("build_apps")){Object buildApps = project.property("build_apps")if(BUILD_APPS_SHOUFA.equals(buildApps)|| BUILD_APPS_ALL.equals(buildApps)){execute = true}else{execute = false}}else{execute = true}execute
}ex48HuaweiRelease.onlyIf {def execute = false;if(project.hasProperty("build_apps")){Object buildApps = project.property("build_apps")if(BUILD_APPS_EXCLUDE_SHOUFA.equals(buildApps)|| BUILD_APPS_ALL.equals(buildApps)){execute = true}else{execute = false}}else{execute = true}execute
}ex48MiuiRelease.onlyIf {def execute = false;if(project.hasProperty("build_apps")){Object buildApps = project.property("build_apps")if(BUILD_APPS_EXCLUDE_SHOUFA.equals(buildApps)|| BUILD_APPS_ALL.equals(buildApps)){execute = true}else{execute = false}}else{execute = true}execute
}

打包时,只需要在命令行(或者新建task设置property,调用Task#setProperty(name,value)):

#打所有渠道包
. /gradlew :example48:build 
. /gradlew -Pbuild_apps=all :example48 : build 
#打首发包
. /gradlew -Pbuild_apps=shoufa :example48 : build 
#打非首发包
./gradlew -Pbuild_apps=exclude shoufa :example48:build

其中,命令行中-P意思是为Project指定K-V格式的属性键值对,使用格式为-PK=V。(命令行task -h可查看用法)

任务规则

通过TaskContaineraddRule()方法添加规则。

Rule addRule(Rule var1);
Rule addRule(String var1, Closure var2);

当我们执行、依赖一个不存在的任务时, Gradle会执行失败,失败信息是任务不存在。我们使用规则对其进行改进,当执行、依赖不存在的任务时,不会执行失败,而是打印提示信息,提示该任务不存在:

tasks.addRule("对该规则的一个描述,便于调试、查看等") { String taskName ->task(taskName) << {println "该${taskName}任务不存在,请查证后再执行"}
}task ex49RuleTask {dependsOn missTask//此任务不存在,会打印上面的提示
}

第五章 Gradle插件

插件作用

  • 可以添加任务到你的项目中,帮你完成一些事情,比如测试、编译、打包
  • 可以添加依赖配置到你的项目中,我们可以通过它们配置我们项目在构建过程中需要的依赖,如我们编译的时候依赖的第三方库等。
  • 可以向项目中现有的对象类型添加新的扩展属性、方法等,让你可以使用它们帮助我们配置、优化构建,比如 android {}这个配置块就是Android Gradle插件为Project对象添加的 个扩展。
  • 可以对项目进行一些约定,比如应用 Java件之后,约定 src/main/java目录下是我们的源代码存放位置,在编译的时候也是编译这个目录下的 Java 源代码文件。

使用插件

  1. 二进制插件
    二进制插件就是实现了org.gradle.api.Plugin接口的插件。

apply plugin:'java'
apply plugin:org.gradle.api.plugins.JavaPlugin
apply plugin:JavaPlugin

上面三种写法是等价的。对于Gradle内置的二进制插件,都有一个容易记的短名,成为plugin id,如上面的java。非内置的需要使用全路径名导入,就是第二种写法。而包 org.gradle.api.plugins又是Gradle默认导入的,所以可以省去,也就是第三种写法。

  1. 脚本插件

apply from:'version.gradle'task ex52PrintlnTask << {println "App版本是:${versionName},版本号是:${versionCode}"
}

其实这不能算一个插件,它只是一个脚本。应用脚本插件,其实就是把这个脚本加载进来,和二进制插件不同的是它使用的是from关键字,后面紧跟的是 个脚本文件,可以是本地的,也可以是网络存在的,如果是网络上的话要使用 HTTP URL
虽然它不是一个真正的插件,但是不能忽视它的作用,它是脚本文件模块化的基础,我们可以把庞大的脚本文件,进行分块、分段整理,拆分成一个个共用、职责分明的文件,然后使apply from 来引用它们,比如我们可以把常用的函数放在一个 utils.gradle 脚本里,供其他脚本文件引用。

  1. 第三方插件
    第三方插件需要先使用buildscript{}进行配置。更多的第三方插件可以通过https://plugins.gradle.org/查找。

自定义插件

如果仅在自身项目中使用,可以在build文件中继承Plugin类实现apply()接口。

apply plugin: Ex53CustomPluginclass Ex53CustomPlugin implements Plugin<Project> {void apply(Project project) {project.task('ex53CustomTask') << {//创建了一个任务供导入方使用println "这是一个通过自定义插件方式创建的任务"}}
}

现在我们就可以用也gradlew :example53 :ex53CustomTask 来执行这个任务,这个任务是我们通过自定义插件创建的。

如果我们想开发个独立的插件给所有想用的人怎么做呢?这就需要我们单独创建一个Groovy 工程作为开发自定义插件的工程了。

  1. 创建 Groovy 工程,然后配置我们插件开发所需的依赖:

apply plugin: 'groovy'dependencies {compile gradleApi()compile localGroovy()
}
  1. 实现插件类

# Ex53CustomPlugin.groovy
package com.github.rujews.pluginsimport org.gradle.api.Plugin
import org.gradle.api.Projectclass Ex53CustomPlugin implements Plugin<Project>{@Overridevoid apply(Project target) {target.task('ex53CustomTask') << {println "这是一个通过自定义插件方式创建的任务"}}
}
  1. 因为每个插件都有一个唯一的 plugin id,所以需要在META-INFO文件夹里的properties 文件来定义对应插件实现类。在src/main/resources/META-INFO/gradle-plugins目录下新建[plugin_id].properties文件,其中,文件名就是plugin id。在文件中写入:

implementation-class=com.github.rujews.plugins.Ex53CustomPlugin

keyimplementation class 固定不变, value 就是我们自定义的插件的实现类,上面的例子中就是com.github.rujews.plugins.Ex53CustomPlugin 。现在都配置好了,我们就可以生成一个 jar 包分发给其他人使用我们的插件了.

buildscript { dependencies { classpath files (’ libs/example53 . jar’) }
}
apply plugin:'com.github.rujews.plugins.ex53customplugin'

第六章 Java Gradle插件

构建Java项目

执行build这个任务,输出如下:

main
test
:example64:compileJava
:example64:processResources
:example64:classes
:example64:jar
:example64:assemble
:example64:compileTestJava UP-TO-DATE
:example64:processTestResources UP-TO-DATE
:example64:testClasses UP-TO-DATE
:example64:test UP-TO-DATE
:example64:check UP-TO-DATE
:example64:build

从上面可以看出这个任务在执行过程中都做了什么,最后在build/libs生成jar包。clean任务则是删除build目录以及其他构建生成的文件。
assemble 任务,该任务不会执行单元测试,只会编译和打包。这个任务在 Android里也有,执行它可以打 apk 包,所以它不止会打 jar 包,其实它算是一个引导类的任务,根据不同的项目类型打出不同的包。
check 任务,它只 执行单元测试,有时候还会做一些质量检查,不会打 jar 包,也是个引导任务。

源码集合(SourceSet)

Java插件在Project下为我们提供了一个sourceSets属性以及 sourceSets{}闭包来访问和配置源集。sourceSets{}闭包配置的都是 SourceSet对象。

apply plugin:'java'sourceSets {main {//这里对main SourceSet配置}
}task ex65SourceSetTask {sourceSets.all{println name}
}

常用源集属性

Java 插件添加的通用任务

源集任务


运行任务的时候,列表中的任务名称中 sourceSet 要换成你的源集的名称,比如 main源集的名称就是compileMainJava

Java插件添加的属性

Java 插件添加了很多常用的属性,这些属性都被添加到Project 中,我们可以直接使用比如前面己经用到的 sourceSets:

Jav 插件添加的源集属性

多项目构建

多项目构建,其实就是多个 Gradle 项目一起构建。

目录结构


如上图目录结构,同一个模块下也可以在setting.gradle中进行配置以达到模块化的作用,供外部依赖使用。

include ':example68app'
project(':example68app').projectDir = new File(rootDir, 'chapter06/example68/app')
include ':example68base'
project(':example68base').projectDir = new File(rootDir, 'chapter06/example68/base')

这样,我们可以在app项目中使用base项目了:

apply plugin:'java'dependencies {compile project(':example68base')
}

发布构建

Gradle 构建的产物,我们称之为构件。一个构件可以是一个 Jar ,也可以是一个 Zip或者 WAR等。

  1. 定义发布的构件

apply plugin:'java'task publishJar(type: Jar)
artifacts {archives publishJar //通过一个 Task 来为我们发布提供构件
}

也可以发布文件:

def publishFile = file ( ’ build/buildile ’) 
artifacts { archives publishFile
}
  1. 发布构件,也就是上传构件到指定的地方。可以是一个指定的目录,Maven库等。

apply plugin:'java'
apply plugin:'maven'task publishJar(type: Jar)group 'org.flysnow.androidgradlebook.ex69'
version  '1.0.0'artifacts {archives publishJar
}uploadArchives {repositories {flatDir {name 'libs'dirs "$projectDir/libs" //发布到本地libs目录}mavenLocal()//发布到本地Maven库,也就是用户目录的.m2/repository文件夹mavenDeployer {repository(url: "http://repo.mycompany.com/nexus/content/repositories/releases") {authentication(userName: "usrname", password: "pwd")}snapshotRepository(url: "http://repo.mycompany.com/nexus/content/repositories/snapshots") {authentication(userName: "usrname", password: "pwd")}}}
}

第七章 Android Gradle插件

Android Gradle插件的分类

App 插件 id: com.android.application
Library 插件 id: com.android.library
Test 插件 id: com.android.test

Android Gradle 工程

  1. Android Gradle 工程的配置,都是在 android{}中,这个是唯一的入口。其具体实现是在com.android build.gradle.AppExtension,是一个Project的扩展。参考源码,以及Doc文档。
    其创建原型:

extension = project.extensions.create ('android', getExtensionClass() , 
(Projectlnternal) project, instantiator, androidBuilder, sdkHandler, 
buildTypeContainer, productFlavorContainer, signingConfigContainer, 
extraModelinfo, isLibrary())
  1. defaultConfigdefaultConfig 是默认的配置,它是一个ProductFlavor

/*** The default configuration, inherited by all build flavors (if any are defined).
*/
public void defaultConfig(Action<ProductFlavor> action) {checkWritability();action.execute(defaultConfig);
  1. buildTypesbuildTypes是一个NamedDomainObjectContainer 类型,是一个域对象。这个和 SourceSet一样。buildTypes里面有release debug 等。我们可以在 buildTypes{}里新增任意多个我们需要构建的类型,比如debug,Gradle 会帮我们自动创建一个对应 BuildType ,名字就是我们定义的名字。
    proguardFiles 方法可以接受一个可变参数。所以我们可以同时配置多个配置文件。

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

getDefaultProguardFileAndroid 扩展的方法,它可以获取你的Android SDK录下默认proguard 配置文件。在 android-sdk/tools proguard/目录下,文件名就是我们传入的参数名字 proguard-android.txt

Android Gradle任务

Android插件是基于Java插件的,因此 Android插件基本上包含了所有Java插件的功能,包括继承的任务,比如 assemble check build等。除此之外, Android在大类上还添加了connectedCheck deviceCheck lint install uninstall等任务,这些是属于Android特有功能。具体作用可以参考java_plugin_tasks说明。

第八章 自定义Android Gradle工程

defaultConfig默认属性

defaultConfig{}配置中applicationldminSdkVersionversionCode,versionName属性在没有配置时,会去manifest文件中查找。

配置签名信息

一个 SigningConfig就是一个签名配置,其可配置的元素如下:storeFile签名证书文件, storePassword签名证书文件的密码,storeType 签名证书的类型, keyAlias 签名证书中密钥别名, keyPassword 签名证书中该密钥的密码。
Android SDK自动生成的debug证书。它一般位于$HOME/.android/debug.keystore路径下。

启用zipalign优化

zipalign是Android 为我们提供的一个整理优化 apk文件的工具,它能提高系统和应用的运行效率,更快地读写apk中的资源,降低内存的使用。所以对于要发布的App,在发布之前一定要使用 zipalign 进行优化。

第九章 Android Gradle高级自定义

使用共享库

那些不包含在Android SDK库里,比如 com.google.android.maps,android.test.runner等,这些库是独立的,并不会被系统自动链接,所以我们要使用它们的话,就需要单独进行生成使用,这类库我们称为共享库。
AndroidManifest 文件中,我们可以指定要使用的库:

<uses-library 
android:name=” com.google.android.maps” 
android:required="true" />

这样我们就声明了需要使用maps这个共享库。声明之后,在安装生成的apk包的时候,系统会根据我们的定义,帮助检测手机系统是否有我们需要的共享库 。因为我们设置的android:required="true",如果子机系统不满足,将不能安装该应用。
Android中,除了标准的SDK还存在两种库: 一种是 add-ons 库,它们位于 add-ons目录下,,这些库大部分是第三方厂商或者公司开发的, 一般是为了让开发者使用,但是又不想暴露具体标准实现的;第二类是optional可选库,它们位于 platforms/android-xx/optional 目录下,一般是为了兼容旧版本的 API,比如org.apache.http.legacy ,这是 HttpClient的库 。从
API 23 开始,标准的 Android SDK 中不再包含 HttpClient 库,如果还想使用 HttpClient 库,就必须使用org.apache.http.legacy这个可选库。后者不会自动添加到我们的classpath里。需要使用下面的方法:

android {useLibrary 'org.apache.http.legacy'
}

批量修改apk名

applicationVariants.all { variant ->variant.outputs.each { output ->if (output.outputFile != null && output.outputFile.name.endsWith('.apk')&&'release'.equals(variant.buildType.name)) {def flavorName = variant.flavorName.startsWith("_") ? variant.flavorName.substring(1) : variant.flavorNamedef apkFile = new File(output.outputFile.getParent(),"Example92_${flavorName}_v${variant.versionName}_${buildTime()}.apk")output.outputFile = apkFile}}}

Android对象为我们提供了 个属性:applicationVariants(仅仅适用于 Android应用 Gradle 插件) , libraryVariants( 仅仅适用于 AndroidGradle插件),testVariants(以上两种Gradle插件都适用)。
ps:
allgradleDomainObjectCollection 接口的方法。
eachgroovyListMap 等集合类的方法。
它们的区别:
1.all 会对集合内现有的元素和之后加入的元素,都执行给定的闭包操作。
each 只会对集合内现有的元素执行给定的闭包操作。
2.all接收的闭包,可以直接访问集合内元素的属性和方法。
each接收的闭包,不能直接访问集合内元素的属性和方法。
原因:
all 接收的闭包是一个配置闭包,集合内元素既会作为参数传给这个闭包,也会被设为闭包的委托对象,这样闭包就可以直接访问委托对象(集合内元素)的属性和方法。
each 接收的是一个普通闭包,集合内元素只会作为参数传给这个闭包。

从git获取apk的版本信息

/*** 以git tag的数量作为其版本号* @return tag的数量*/
def getAppVersionCode(){def stdout = new ByteArrayOutputStream()exec {commandLine 'git','tag','--list'standardOutput = stdout}return stdout.toString().split("\n").size()
}/*** 从git tag中获取应用的版本名称* @return git tag的名称*/
def getAppVersionName(){def stdout = new ByteArrayOutputStream()exec {//执行shell命令commandLine 'git','describe','--abbrev=0','--tags'standardOutput = stdout}return stdout.toString().replaceAll("\n","")
}

动态配置manifest

productFlavors.all { flavor ->manifestPlaceholders.put("UMENG_CHANNEL",name)}

自定义BuildConfig

productFlavors {google {buildConfigField 'String','WEB_URL','"http://www.google.com"'}baidu {buildConfigField 'String','WEB_URL','"http://www.baidu.com"'}}

动态添加自定义资源

 productFlavors {google {resValue 'string','channel_tips','google渠道欢迎你'}baidu {resValue 'string','channel_tips','baidu渠道欢迎你'resValue 'color','main_bg','#567896'resValue 'integer-array','months','<item>1&lt/item>'}}

其效果和在 res/values 文件中定义一个资源是等价的。最终生成的值可以在build/generated/res/resValues/[productFlavor]/[buildType]/ values/generated.xml中找到。

Java编译选项

compileOptions { encoding = ’ utf-8 ’ sourceCompatibility = JavaVersion.VERSION_1_6targetCompatibility = JavaVersion.VERSION_1_6
}

adb 操作选项配置

adbOptions { timeOutinMs = 5*1000//秒installOptions '-r','-s'
}

adb installl,r,t,s,d,g六个选项。-l 锁定该应用程序。-r:替换己存在的应用程序,也就是我们说的强制安装 ;-t :允许测试包; -s: 也把应用程序安装到SD卡上。-d: 允许进行降级安装,也就是安装的程序比于机上带的版本低。-g: 为该应用授予所有运行时的权限。

自动清理未使用的资源

  1. 手动清除
  2. Android Lint工具
  3. Resource Shrinking,需要配合Code Shrinking一起使用。

release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile ('proguard-android.txt '),'proguard�rules.pro'
}

如果想看详细日志,想知道哪些资源被自动清理了,可以使--info 标记。

./gradlew clean:example912:assembleRelease --info I grep "unused resource "

自动清理未使用的资源这个功能虽好,但是有时候会误删有用的程 ,为什么呢? 因为我在代码编写的时候,可能会使用反射去引用资源文件,尤其很多你引用的第三方库会这么做,这时候Android Gradle 就区分不出来了,可能会误认为这些资源没有被使用。针对这种情况,Android Gradle 提供了keep方法来让我们配置哪些资源不被清理。
keep方法使用非常简单,我们要新建xml 文件来配 ,这个文件 res/raw/keep.xml,然后通过 tools:keep 属性来配置。这个tools:keep接受一个以逗号(,)分割的配置资源列表,并且支持星号(*)通配符。

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools http://schemas.android.com/tools” tools:keep= @layout/used*_c,@layout/l_used_a,@layout/l_used_b* />
  1. resConfigs方法

defaultConf { ...resConfigs 'zh'
}

第十章 Android Gradle多项目构建

库项目的引用和配置

默认情况下, Android 库项目发布出来的包都是release版本的,当然可以通过配置来改变它,比如改成默认发布的,这就是 debug 版本的:

android{defaultPublishConfig "flavor1Debug"//flavor+buildtype
}

如果要针对不同的版本,引用不同的发布的 aar

android{publishNonDefault true
}

在引用时使用以下方法:

dependencies { flavor1Compile project(path:':lib1', configuration:'flavorlRelease') flavor2Compile project(path :':lib1', configuration:'flavor2Release')
}

库项目发布到Nexus

应用过Maven插件之后,我们需要配置 Maven构建的 要素,它们分别是 group:artifact:version。使用 groupversion 比较方便,直接指定即可。应用 version 还要理解一个概念,快照版本 SNAPSHOT ,比如配置成 1.0.0-SNAPSHOT ,这时候就会发布到 snapshot 中心库里,每次发布版本号不会变化,只会在版本号后按顺序号+1 ,比如 1.0 0-1, 1.0.0-2, 1.0.0-3 等。类似于这样的版本号,我们引用的时候版本号写成 1.0.0-SNAPSHOT 即可, Maven 会帮我们下载最新(序号最大的)的快照版本 。这种方式适用于联调测试的时候,每次修复好测试的问题就发布一个快照版本,直到没有问题为止,然后再放出 release 版本,正式发布。

uploadArchives {repositories.mavenDeployer {name = 'mavenCentralReleaseDeployer' //名称固定repository(url: "http://xxx/nexus/repository/releases/") { //仓库地址authentication(userName: "admin", password: "admin") //仓库用户名和密码}snapshotRepository (url: "http://xxx/nexus/repository/snapshots/") { //仓库地址authentication(userName: "admin", password: "admin") //仓库用户名和密码}pom.version = '3.5.23-dev'  //版本号pom.artifactId = "test" //aar名称pom.groupId = "com.example.abc" //包名pom.name = "sdk"pom.packaging = 'aar' //打包格式,aar固定}
}

然后配置好仓库的地址告知Gradle

allprojects {repositories {...maven {url "http://xxx/nexus/content/groups/xxx/"}}

第十一章 Android多渠道构建

多渠道构建定制

  1. consumerProguardFiles。既是一个属性,也有一个同名的方法,它只对 Android库项目有用。它使得混淆文件列表也会被打包到aar里一起发布 ,这样 当应用项目 引用这个 aar 包, 并且启用混淆的时候,会自动使用aar包里的混淆文件对 aar 里的代码进行混淆 ,这样我们就不用对该 aar 包进行混淆配置了。

android { productFlavors { google { consumerProguardFiles 'proguard-rules.pro','proguard-rules.txt'}}
}
  1. flavorDimensionsProductFlavor的一个属性,接受一个字符串,作为该 ProductFlavor的维度。

flavorDimensions "abi", "version"productFlavors {free {dimension 'version'}paid {dimension 'version'}x86 {dimension 'abi'}arm {dimension 'abi'}}

提高多渠道构建的效率

参考美团技术的方法:利用了在 apkMETA-INF 目录下添加空文件不用重新签名的原理,非常高效,其大概就是:

  1. 利用 Android Gradle打一个基本包(母包);
  2. 然后基于该包复制一个,文件名要能区分产品 、打包时间、版本、渠道等;
  3. 然后对复制出来的 apk 文件进行修改,在其 META-INF 目录下新增空文件,但是空文件的文件名要有意义,必须包含能区分渠道的名字 ,如 mtchannel _google;
  4. 重复步骤2 、步骤3生成我们所需的所有渠道包 apk ,这个可以使用 Python 这类脚本来做;
  5. 这样就生成了我们所有发布渠道的 apk 包了。

相关文章:

Android Gradle权威指南读书笔记

第一章 Gradle入门 生成Gradle Wrapper 命令&#xff1a;gradle wrapper --gradle-version 版本号自定义Gradle Wrapper task wrapper(type : Wrapper) { gradleVersion 2.4 archiveBase GRADLE USER HOME archivePath wrapper/dists distributionBase GRADLE USER HOME …...

顺子日期(蓝桥杯)

文章目录 顺子日期问题描述答案&#xff1a;14字符串解题CC语言指针C语言函数 数组解题 顺子日期 问题描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 小明特别喜欢顺子。顺子指的就是连续的三个数字&#xff1a;123、…...

攻防世界web篇-unserialize3

得出php代码残篇 将代码补全后再在线php运行工具中进行运行 在浏览器输入后得到下面的界面 这里需要将O:4:“xctf”:1:{s:4:“flag”;s:3:“111”;} 改为 O:4:“xctf”:2:{s:4:“flag”;s:3:“111”;}...

微信小程序 onLoad和onShow的区别

在微信小程序中&#xff0c;onLoad() 和 onShow() 是两个常用的生命周期函数&#xff0c;用于监听页面的加载和显示事件。这两个函数的区别如下&#xff1a; 触发时机 onLoad() 函数只会在页面加载时触发一次&#xff0c;而 onShow() 函数每次页面显示时都会被触发。因此&#…...

elementui select组件下拉框底部增加自定义按钮

elementui select组件下拉框底部增加自定义按钮 el-select组件的visible-change 事件&#xff08;下拉框出现/隐藏时触发&#xff09; <el-selectref"select":value"value"placeholder"请选择"visible-change"visibleChange">&…...

深入理解闭包:原理、应用与最佳实践

1、 什么是闭包&#xff1f; 如果一个函数内部定义了另一个函数&#xff0c;并且内部函数引用了外部函数的变量&#xff0c;那么内部函数就形成了一个闭包。 def outer_function(x):# 外部函数接受一个参数 x 是自由变量# seed 也是一个自由变量seed 10def inner_function(y…...

[NSSCTF 2nd]Math

原题py&#xff1a; from secret import flag from Crypto.Util.number import * import gmpy2length len(flag) flag1 flag[:length//2] flag2 flag[length//2:] e 65537m1 bytes_to_long(flag1) p getPrime(512) q getPrime(512) n p*q phi (p-1)*(q-1) d gmpy2.i…...

uml知识点学习

https://zhuanlan.zhihu.com/p/659911315https://zhuanlan.zhihu.com/p/659911315软件工程分析设计图库目录 - 知乎一、结构化绘图1. 结构化——数据流图Chilan Yuk&#xff1a;1. 结构化——数据流图2. 结构化——数据字典Chilan Yuk&#xff1a;2. 结构化——数据字典3. 结构…...

JAVA学习日记1——JAVA简介及第一个java程序

简单记忆 JAVA SE &#xff1a;标准版&#xff0c;核心基础 JAVA EE&#xff1a;企业版&#xff0c;进阶 JDK&#xff1a;Java Development Kit&#xff0c;Java开发工具包&#xff0c;包含JRE JRE&#xff1a;Java Runtime Environment&#xff0c;Java运行时环境&#xff…...

Linux命令(102)之less

linux命令之less 1.less介绍 linux命令less是一个文本文件查看工具&#xff0c;它以一种交互的方式&#xff0c;逐页地显示文本文件的内容&#xff0c;并且可以在文件中进行搜索等定位 2.less用法 less [参数] filename less参数 参数说明-N显示每行的行号-i忽略搜索时的大…...

vue多条件查询

<template><div><input type"text" v-model"keyword" placeholder"关键字"><select v-model"category"><option value"">所有分类</option><option v-for"cat in categories&q…...

c 语言基础:L1-038 新世界

这道超级简单的题目没有任何输入。 你只需要在第一行中输出程序员钦定名言“Hello World”&#xff0c;并且在第二行中输出更新版的“Hello New World”就可以了。 输入样例&#xff1a; 无输出样例&#xff1a; Hello World Hello New World 程序源码&#xff1a; #incl…...

计算机算法分析与设计(13)---贪心算法(多机调度问题)

文章目录 一、问题概述1.1 思路分析1.2 实例分析 二、代码编写 一、问题概述 1.1 思路分析 1. 设有 n n n 个独立的作业 1 , 2 , … , n {1, 2, …, n} 1,2,…,n&#xff0c;由 m m m 台相同的机器 M 1 , M 2 , … , M m {M_1, M_2, …, M_m} M1​,M2​,…,Mm​ 进行加工处…...

小程序canvas层级过高真机遮挡组件的解决办法

文章目录 问题发现真机调试问题分析问题解决改造代码效果展示 问题发现 在小程序开发中需要上传图片进行裁剪&#xff0c;在实际真机调试中发现canvas层遮挡住了生成图片的按钮。 问题代码 <import src"../we-cropper/we-cropper.wxml"></import> <…...

番外8.1 配置+管理文件系统

Task01: Linux 文件系统结构&#xff1b; 可以进行Linux操作系统的文件权限管理与方式切换&#xff0c;可以应用磁盘与文件权限管理工具&#xff1b; 01&#xff1a;常见文件系统类型&#xff08;Ext4[rhel6默认文件管理系统], 存储容量1 EB1073741824 GB; XFS[rhel 7/8默认的文…...

互联网Java工程师面试题·Java 总结篇·第八弹

目录 72、用 Java 的套接字编程实现一个多线程的回显&#xff08;echo&#xff09;服务器。 73、XML 文档定义有几种形式&#xff1f;它们之间有何本质区别&#xff1f;解析XML 文档有哪几种方式&#xff1f; 74、你在项目中哪些地方用到了 XML&#xff1f; 72、用 Java 的套…...

VSCode修改扩展和用户文件夹目录位置(Windows)

VSCode修改扩展和用户文件夹目录位置&#xff08;Windows&#xff09; 前言&#xff1a;方法前期准备&#xff1a;方法1&#xff08;强推荐&#xff09;方法2&#xff08;不太推荐&#xff09;方法3&#xff08;好麻烦&#xff0c;不太推荐&#xff09; 前言&#xff1a; VSCod…...

Spring 事务

文章目录 实现CURD&#xff08;没加入事务前&#xff09;1.加入依赖2.创建jdbc.properties3.配置Spring的配置文件4.数据库与测试表 基于注解的声明式事务准备工作测试模拟场景 加入事务①添加事务配置 Transactional注解标识的位置只读事务属性&#xff1a;超时事务属性&#…...

无法访问 github ,解决办法

一、使用代理&#xff08;首选&#xff09; 这种办法只需要更改github.com为代理的域名即可&#xff0c;使用方式与GitHub除了域名不同其他都一样&#xff0c;速度挺快&#xff0c;可登陆&#xff0c;可提交。 1、查看当前的代理&#xff1a; git config --global --get htt…...

SD卡与emmc的异同

eMMC与SD卡的异同&#xff1a; 物理尺寸和接口&#xff1a; eMMC&#xff1a;eMMC是一种嵌入式存储解决方案&#xff0c;通常采用BGA&#xff08;Ball Grid Array&#xff09;封装&#xff0c;焊接在电路板上。它没有标准的物理尺寸&#xff0c;而是以芯片的形式存在。SD卡&…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...