读书笔记--Android Gradle权威指南(上)

本篇文章已授权微信公众号 dasu_Android(大苏)独家发布

最近看了一本书《Android Gradle 权威指南》,对于 Gradle 理解又更深了,但不想过段时间就又忘光了,所以打算写一篇读书笔记,将书中一些我个人觉得蛮有用的点记录、总结一下。

前言

首先,先来过一下整书的目录章节,先大概清楚整本书都介绍了哪些知识点:

第 1 章 Gradle 入门

第 2 章 Groovy 基础

第 3 章 Gradle 构建脚本基础

第 4 章 Gradle 任务

第 5 章 Gradle 插件

第 6 章 Java Gradle 插件

第 7 章 Android Gradle 插件

第 8 章 自定义 Android Gradle 工程

第 9 章 Android Gradle 高级自定义

第 10 章 Android Gradle 多项目构建

第 11 章 Android Gradle 多渠道构建

第 12 章 Android Gradle 测试

第 13 章 Android Gradle NDK 支持

第 14 章 Android Gradle 持续集成

整本书介绍的内容很全,从 Gradle 的环境配置 --> Groovy 介绍 --> 讲解项目中常见 gradle 文件作用 (setting.gradle, build.gradle) --> 详细讲解 build.gradle 文件内每行代码的含义 --> 各种高级自定义使用。

看完这本书,对于掌握项目中的 build.gradle 文件应该就不成问题了,虽然将整本书过了一遍,但其实我也只是着重挑了一些自己感兴趣的章节深入阅读,所以就来记录一下,方便后续查阅吧。

笔记

1. Groovy 基础

首先清楚一点,Gradle 是基于 Groovy 语言的,他们之间的关系就像《Android 群英传:神兵利器》中说的:

Groovy 对于 Gradle,就好比 Java 对于 Android

所以,了解一些 Groovy,对于学习 Gradle 来说,肯定是有所帮助的。

关于这方面内容,我之前写过一篇博客:学点Groovy来理解build.gradle代码

所以,这里不会再去介绍,但有几个点可以提一下,如果你都还不怎么熟悉,那么可以点开链接去看看:

  • Groovy 中支持用 'xxx',"xxx",'''xxx''',/xxx/ 等多种方式来定义字符串,所以如果在 build.gradle 里看到既有单引号又有双引号定义的字符串时,不用去疑惑他们到底是不是字符串。
  • Groovy 中的方法支持省略括号,也就是说,在 build.gradle 中一行行的代码,大部分都是在调用某个方法。
  • Groovy 中有一种特性叫闭包,说白点也就是代码块,支持作为方法参数,结合方法括号省略的特点,在 build.gradle 里 defauleConfig {} 代码块之类的其实也都是在调用一个个方法。

2. Android 项目中的 Gradle

项目结构.png

新建一个项目时,Android Studio 会自动生成项目的初步结构,这通常会携带一些 gradle 相关的文件,这一节就来学学,各个 gradle 文件都有什么作用

2.1 gradle/wrapper 目录

就像我们要开发 Java 程序,本地需要配置 JDK 环境,要开发 Android 程序,需要配置 SDK 一样,想要借助 Gradle 来构建项目,那么按理说本地也需要配置相关的 Gradle 环境才对。

而我们之所以可以省掉这一步,就是 gradle/wrapper 这个目录下的文件的作用了,可以先看看 gradle-wrapper.properties 这个文件的内容:

#Thu May 24 10:30:42 CST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip

内容无非就是一些配置项,而最重要的就是最后一句,指明了当前这个项目要使用哪个版本的 Gradle 来构建,我们在 Android Studio 的 File -> Project Structure -> Project 里配置的 Gradle Version,最终改变的其实就是上述文件里最后一行的 Gradle 版本属性值

AS的Gradle版本配置.png

官方说了,提供了 gradle/wrapper 这种方式,可以让你特别灵活的进行配置,想换个 Gradle 版本来构建项目,只需要修改这个配置文件的 Gradle 版本属性值即可,当然也可以直接通过 AS 提供的 UI 界面操作,结果都一样。

由于 Gradle 更新换代特别快,而且新的大版本经常都会提供很多新特性,这就导致了在 clone Github 上一些开源项目到本地构建时经常有报错的问题,本质原因就是因为它使用的 Gradle 版本跟你本地不一样,而由于有堵巨墙的原因,导致一直没法成功下载它配置的 Gradle 版本,进而就无法构建项目,而报错了。

网上说的一些解决方案是让你手动去修改 gradle-wrapper.properties 文件里的 Gradle 版本,改成你本地的版本,但我觉得这种方法不一定适用,这取决于那个项目中是否有用到一些新特性,以及你本地的 Gradle 版本是否兼容项目中用到的 Gradle 新特性。

通常来说,如果你本地的 Gradle 比克隆的项目的 Gradle 版本高的话,那么这种直接修改项目的 Gradle 版本方式应该是可行的,那么怎么知道你本地都有哪些 Gradle 版本呢:

本地Gradle版本.png

默认在 C 盘, C:\Users\suxq .gradle 目录下有 Android Studio 自动帮你下载的 Gradle 的各个版本,只要你在 gradle-wrapper.properties 修改了 Gradle 的版本号,那么当你在构建项目时,就会先到你电脑的这个路径下查找相对应版本的 Gradle,如果可用,则直接进行构建项目任务,如果不存在,那么就会自动去下载对应版本的 Gradle。

最后,还有个问题,怎么确定都有哪些 Gradle 版本可用呢?如果想要自己去下载,不借助 Android Studio 可行么?

当然可以,去官网找到对应版本点击下载即可:http://services.gradle.org/distributions/

下载完成之后,将文件放到上面介绍的 C 盘下的 .gradle 文件里相对应版本的目录下即可。

如果你有自己去尝试下载,你就会体验到,下载速度是有多么的龟速,90M 左右的文件硬是要下载个把小时。同样的道理,你自己下载这么慢,那通过 Android Studio 下载的速度也同样这么慢,两者唯一的区别就只是在于你自己下载时你可以看到速度和进度。

这样一来的话,明白为什么有时候打开新项目或者打开 Github 上的项目时,Android Studio 会一直卡在构建中的原因了吧?

因为这个项目用到了你本地没有的 Gradle 版本,所以 Android Studio 自动去下载了,但由于都懂的原因,下载速度贼慢,因此网上才有一些博客教你说让你要去FQ,明白为什么了吧。

另外,网上还有一些博客会让你不管它,让你等隔天再去打开这个项目,然后有时候你会发现,隔天打开竟然能正常构建项目了,莫名其妙的就好了,就不一直卡在构建中了。这是由于 Android Studio 会有一个后台构建的功能,也就是说它可以在背后默默的帮你自动去下载 Gradle,虽然速度贼慢,但总有下载完成的时候,当你隔天再去打开这个新项目时,如果已经下载好了,那自然就可以正常构建使用了。

2.2 gradlew.bat 文件

gradlew 文件和 gradlew.bat 文件,两份没有什么差别,它们都是脚本文件,区别只是一个是 shell 脚本,一个是批处理脚本,那么自然一个是用来在 Linux 上运行,一个在 Windows 上运行。

感兴趣的可以去看看这份脚本代码,其中比较关键的代码:

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

大概翻译一下,它借助了 gradle/wrapper 目录下的 gradle-wrapper.jar 文件,并借助了 java 命令,提供了可让我们直接以命令行形式运行一些相应的 gradle 指令,而这些指令在 gradle-wrapper.jar 文件中都提供了相应的实现。

比如: gradlew -version

gradlew命令示例2.png

直接在 Android Studio 的 Terminal 面板运行 gradlew -version 命令,或者在 Dos 窗口,进到项目的根目录下执行该命令,都可以,前者只是打开时就默认将项目根目录作为当前路径了。

这也是为什么一些资料说,如果没有配置 Gradle 环境,那么在每个项目根目录下也可以运行 gradle 命令的原因,因为每个项目都提供了 gradlew.bat 脚本以及 gradle/wrapper 目录下的 gradle-wrapper.jar 文件支持。你可以试一下,删掉两者中任意一个,就没法正常运行 gradle 命令了。

那么,提供了脚本文件来支持直接运行 gradle 命令有什么用呢?

用处可多了,我们在构建项目时,基本都是直接借助 Android Studio 的图形界面来操作,点一点就可以了。但有时候,经常会遇见一些构建失败的情况,然后日志中经常会给我们这么一段提示:

gradle构建失败日志.png

如果想查看更多的日志信息,需要在执行命令的时候加上一些参数,而这种时候就需要通过命令行的形式来了,那么这时脚本文件也就派上用场了:

gradle命令带参数示例.png

gradle命令异常栈.png

这样一来就可以看到更多的日志信息了,当然我举的这个例子不好,因为可以直接看出问题出在哪了,不需要再去获取更多的辅助信息来定位了。

但有些时候,Gradle 构建时确实就是失败了,然后给的信息又少,让人莫名其妙,不知道为啥失败了,这种时候就可以借助命令行形式来执行这个 task,然后添加一些参数来获取更多的辅助日志。至于要添加哪些参数,执行什么命令,通常情况下,Gradle 构建失败时都会给出建议,跟着来就可以了。

2.3 setting.gradle 文件

setting.gradle 文件通常是当项目涉及到多 Module 的场景。

setting示例.png

只有在 setting.gradle 中 include 的 Module,才会被加入构建中,也就是说,如果一个项目文件夹内,包含了很多子工程,但如果没在 setting.gradle 中将这些子工程 include 进来的话, 这些 Module 是不会参与进构建的。

另外,如果子工程的这些 Module 都直接放在了项目根目录中,那么 setting.gradle 中只需要写 include 就可以了,那如果这些子工程是放在别的地方,那么也可以通过修改 project().projectDir 来指定子工程的具体路径,也就是说,所有的 Module 并不一定需要全部集中放在同一个项目内。

2.4 build.gradle 文件

一个项目中可能存在多个子工程,每个子工程构建都应该是相互独立的,也就是说,每个子工程都可以根据自己的需要,配置各种依赖,插件等。那么,Gradle 是如何分开来管理每个子工程的构建任务的呢?

这就是 build.gradle 文件的作用了,所以你会发现,每个子工程,也就是每个 Module 都会有一个 build.gradle 文件,Gradle 就是以这个文件为根据来构建这个 Module。

那么,如果有些配置项,在所有的子工程中都是一致的话,如果在每个子工程里都去重复粘贴的话,当这个共同的配置项需要发生变化时,维护起来会非常麻烦,这也就是为什么根目录下面还会有一个 build.gradle 文件。

根目录下的这个 build.gradle 是统筹全局的,在这里,你可以配置一些所有工程共同的配置项,比如 Android Gradle 的版本,依赖库的仓库地址这些所有工程的共同配置项。

也就是说,其实将根目录下的 build.gradle 文件里的内容移到每一个工程下的 build.gradle 里,也是可行的。但没必要这样做,吃饱了撑着。

3. Gradle 基础

3.1 task 概念

task 是 Gradle 中的一种概念,引用书中的解释:

一个 task 其实就是一个操作,一个原子性的操作,比如打个 jar 包,复制一份文件,编译一次 Java 代码,上传一个 jar 到 Maven 中心库等,这就是一个 Task,和 Ant 里的 Target, Maven 中的 goal 是一样的。

有点类似于 Java 里面的类,但又有很多不同之处。我们要通过 Java 命令来执行某个 java 文件,那么至少需要一个类,类里面需要有 main 方法,这个 java 文件才能运行起来。

同样,要通过 gradle 命令来执行某个构建任务,那么至少需要一个 task,这个构建任务才能跑起来。

但更多的是不同的概念,多个类之间可以有相互依赖的关系,类中持有另一个类的引用等等。

但在 task 方面,多个 task 之间只能有前后依赖关系,即某个 task 的运行是否需要哪个 task 先运行的基础上才允许,也就是说,在 Gradle 的构建工作过程中,多个 task 是构成一条直线的,一个个 task 按顺序的去工作,而不存在某个 task 工作到一半时去调用另一个 task。

不过,通常情况下,我们并不需要去接触到 task 层面,build.gradle 文件里的代码大多都只是在调用各种方法进行各种配置,而最后,会根据这份文件生成很多 task,比如:

Gradle面板.png

在 Android Studio 右侧的 Gradle 的面板这边,就是一个个的 task,assemble 是一个 task,build 也是一个 task,很多 task 是 Gradle 已经提供的,而有些 task 则是根据 build.gradle 里面的配置项自动生成的,比如 assembleDebug 这一类。

要执行 task 的方式,可以通过 AS 的图形界面点一点即可,也可以通过命令行方式,由于根目录提供了 gradlew 脚本文件,因此可以在根目录下执行 gradlew task名 即可。

3.2 gradle 插件概念

Gradle 是用来构建项目的,但并不是说只能用于构建 Android 的项目,Java 的也行,C++ 的也行,很多很多。

那如果我只是做 Android 开发,我也就只需要 Gradle 构建 Android 项目的功能即可,其他的又不需要,鉴于此,Gradle 封装好了基本的构建工作,然后提供了插件的接口,支持根据各自需要去扩展相应的构建任务。

以上就是我对于 Gradle 插件概念的理解,我认为它是用于给大伙可以根据需要自行去扩展。

就拿 Android 项目来说,来看一份 build.gradle 文件结构:

apply plugin: 'com.android.application'android {...defaultConfig {....}
}dependencies {...
}

如果有点击方法进去看过源码的话,你会发现:

dependencies.png

androidGradle.png

发现没有,dependencies 是 Gradle 提供的方法,但 android,defaultConfig 却都是 Android Gradle 插件提供的方法了。

也就是说,其实 Gradle 只提供了构建项目的一些基本功能,如配置依赖库,不管什么项目都需要。但像 android {} 代码块里的配置项,很明显,就只有 Android 项目才需要用到了,所以这些配置并不是由 Gradle 来提供的,而是由 Android Gradle 插件提供的,这也就是为什么在根目录的 build.gradle 文件里会有这么一行代码:

buildscript {repositories {jcenter()}dependencies {classpath 'com.android.tools.build:gradle:2.3.3'}
}

com.android.tools.build:gradle:2.3.3 这行代码其实就是声明了我们的项目还需要使用 Android Gradle 插件,版本号为 2.3.3,而插件的下载地址在 jecnter() 仓库。

所以,得搞清楚,Gradle 和 Android Gradle 是两种概念,也是两个不同的东西,如果有人问说你项目构建的 Gradle 的版本是多少,得搞清楚,他想问的是 Gradle,还是 Android Gradle 插件。

但是我们在根目录的 build.gradle 里是可以配置多个插件的,比如如果有使用 GreenDao,或者使用了 Jcenter 的上传功能:

buildscript {repositories {jcenter()}dependencies {//Google提供的构建Android项目的插件classpath 'com.android.tools.build:gradle:2.3.3'//GreenDao 提供的插件classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'//Jcenter提供的插件classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4'//Maven提供的插件classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'}
}

那么,Gradle 在根据 build.gradle 构建 Module 时,怎么知道要使用哪个插件呢,声明了这么多。

这就是为什么在每个 Module 的 build.gradle 文件的开头有行 apply plugin 的代码了。

apply 是 Gradle 的方法,它可以接收一个 map 类型的参数,而在 Groovy 中,map 的定义可以直接 'key': value,也就是说:

apply plugin: 'com.android.application'  
//等效于
// def map = ['plugin':'com.android.application']
// apply(map)

每个 build.gradle 开头这行代码,其实是调用了 Gradle 的 apply() 方法,然后传入了一个 map 值,key 为 plugin, value 为 ‘com.android.application',那么 Gradle 就知道了你这个项目需要使用到一个 id 为 'com.android.application' 的插件来辅助构建了,那么它就会去你在根目录下配置的插件列表里寻找。

也就是说,apply plugin 是 Gradle 规定并提供的,但 'com.android.application' 则是由 Android Gradle 来提供的。

那么,小结一下,要使用一个 Gradle 插件的话,先得在根目录下声明你要用的插件以及版本,当然也得指定插件的下载地址,然后在你具体的 Module 的 build.gradle 的开头通过 apply plugin 方式来应用插件,这个插件得有一个唯一区分开的 id 值。

4. 区分 Gradle 和 Android Gradle

ProjectStructure.png

先来看张图,这个在 File -> Project Structure -> Project 打开,在这里可以配置 Gradle 和 Android Gradle 插件的版本。

最开始接触的时候,我看到这里是有些迷茫的,怎么有一个 Gradle 版本,又有一个 Android Gradle 版本。当别人问我你 Android Studio 使用的 Gradle 版本是多少时,我也傻乎乎的打开根目录下的 build.gradle 文件里,看到 com.android.tools.build:gradle:2.3.3,然后跟他说 2.3.3 版本,当初根本没搞清楚这两个有什么区别,一直以为是同一个东西。

所以,要搞清楚 Android Gradle 其实只是 Gradle 的一个插件,是 Google 基于 Gradle 提供的插件接口所做的一些扩展。

所以,要查找 Android Gradle 的相关资料,自然就不是去 Gradle 官网了,而是要去 Android 官网找:

https://developer.android.google.cn/studio/releases/gradle-plugin

由于 Gradle 更新换代很快,又经常提供一些新特性,所以 Android Gradle 插件也就跟随着发布了很多版本,所以,Android Gradle 的版本并不是可以任意更改的,因为它是基于每一个 Gradle 版本开发的,因此需要在对应的 Gradle 版本中才能使用。

这也是为什么,我们有时候直接修改根目录下的 build.gradle 中的 Android Gradle 版本时,会报一些错误说需要 Gradle 版本在多少在可以使用的原因,至于这些对应关系,官网当然有给出来了:

版本对应关系.png

举个例子,如果你 Gradle 版本使用的是 3.3,然后你在 Github 上 clone 了某个人的项目,他的项目中使用的 4.4 的 Gradle 版本 和 3.1.0 的 Android Gradle 插件,但是你发现在打开这个项目的时一直处于构建中,一直打不开。

你查了下原因,网上有教程说,让你将项目中的 gradle/wrapper 文件里的 Gradle 版本换成你本地项目中的 Gradle 版本,还跟你说因为它使用的版本你本地没有,而且被墙了,你下载需要很长时间,让你直接改成使用你本地的版本即可。

你改了后,发现,是可以打开项目了,但构建的时候又报错了,说你使用了 3.1.0 的 Android Gradle 插件,需要让你将 Gradle 版本改成 4.4 才可以正常构建,这 MMP 不是又绕回去了。

所以说,别听他放屁,搞清楚了 Gradle 和 Android Gradle 插件的关系之后。那为什么会一直在构建中,为什么会报错我们心里就有数了,要解决,没有其他办法,就是要将对应的版本下载下来。

所以,你应该去搜的是如何下载,Android Gradle 插件并没有被墙,如果想自行下载,可以参考我之前的博客: 如何用Android Studio查看build.gradle源码,而至于 Gradle 要如何下载,如果官网下载不了,那就去搜搜有没有相关的镜像吧。


本篇就先到这里了,还会有一篇下篇,下篇的内容就是侧重于介绍 build.gradle 里各种配置项的作用和意义了,还有就是如何自己写 Gradle 脚本来运行,敬请期待~


QQ图片20180316094923.jpg
最近(2018-03)刚开通了公众号,想激励自己坚持写作下去,初期主要分享原创的Android或Android-Tv方面的小知识,准备可能还有点不足,感兴趣的可以先点一波关注,谢谢支持~~

转载于:https://www.cnblogs.com/dasusu/p/9085174.html

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[js] 如何判断对象是否属于某个类?

[js] 如何判断对象是否属于某个类? obj.proto class.prototype 可以递归去找obj instanceof class个人简介 我是歌谣,欢迎和大家一起交流前后端知识。放弃很容易, 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

基于Docker搭建私有镜像仓库

通常我们在docker中拉取的镜像都是在docker hub在线存储库中获取的,这个在线存储库里的docker镜像可以由任何用户发布和使用,显然这在某些场景下是不适用的,比如某些互金的隐私项目,或者是公司完全处于内网状态不能访问外网&#…

[js] 说说你对js沙箱的理解,它有什么应用场景?

[js] 说说你对js沙箱的理解,它有什么应用场景? 在微前端有用到js沙箱,例如qiankun框架,主应用的js运行和子任务的js运行不会相互影响,是使用es6的proxy来实现的个人简介 我是歌谣,欢迎和大家一起交流前后…

volatile理解了吗?

到这里大家感觉自己对volatile理解了吗? 如果理解了,大家考虑这么一个问题:ReentrantLock(或者其它基于AQS实现的锁)是如何保证代码段中变量(变量主要是指共享变量,存在竞争问题的变量&…

Linux|CentOS下配置Maven环境

1、下载maven包 wget http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz如果提示 wget: 未找到命令,请尝试如下指令安装 wget yum -y install wget2、解压下载的maven压缩吧 tar -xzvf apache-maven-3.3.…

[js] 纯函数和函数式编程有什么关系?

[js] 纯函数和函数式编程有什么关系? 函数式编程是一种编程思想,纯函数是这种思想的基本要实现函数式编程,我们所封装的方法应该是抽象的,应该是和外部状态无关系的,也就需要是纯函数的,这样才能保证抽象的…

PHP 超级全局变量

PHP 超级全局变量列表: $GLOBALS $_SERVER $_REQUEST $_POST $_GET $_FILES $_ENV $_COOKIE $_SESSION 转载于:https://www.cnblogs.com/alexguoyihao/p/9086857.html

[js] 为什么要用纯函数?

[js] 为什么要用纯函数? 在此之前要先了解什么是纯函数,简单来说纯函数的定义有两个: 1.返回的结果只依赖于传入的参数。 2.执行过程中不产生副作用。 在这里就需要了解到什么是副作用 1.改变了外部变量或者对象属性 2.触发任何外部进程 3.发…

CentOS中安装Docker步骤

1、安装仓库所需要的软件包 yum install -y yum-utils device-mapper-persistent-data lvm22、设置yum加速源 yum-config-manager --add-repo http://mirrors.aliyun.com/repo/Centos-7.repo3、安装docker-ce yum install docker-ce docker-ce-cli containerd.io4、启动dock…

LeetCode 22. 括号生成(Generate Parentheses)

题目描述 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。 例如,给出 n 3,生成结果为: ["((()))","(()())","(())()","()(())",&qu…

[js] 使用js实现一个图片剪裁的功能

[js] 使用js实现一个图片剪裁的功能 /** * 裁切图片* param imgUrl 原始图片路径* param x,y,width,height 从点[x, y]开始,将宽度width,高度height的区域裁切下来* tips:需要运行于服务器环境下切图片为同域*/ function clipImage(imgUrl, x, y, width…

Docker+Jenkins+Git+GitLab实现DevOps

先了解一下Jenkins Jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具,提供了数百个插件来支持构建,部署和自动化任何项目。我们可以使用Jenkins结合常用的版本控制工具(git、svn等)来实现自动部署项目,比如说我们从本地上传代码到G…

[js] 使用for-in语句能保证遍历对象的顺序吗?如果不能那为什么?如果可以那又如何保证?

[js] 使用for-in语句能保证遍历对象的顺序吗?如果不能那为什么?如果可以那又如何保证? Chrome Opera 的 JavaScript 解析引擎遵循的是新版 ECMA-262 第五版规范。因此,使用 for-in 语句遍历对象属性时遍历顺序并非属性构建顺序。…

深入理解Flask中的上下文

https://blog.csdn.net/barrysj/article/details/51519254 1.AppContext类即是应用上下文,可以看到里面只保存了几个变量,其中比较重要的有: app是当前web应用对象的引用,如Flask;还有g,用来保存需要在每个…

毕业两年的大专生程序员工作总结(java后端)

文章目录前言这一年做了啥去年的学习清单今年的学习清单第三年的规划最后唠叨的话前言 如题,这是我毕业第二年的工作总结,对第一年工作总结感兴趣的请戳这《毕业一年的大专生程序员工作总结》,再简单介绍一下我以及这个系列的文章。 关于我…

[js] 写个方法获取屏幕的DPI

[js] 写个方法获取屏幕的DPI (function() {var arrDPI new Array();if ( window.screen.deviceXDPI) {arrDPI[0] window.screen.deviceXDPI;arrDPI[1] window.screen.deviceYDPI;}else {var tmpNode document.createElement( "DIV" );tmpNode.style.cssText &qu…

Docker开启远程安全访问

一、编辑docker.service文件 vi /usr/lib/systemd/system/docker.service找到 [Service] 节点,修改 ExecStart 属性,增加 -H tcp://0.0.0.0:2375 ExecStart/usr/bin/dockerd -H fd:// --containerd/run/containerd/containerd.sock -H tcp://0.0.0.0:23…

acm--1004

问题描述再次比赛时间!看到气球在四周漂浮,多么兴奋。但要告诉你一个秘密,评委最喜欢的时间是猜测最流行的问题。比赛结束后,他们会统计每种颜色的气球并找出结果。今年,他们决定离开这个可爱的工作给你。输入输入包含…

[js] promise的构造函数是同步执行还是异步执行,它的then方法呢?

[js] promise的构造函数是同步执行还是异步执行,它的then方法呢? promise构造函数是同步执行的,then方法是异步执行的。个人简介 我是歌谣,欢迎和大家一起交流前后端知识。放弃很容易, 但坚持一定很酷。欢迎大家一起…

Netty入门篇-从双向通信开始

百度百科描述 Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。 也就是说,Netty 是一个基于NIO的客户、服务器…