前言
Gradle
是Android
的构建工具,它的主要目标就是实现快速的编译构建,而这主要就是通过缓存实现的。本文主要介绍Gradle
的缓存机制,具体包括以下内容
Gradle
缓存机制Gradle
内存缓存Gradle
项目缓存Gradle
本机缓存Gradle
远程缓存
Gradle
是Android
的构建工具,它的主要目标就是实现快速的编译构建,而这主要就是通过缓存实现的。本文主要介绍Gradle
的缓存机制,具体包括以下内容
Gradle
缓存机制Gradle
内存缓存Gradle
项目缓存Gradle
本机缓存Gradle
远程缓存使用 allowInsecureProtocol 属性解决 gradle 的仓库地址不安全警告,在 IDEA 的 Terminal 中使用命令
$ gradlew --warning-mode all
可以打印出当前 gradle 存在的所有警告信息。
如果有报以下警告:
Using insecure protocols with repositories, without explicit opt-in, has been deprecated. This is scheduled to be removed in Gradle 7.0. Switch Maven repository 'm aven(http://maven.aliyun.com/nexus/content/groups/public/)' to a secure protocol (like HTTPS) or allow insecure protocols. See https://docs.gradle.org/6.8.3/dsl/or g.gradle.api.artifacts.repositories.UrlArtifactRepository.html#org.gradle.api.artifacts.repositories.UrlArtifactRepository:allowInsecureProtocol for more details. Configuration on demand is an incubating feature.
说明你配置了除 maven 中央仓库之外的其他不安全的仓库(至于“不安全”在这里的定义,我也不是很清楚,一些国内的镜像仓库例如阿里的也是“不安全”的)gradle 中有一个属性可以允许 gradle 使用“不安全”的仓库并且不报警告信息,该属性是 allowInsecureProtocol,官方的
Description :Specifies whether it is acceptable to communicate with a repository over an insecure HTTP connection.
翻译过来就是指定通过不安全的HTTP连接与仓库通信是否可接受,如果该属性的值设置为 true,则表示接受“不安全”的仓库地址。
目前,升级项目的 gradle 到 7.0.2 版本之后,报错如下:
* What went wrong: A problem occurred configuring root project 'xxxx'. > Could not resolve all dependencies for configuration ':classpath'. > Using insecure protocols with repositories, without explicit opt-in, is unsupported. Switch Maven repository 'maven(http://xxx.xxx.xxx.xxx/jcenter/)' to red irect to a secure protocol (like HTTPS) or allow insecure protocols. See https://docs.gradle.org/7.0.2/dsl/org.gradle.api.artifacts.repositories.UrlArtifactRepos itory.html#org.gradle.api.artifacts.repositories.UrlArtifactRepository:allowInsecureProtocol for more details. * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
只需要在 build.gradle 中进行如下的配置即可:
repositories { ... maven { allowInsecureProtocol = true ... } }
参考链接
gradle下载后会对文件路径进行修饰,本文给出反向解析,把文件路径修改为原始路径的办法。
之所以研究这个,本来的目的是为了让Gradle支持离线编译,但是由于Gradle目录组织的缺陷,如.gradle/caches/modules-2/metadata-2.23(metadata-xx跟使用的gradle版本有关)目录下module-artifacts.bin等bin文件中存的是本机的绝对路径,导致就算将.gradle拷贝给另一台机器,还是需要联网验证。
将gradle的jcenter重定向的方法见我的另一篇文章:[android]gradle下载加速 - 知乎专栏,例如阿里云的jcenter是Index of /repositories/jcenter
我在内网中搭建了jcenter仓库。然而我不想搭建一个大而全的jcenter的仓库,搭建大而全的仓库可以使用nexus来搭建,阿里云也是那么搭的,由于公司的网络需要上网认证,时不时会断一下,所以我的做法是通过利用.gradle目录来创建jcenter仓库。
下面先介绍.gradle目录的组织。
目录| 功能
caches | gradle缓存目录
daemon | daemon日志目录
native | gradle平台相关目录
wrapper | gradle-wrapper下载目录
目录 | 功能
2.14.1 | gradle程序的脚本(gradle程序版本)
3.2.1 | gradle程序的脚本(gradle程序版本)
jars-1 | ?
jars-2 | ?
modules-2 | 下载缓存目录
目录 | 功能
files-2.1 | gradle下载的jar/aar目录
metadata-2.16 | gradle-2.14.1的描述文件?
metadata-2.23 | gradle-3.2.1的描述文件?
${org}/${package}/${version}/${shanum1}/${package-version}.pom ${org}/${package}/${version}/${shanum2}/${package-version}.jar
例如:
#jcenter上jar的路径 https://jcenter.bintray.com/com/android/tools/lint/lint-api/25.1.3/lint-api-25.1.3.jar #对应的缓存目录为 .gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/${shasum1}/lint-api-25.1.3.jar #看下这个目录下有什么? ~ find .gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/ -type f #除了jar包还有pom,用于描述jar。 .gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/14c6a94811fb8114a61b8f3ab29214f9466b5c59/lint-api-25.1.3.jar .gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/c4844e26d84dd1f450f90d89d7e2d2d09f52760/lint-api-25.1.3.pom #看下jar的shasum ~ shasum .gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/14c6a94811fb8114a61b8f3ab29214f9466b5c59/lint-api-25.1.3.jar 14c6a94811fb8114a61b8f3ab29214f9466b5c59 .gradle/caches/modules-2/files-2.1/com.android.tools.lint/lint-api/25.1.3/14c6a94811fb8114a61b8f3ab29214f9466b5c59/lint-api-25.1.3.jar #即目录名。
注意:创建jcenter时,对于jar包,可以没有pom,但是如果使用aar,则必须有pom,所以最好是每个版本都有个pom。因为pom中也描述了依赖关系。
用于存放gradle daemon的运行日志。按gradle程序版本存放。
目录 | 功能
2.14.1 | gradle-2.14.1运行的日志
3.2.1 | gradle-3.2.1运行的日志
用于存放平台相关(Windows/Linux/Mac)的库。
目录 | 功能
19 | gradle-2.14.1对应的lib目录,按平台存放,如osx-amd64
21 | gradle-3.2.1对应的lib目录,按平台存放,如osx-amd64
jansi | ?
用于存放gradle-wrapper下载gradle的zip包和解压后的文件夹。
wrapper的目录规则是
wrapper/dists/gradle-2.14.1-all/${base36}/gradle-2.14.1-all.zip
wrapper/dists/gradle-2.14.1-all/${base36}/gradle-2.14.1-all.zip.lck
wrapper/dists/gradle-2.14.1-all/${base36}/gradle-2.14.1-all.zip.ok
其中base36的规则为:
从gradle-wrapper的jar包中提取的Java代码如下:
import java.math.BigInteger; import java.security.MessageDigest; public class Hash { public static void main(String[] args) { try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); byte[] bytes = args[0].getBytes(); messageDigest.update(bytes); String str = new BigInteger(1, messageDigest.digest()).toString(36); System.out.println(str); } catch (Exception e) { throw new RuntimeException("Could not hash input string.", e); } } }
c++跟java代码见http://github.com/xiaoyur347/gradlew/helper。
以下是mirror.sh,我把它放在.gradle目录下。运行脚本会在.gradle目录下生成jcenter目录。把它移走或直接把jcenter目录加入http服务器即可。
目前我的做法是在本地使用gradle编译一次,然后把gradle下载下来的jar/aar/pom全部提交到版本管理,然后由持续集成去拉版本库上的.gradle目录,然后生成jcenter提供给内网编译服务器。
#!/bin/bash #use .gradle/caches/modules-2/files-2.1/ to create jcenter SHPATH=$(cd "$(dirname "$0")"; pwd) DIR="${SHPATH}/caches/modules-2/files-2.1/" DIR_LENGTH=${#DIR} if [ `uname` = "Darwin" ]; then #mac will add "/" DIR_LENGTH=$((DIR_LENGTH+1)) fi rm -rf ${SHPATH}/jcenter/* find ${DIR} -type f | grep -Ev "DS_Store" | while read line do SRC=${line} URL=${line:${DIR_LENGTH}} ORG=${URL%%/*} URL=${URL#*/} MODULE=${URL%%/*} URL=${URL#*/} REVISION=${URL%%/*} URL=${URL#*/} SHA1=${URL%%/*} URL=${URL#*/} FILE=${URL} #echo "ORG=$ORG, MODULE=$MODULE, REVISION=$REVISION, SHA1=$SHA1, FILE=$FILE" DST=${SHPATH}/jcenter/${ORG//.//}/${MODULE}/${REVISION}/${FILE} echo "$DST" mkdir -p `dirname ${DST}` if [ ! -f ${DST} ]; then cp -a ${SRC} ${DST} fi done
另外,上面提到aar有可能会因为缺失pom导致无法使用,我还写了一个脚本,也是放在.gradle目录下,用于修复aar问题。本地运行时,可以只运行这个脚本,因为它会顺路执行上面的脚本。
以下是fix_aar_cache.sh的内容:
#!/bin/bash SHPATH=$(cd "$(dirname "$0")"; pwd) SRC_DIR=${SHPATH}/jcenter CACHE_DIR="${SHPATH}/caches/modules-2/files-2.1/" ${SHPATH}/mirror.sh > /dev/null DIR_LENGTH=${#SRC_DIR} if [ `uname` = "Darwin" ]; then #mac will add "/" DIR_LENGTH=$((DIR_LENGTH+1)) fi find ${SRC_DIR} -name "*.aar" | while read line do SRC=${line} LOCAL_POM=${SRC/.aar/.pom} if [ -f ${LOCAL_POM} ]; then continue fi echo "$LOCAL_POM not found" URL=${line:${DIR_LENGTH}} URL=${URL/.aar/.pom} REMOTE_POM=http://maven.aliyun.com/nexus/content/repositories/jcenter/${URL} REMOTE_POM_SHA1=${REMOTE_POM}.sha1 SHA1=`wget -q -O- ${REMOTE_POM_SHA1}` SHA1=${SHA1:2} #remove additional \r\n echo $SHA1 #reverse to get it FILE=${URL##*/} URL=${URL%/*} REVISION=${URL##*/} URL=${URL%/*} MODULE=${URL##*/} URL=${URL%/*} ORG=${URL} echo "ORG=$ORG, MODULE=$MODULE, REVISION=$REVISION, FILE=$FILE" #org /->. DST=${CACHE_DIR}/${ORG//\//\.}/${MODULE}/${REVISION}/${SHA1}/${FILE} echo "$DST" mkdir -p `dirname ${DST}` if [ ! -f ${DST} ]; then wget -O ${DST} ${REMOTE_POM} fi done
debug/release 修改包名,取不同包名下的agconnect-services.json 文件 V2 解决每次更换包名,都是要手动删除agconnect-services.json文件操作
集成华为HMS Core/Push在打多渠道包的时候,我需要区分debug版本,release版本,其中涉及到包名的不同,我使用release编译的时候,发现如下错误信息。这个原因是因为你的agconnect-services文件里面含有一个 package_name 参数,这个参数是需要指定包名的,如果 package_name 填写的报名,和目前你所使用的包名没有对应上就会出现这样的错误
* What went wrong: Execution failed for task ':app:processDebugAGCPlugin'. > ERROR: Failed to verify AGConnect-Config '/client/package_name', expected: 'com.gxx.fast', but was: 'com.gxx.fast.debug'
既然是需要区分包名的,我不如直接copy 2份出来,放到src下面,并新建一个类 pushservices 里面存放 debug/release 的类并存放 agconnect-services.json文件,这样我们想使用哪个版本的,就使用哪个版本的
继续阅读集成华为HMS debug/release 修改包名,取不同包名下的agconnect-services.json 文件
最近在搭建`Jenkins`环境,实现`Android`自动化编译的过程中,由于内网服务器不能访问外网,因此只能配置`Robolectric`访问内网的服务器。
根据官方文档 Configuring Robolectric,发现如果要更改`Robolectric`默认的下载服务器链接地址,需要在项目的每个`lib`库中都配置如下参数,才能实现:
android { testOptions { unitTests.all { systemProperty 'robolectric.dependency.repo.url', 'https://local-mirror/repo' systemProperty 'robolectric.dependency.repo.id', 'local' } } }
但是这样配置有一个问题,就是没办法动态调整链接地址。
而我们使用
$ bash gradlew clean build -Drobolectric.dependency.repo.url=http://127.0.0.1/jcenter
手工指定的参数,并没有在编译的时候生效。
参数丢失的原因是因为测试用例在新的`JVM`中执行,传入的参数不会自动带给新创建的`JVM`。
这时需要在`Gradle`脚本中将读到的值重新设到系统属性里面,才可以被`Java`程序读到。
android { testOptions { unitTests.all { //命令行下 单元测试可能卡住的问题 jvmArgs '-noverify' //robolectric外部指定下载资源链接的参数,使用 -D 参数指定 bash gradlew clean build -Drobolectric.dependency.repo.url=http://127.0.0.1/jcenter systemProperty 'robolectric.dependency.repo.url', System.getProperty("robolectric.dependency.repo.url") systemProperty 'robolectric.dependency.repo.id', System.getProperty("robolectric.dependency.repo.id") } } }
每个项目的`build.gradle`中都增加上面的配置之后,就可以保证在外部编译的时候动态指定参数了。
在 Android Studio Chipmunk | 2021.2.1 Patch 1 以及以上的版本,可能会发生如下报错:
URI is not absolute java.lang.IllegalArgumentException: URI is not absolute at java.base/java.net.URL.fromURI(URL.java:692) at java.base/java.net.URI.toURL(URI.java:1116) at org.robolectric.internal.dependency.MavenArtifactFetcher.getRemoteUrl(MavenArtifactFetcher.java:138) at org.robolectric.internal.dependency.MavenArtifactFetcher.fetchToStagingRepository(MavenArtifactFetcher.java:145) at org.robolectric.internal.dependency.MavenArtifactFetcher.fetchArtifact(MavenArtifactFetcher.java:65) at org.robolectric.internal.dependency.MavenDependencyResolver.lambda$getLocalArtifactUrls$0(MavenDependencyResolver.java:80) at org.robolectric.internal.dependency.MavenDependencyResolver.whileLocked(MavenDependencyResolver.java:100) at org.robolectric.internal.dependency.MavenDependencyResolver.getLocalArtifactUrls(MavenDependencyResolver.java:75) at org.robolectric.internal.dependency.MavenDependencyResolver.getLocalArtifactUrls(MavenDependencyResolver.java:65) at org.robolectric.internal.dependency.MavenDependencyResolver.getLocalArtifactUrl(MavenDependencyResolver.java:116) at org.robolectric.plugins.LegacyDependencyResolver.getLocalArtifactUrl(LegacyDependencyResolver.java:89) at org.robolectric.plugins.DefaultSdkProvider$DefaultSdk.getJarPath(DefaultSdkProvider.java:146) at org.robolectric.internal.AndroidSandbox$SdkSandboxClassLoader.<init>(AndroidSandbox.java:102) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490) at org.robolectric.util.inject.Injector.inject(Injector.java:250) at org.robolectric.util.inject.Injector.lambda$memoized$1(Injector.java:232) at org.robolectric.util.inject.Injector$MemoizingProvider.get(Injector.java:498) at org.robolectric.util.inject.Injector.getInstanceInternal(Injector.java:224) at org.robolectric.util.inject.Injector.resolveDependencies(Injector.java:296) at org.robolectric.util.inject.Injector.inject(Injector.java:248) at org.robolectric.util.inject.Injector.lambda$memoized$1(Injector.java:232) at org.robolectric.util.inject.Injector$MemoizingProvider.get(Injector.java:498) at org.robolectric.util.inject.Injector.getInstanceInternal(Injector.java:224) at org.robolectric.util.inject.Injector.getInstance(Injector.java:208) at org.robolectric.util.inject.Injector.access$700(Injector.java:96) at org.robolectric.util.inject.Injector$ScopeBuilderProvider.create(Injector.java:564) at org.robolectric.util.inject.Injector$ScopeBuilderProvider.lambda$get$0(Injector.java:547) at com.sun.proxy.$Proxy19.build(Unknown Source) at org.robolectric.internal.SandboxManager.getAndroidSandbox(SandboxManager.java:57) at org.robolectric.RobolectricTestRunner.getSandbox(RobolectricTestRunner.java:285) at org.robolectric.RobolectricTestRunner.getSandbox(RobolectricTestRunner.java:68) at org.robolectric.internal.SandboxTestRunner$2.evaluate(SandboxTestRunner.java:236) at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63) at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) at org.robolectric.internal.SandboxTestRunner$1.evaluate(SandboxTestRunner.java:93) at org.junit.runners.ParentRunner.run(ParentRunner.java:413) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38) at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36) at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33) at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94) at com.sun.proxy.$Proxy2.processTestClass(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176) at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129) at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100) at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60) at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56) at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133) at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71) at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69) at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
该报错的原因是 Android Studio 没有正确读取我们配置的 robolectric.dependency.repo.url 环境变量,导致无法成功下载,当然也可能是由于高版本不支持 HTTP ,链接地址必须是 HTTPS 导致此问题,这个没有深究。
解决方法是,在命令行执行
$ bash gradlew clean build -Drobolectric.dependency.repo.url=http://127.0.0.1/jcenter
确保在当前用户目录下的 .m2 目录下,成功完成 robolectric 需要的全部文件的下载。这样,执行测试用例的时候,就可以从本地获取依赖文件,而不需要再去服务器上获取。
使用Android Studio
开发,最痛苦的其中一项是Maven
下载数据缓慢,目前已经可以根据在Ubuntu 14.04 系统中的Apache Tomcat上部署Apache Archiva 2.2.1来使用本站的代理服务器的方式进行提速了。另一个痛苦的事情就是下载Gradle
工具包的速度异常缓慢了,不仅慢,而且还容易失败。
目前本网站已经提供了Gradle
工具包的下载代理,具体的操作就是把services.gradle.org
进行域名污染,指向本站的IP
地址121.199.27.227
。
Windows下的解决方法为修改C:\Windows\System32\drivers\etc
下的hosts
文件,里面增加如下内容:
# Copyright (c) 1993-2009 Microsoft Corp. # # This is a sample HOSTS file used by Microsoft TCP/IP for Windows. # # This file contains the mappings of IP addresses to host names. Each # entry should be kept on an individual line. The IP address should # be placed in the first column followed by the corresponding host name. # The IP address and the host name should be separated by at least one # space. # # Additionally, comments (such as these) may be inserted on individual # lines or following the machine name denoted by a '#' symbol. # # For example: # # 102.54.94.97 rhino.acme.com # source server # 38.25.63.10 x.acme.com # x client host # localhost name resolution is handled within DNS itself. # 127.0.0.1 localhost # ::1 localhost 121.199.27.227 services.gradle.org
接着修改Android Studio
项目下的gradle\wrapper\gradle-wrapper.properties
文件,把其中的
#Tue Aug 16 10:46:15 CST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
修改为:
#Tue Aug 16 10:46:15 CST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=http\://services.gradle.org/distributions/gradle-2.14.1-all.zip
注意,上面的修改其实主要是把HTTPS
修改成了HTTP
,原因在于HTTPS
无法进行域名污染。
当然,另外一个比较简单的修改方式为,只要修改distributionUrl
为本站地址,更加省事:
#Tue Aug 16 10:46:15 CST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://www.mobibrw.com/distributions/gradle-2.14.1-all.zip
目前本站提供的Gradle
工具包版本如下:
gradle-2.10-all.zip.sha256 gradle-2.10-all.zip gradle-2.14.1-all.zip.sha256 gradle-2.14.1-all.zip gradle-6.6-all.zip.sha256 gradle-6.6-all.zip gradle-8.2.1-all.zip
当前更建议大家使用 腾讯的国内镜像 https://mirrors.cloud.tencent.com/gradle/
最近在调整代码的时候,出现如下问题:
Cause: com.android.dex.DexException: Multiple dex files define Landroid/support/annotation/AnimRes; UNEXPECTED TOP-LEVEL EXCEPTION: com.android.dex.DexException: Multiple dex files define Landroid/support/annotation/AnimRes; at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:579) at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:535) at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:517) at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:164) at com.android.dx.merge.DexMerger.merge(DexMerger.java:188) at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:504) at com.android.dx.command.dexer.Main.runMonoDex(Main.java:334) at com.android.dx.command.dexer.Main.run(Main.java:277) at com.android.dx.command.dexer.Main.main(Main.java:245) at com.android.dx.command.Main.main(Main.java:106)
网上查询了一下,找到如下的解决方法:
我们常常用gradle.build
文件导入依赖库,但是有时候库依赖会发生版本冲突或多个模块依赖同一个库的情况。
这就引出两个问题,如下:
Gradle
会下载多个版本的依赖库吗?Android Studio
用Gradle
举个例子:gradle.build
依赖部分的配置:dependencies { compile 'com.android.support:appcompat-v7:22.2.1' compile ('com.android.support:design:22.2.1') compile 'com.shamanland:fonticon:0.1.8' androidTestCompile 'com.android.support:support-annotations:22.2.1' androidTestCompile 'com.android.support.test:runner:0.3' androidTestCompile('com.android.support.test.espresso:espresso-core:2.2') }
在文件目录下或Android Studio
的Terminal
下敲gradlew -q app:dependencies
命令,便有以下输出:
_debugAndroidTestApk - ## Internal use, do not manually configure ## +--- com.android.support:support-annotations:22.2.1 +--- com.android.support.test:runner:0.3 | +--- com.android.support.test:exposed-instrumentation-api-publish:0.3 | +--- junit:junit:4.12 | | \--- org.hamcrest:hamcrest-core:1.3 | \--- com.android.support:support-annotations:22.2.0 -> 22.2.1 \--- com.android.support.test.espresso:espresso-core:2.2 +--- com.android.support.test.espresso:espresso-idling-resource:2.2 +--- com.squareup:javawriter:2.1.1 +--- javax.inject:javax.inject:1 +--- org.hamcrest:hamcrest-library:1.3 | \--- org.hamcrest:hamcrest-core:1.3 +--- com.android.support.test:rules:0.3 | \--- com.android.support.test:runner:0.3 (*) +--- org.hamcrest:hamcrest-integration:1.3 | \--- org.hamcrest:hamcrest-library:1.3 (*) +--- com.google.code.findbugs:jsr305:2.0.1 +--- javax.annotation:javax.annotation-api:1.2 \--- com.android.support.test:runner:0.3 (*) _debugAndroidTestCompile - ## Internal use, do not manually configure ## +--- com.android.support:support-annotations:22.2.1 +--- com.android.support.test:runner:0.3 | +--- com.android.support.test:exposed-instrumentation-api-publish:0.3 | +--- junit:junit:4.12 | | \--- org.hamcrest:hamcrest-core:1.3 | \--- com.android.support:support-annotations:22.2.0 -> 22.2.1 \--- com.android.support.test.espresso:espresso-core:2.2 +--- com.android.support.test.espresso:espresso-idling-resource:2.2 +--- com.squareup:javawriter:2.1.1 +--- javax.inject:javax.inject:1 +--- org.hamcrest:hamcrest-library:1.3 | \--- org.hamcrest:hamcrest-core:1.3 +--- com.android.support.test:rules:0.3 | \--- com.android.support.test:runner:0.3 (*) +--- org.hamcrest:hamcrest-integration:1.3 | \--- org.hamcrest:hamcrest-library:1.3 (*) +--- com.google.code.findbugs:jsr305:2.0.1 +--- javax.annotation:javax.annotation-api:1.2 \--- com.android.support.test:runner:0.3 (*) _debugApk - ## Internal use, do not manually configure ## +--- com.android.support:multidex:1.0.1 +--- com.android.support:appcompat-v7:22.2.1 | \--- com.android.support:support-v4:22.2.1 | \--- com.android.support:support-annotations:22.2.1 +--- com.android.support:design:22.2.1 | +--- com.android.support:appcompat-v7:22.2.1 (*) | \--- com.android.support:support-v4:22.2.1 (*) \--- com.shamanland:fonticon:0.1.8 _debugCompile - ## Internal use, do not manually configure ## +--- com.android.support:multidex:1.0.1 +--- com.android.support:appcompat-v7:22.2.1 | \--- com.android.support:support-v4:22.2.1 | \--- com.android.support:support-annotations:22.2.1 +--- com.android.support:design:22.2.1 | +--- com.android.support:appcompat-v7:22.2.1 (*) | \--- com.android.support:support-v4:22.2.1 (*) \--- com.shamanland:fonticon:0.1.8 _debugUnitTestApk - ## Internal use, do not manually configure ## No dependencies _debugUnitTestCompile - ## Internal use, do not manually configure ## No dependencies _releaseApk - ## Internal use, do not manually configure ## +--- com.android.support:multidex:1.0.1 +--- com.android.support:appcompat-v7:22.2.1 | \--- com.android.support:support-v4:22.2.1 | \--- com.android.support:support-annotations:22.2.1 +--- com.android.support:design:22.2.1 | +--- com.android.support:appcompat-v7:22.2.1 (*) | \--- com.android.support:support-v4:22.2.1 (*) \--- com.shamanland:fonticon:0.1.8 _releaseCompile - ## Internal use, do not manually configure ## +--- com.android.support:multidex:1.0.1 +--- com.android.support:appcompat-v7:22.2.1 | \--- com.android.support:support-v4:22.2.1 | \--- com.android.support:support-annotations:22.2.1 +--- com.android.support:design:22.2.1 | +--- com.android.support:appcompat-v7:22.2.1 (*) | \--- com.android.support:support-v4:22.2.1 (*) \--- com.shamanland:fonticon:0.1.8 _releaseUnitTestApk - ## Internal use, do not manually configure ## No dependencies _releaseUnitTestCompile - ## Internal use, do not manually configure ## No dependencies androidJacocoAgent - The Jacoco agent to use to get coverage data. \--- org.jacoco:org.jacoco.agent:0.7.4.201502262128 androidJacocoAnt - The Jacoco ant tasks to use to get execute Gradle tasks. \--- org.jacoco:org.jacoco.ant:0.7.4.201502262128 +--- org.jacoco:org.jacoco.core:0.7.4.201502262128 | \--- org.ow2.asm:asm-debug-all:5.0.1 +--- org.jacoco:org.jacoco.report:0.7.4.201502262128 | +--- org.jacoco:org.jacoco.core:0.7.4.201502262128 (*) | \--- org.ow2.asm:asm-debug-all:5.0.1 \--- org.jacoco:org.jacoco.agent:0.7.4.201502262128 androidTestApk - Classpath packaged with the compiled 'androidTest' classes. No dependencies androidTestCompile - Classpath for compiling the androidTest sources. +--- com.android.support:support-annotations:22.2.1 +--- com.android.support.test:runner:0.3 | +--- com.android.support.test:exposed-instrumentation-api-publish:0.3 | +--- junit:junit:4.12 | | \--- org.hamcrest:hamcrest-core:1.3 | \--- com.android.support:support-annotations:22.2.0 -> 22.2.1 \--- com.android.support.test.espresso:espresso-core:2.2 +--- com.android.support.test.espresso:espresso-idling-resource:2.2 +--- com.squareup:javawriter:2.1.1 +--- javax.inject:javax.inject:1 +--- org.hamcrest:hamcrest-library:1.3 | \--- org.hamcrest:hamcrest-core:1.3 +--- com.android.support.test:rules:0.3 | \--- com.android.support.test:runner:0.3 (*) +--- org.hamcrest:hamcrest-integration:1.3 | \--- org.hamcrest:hamcrest-library:1.3 (*) +--- com.google.code.findbugs:jsr305:2.0.1 +--- javax.annotation:javax.annotation-api:1.2 \--- com.android.support.test:runner:0.3 (*) androidTestProvided - Classpath for only compiling the androidTest sources. No dependencies androidTestWearApp - Link to a wear app to embed for object 'androidTest'. No dependencies apk - Classpath packaged with the compiled 'main' classes. No dependencies archives - Configuration for archive artifacts. No dependencies compile - Classpath for compiling the main sources. +--- com.android.support:appcompat-v7:22.2.1 | \--- com.android.support:support-v4:22.2.1 | \--- com.android.support:support-annotations:22.2.1 +--- com.android.support:design:22.2.1 | +--- com.android.support:appcompat-v7:22.2.1 (*) | \--- com.android.support:support-v4:22.2.1 (*) \--- com.shamanland:fonticon:0.1.8
可以看出:com.android.support.test:runner:0.3
这个模块原本是依赖于com.android.support:support-annotations
22.2.0版本,但由于你又在gradle.build
中加了这行androidTestCompile 'com.android.support:support-annotations:22.2.1'
故gradle
就会下载com.android.support:support-annotations
22.2.1版本,而不会去下载com.android.support:support-annotations
22.2.0版本,因此com.android.support.test:runner:0.3
就会被强转变依赖于com.android.support:support-annotations
22.2.1版本
第二个问题,我还是利用上面gradle命令行打出的信息来看看:
debugApk - ## Internal use, do not manually configure ## +--- com.android.support:multidex:1.0.1 +--- com.android.support:appcompat-v7:22.2.1 | \--- com.android.support:support-v4:22.2.1 | \--- com.android.support:support-annotations:22.2.1 +--- com.android.support:design:22.2.1 | +--- com.android.support:appcompat-v7:22.2.1 (*) | \--- com.android.support:support-v4:22.2.1 (*) \--- com.shamanland:fonticon:0.1.8
仔细看上面的依赖,com.android.support:appcompat-v7:22.2.1
和com.android.support:design:22.2.1
,有共同依赖一个com.android.support:support-v4:22.2.1
库,且com.android.support:design:22.2.1
依赖于com.android.support:appcompat-v7:22.2.1
,但从打印出的信息可以看出com.android.support:design:22.2.1
依赖两个库的信息被打了星号标记,这标记意思是忽略这两个依赖的意思,也就是说不去下载这两个库。
从上面的两个例子来看,gradle
对于需要多个版本的依赖库来说,一般只会下载你配置导入那个版本,如果没有手动导入,那就会下载所有模块依赖的最新版本那个库,面对多个模块依赖同一个库,每个模块不都会去下载该依赖库,而是下载一个该依赖库,共享给多个模块。
修改配置文件为:
dependencies { compile 'com.android.support:appcompat-v7:22.2.1' compile ('com.android.support:design:22.2.1') { exclude group: 'com.android.support' } compile 'com.shamanland:fonticon:0.1.8' androidTestCompile 'com.android.support:support-annotations:22.2.1' androidTestCompile 'com.android.support.test:runner:0.3' androidTestCompile('com.android.support.test.espresso:espresso-core:2.2') { exclude group:'com.android.support.test' } }
打印信息:
$ gradlew -q app:dependencies ------------------------------------------------------------ Project :app ------------------------------------------------------------ _debugAndroidTestApk - ## Internal use, do not manually configure ## +--- com.android.support:support-annotations:22.2.1 +--- com.android.support.test:runner:0.3 | +--- com.android.support.test:exposed-instrumentation-api-publish:0.3 | +--- junit:junit:4.12 | | \--- org.hamcrest:hamcrest-core:1.3 | \--- com.android.support:support-annotations:22.2.0 -> 22.2.1 \--- com.android.support.test.espresso:espresso-core:2.2 +--- com.android.support.test.espresso:espresso-idling-resource:2.2 +--- com.squareup:javawriter:2.1.1 +--- javax.inject:javax.inject:1 +--- org.hamcrest:hamcrest-library:1.3 | \--- org.hamcrest:hamcrest-core:1.3 +--- org.hamcrest:hamcrest-integration:1.3 | \--- org.hamcrest:hamcrest-library:1.3 (*) +--- com.google.code.findbugs:jsr305:2.0.1 \--- javax.annotation:javax.annotation-api:1.2 _debugAndroidTestCompile - ## Internal use, do not manually configure ## +--- com.android.support:support-annotations:22.2.1 +--- com.android.support.test:runner:0.3 | +--- com.android.support.test:exposed-instrumentation-api-publish:0.3 | +--- junit:junit:4.12 | | \--- org.hamcrest:hamcrest-core:1.3 | \--- com.android.support:support-annotations:22.2.0 -> 22.2.1 \--- com.android.support.test.espresso:espresso-core:2.2 +--- com.android.support.test.espresso:espresso-idling-resource:2.2 +--- com.squareup:javawriter:2.1.1 +--- javax.inject:javax.inject:1 +--- org.hamcrest:hamcrest-library:1.3 | \--- org.hamcrest:hamcrest-core:1.3 +--- org.hamcrest:hamcrest-integration:1.3 | \--- org.hamcrest:hamcrest-library:1.3 (*) +--- com.google.code.findbugs:jsr305:2.0.1 \--- javax.annotation:javax.annotation-api:1.2 _debugApk - ## Internal use, do not manually configure ## +--- com.android.support:multidex:1.0.1 +--- com.android.support:appcompat-v7:22.2.1 | \--- com.android.support:support-v4:22.2.1 | \--- com.android.support:support-annotations:22.2.1 +--- com.android.support:design:22.2.1 \--- com.shamanland:fonticon:0.1.8 _debugCompile - ## Internal use, do not manually configure ## +--- com.android.support:multidex:1.0.1 +--- com.android.support:appcompat-v7:22.2.1 | \--- com.android.support:support-v4:22.2.1 | \--- com.android.support:support-annotations:22.2.1 +--- com.android.support:design:22.2.1 \--- com.shamanland:fonticon:0.1.8 _debugUnitTestApk - ## Internal use, do not manually configure ## No dependencies _debugUnitTestCompile - ## Internal use, do not manually configure ## No dependencies _releaseApk - ## Internal use, do not manually configure ## +--- com.android.support:multidex:1.0.1 +--- com.android.support:appcompat-v7:22.2.1 | \--- com.android.support:support-v4:22.2.1 | \--- com.android.support:support-annotations:22.2.1 +--- com.android.support:design:22.2.1 \--- com.shamanland:fonticon:0.1.8 _releaseCompile - ## Internal use, do not manually configure ## +--- com.android.support:multidex:1.0.1 +--- com.android.support:appcompat-v7:22.2.1 | \--- com.android.support:support-v4:22.2.1 | \--- com.android.support:support-annotations:22.2.1 +--- com.android.support:design:22.2.1 \--- com.shamanland:fonticon:0.1.8 _releaseUnitTestApk - ## Internal use, do not manually configure ## No dependencies _releaseUnitTestCompile - ## Internal use, do not manually configure ## No dependencies androidJacocoAgent - The Jacoco agent to use to get coverage data. \--- org.jacoco:org.jacoco.agent:0.7.4.201502262128 androidJacocoAnt - The Jacoco ant tasks to use to get execute Gradle tasks. \--- org.jacoco:org.jacoco.ant:0.7.4.201502262128 +--- org.jacoco:org.jacoco.core:0.7.4.201502262128 | \--- org.ow2.asm:asm-debug-all:5.0.1 +--- org.jacoco:org.jacoco.report:0.7.4.201502262128 | +--- org.jacoco:org.jacoco.core:0.7.4.201502262128 (*) | \--- org.ow2.asm:asm-debug-all:5.0.1 \--- org.jacoco:org.jacoco.agent:0.7.4.201502262128 androidTestApk - Classpath packaged with the compiled 'androidTest' classes. No dependencies androidTestCompile - Classpath for compiling the androidTest sources. +--- com.android.support:support-annotations:22.2.1 +--- com.android.support.test:runner:0.3 | +--- com.android.support.test:exposed-instrumentation-api-publish:0.3 | +--- junit:junit:4.12 | | \--- org.hamcrest:hamcrest-core:1.3 | \--- com.android.support:support-annotations:22.2.0 -> 22.2.1 \--- com.android.support.test.espresso:espresso-core:2.2 +--- com.android.support.test.espresso:espresso-idling-resource:2.2 +--- com.squareup:javawriter:2.1.1 +--- javax.inject:javax.inject:1 +--- org.hamcrest:hamcrest-library:1.3 | \--- org.hamcrest:hamcrest-core:1.3 +--- org.hamcrest:hamcrest-integration:1.3 | \--- org.hamcrest:hamcrest-library:1.3 (*) +--- com.google.code.findbugs:jsr305:2.0.1 \--- javax.annotation:javax.annotation-api:1.2 androidTestProvided - Classpath for only compiling the androidTest sources. No dependencies androidTestWearApp - Link to a wear app to embed for object 'androidTest'. No dependencies apk - Classpath packaged with the compiled 'main' classes. No dependencies archives - Configuration for archive artifacts. No dependencies compile - Classpath for compiling the main sources. +--- com.android.support:appcompat-v7:22.2.1 | \--- com.android.support:support-v4:22.2.1 | \--- com.android.support:support-annotations:22.2.1 +--- com.android.support:design:22.2.1 \--- com.shamanland:fonticon:0.1.8
比较上面两种配置compile
信息部分,后面一种配置的 com.android.support:design:22.2.1
就没有输出两个打星号的依赖库信息了。
因此遇到这两种情况是,是gradle
能自动帮我们解决掉了这两种情况,对于如何使用gralde
命令行查看库依赖(depends)请看官方文档
Jenkins
官网下载,如果官网下载存在问题,也可以在本站下载Jenkins。
Gradle
目前Android Studio
支持的是Gradle 2.8
版本,因此下载2.8版本的即可。Gradle 2.8,也可以本站下载。
官方主页Apache Tomcat。Windows下面建议下载32-bit/64-bit Windows Service Installer
版本。
Jenkins
将下载的jenkins.war
包直接放到tomcat
下的webapps
目录,启动tomcat
,在浏览器输入:http://127.0.0.1:8080/jenkins
Git plugin
,Gradle plugin
,Android Lint Plugin
插件点击Jenkins
首页的"Manage Jenkins
"链接,如下图:
进入设置,点击"Manage Plugins
",添加Git plugin
,Gradle plugin
,Android Lint Plugin
在打开的页面中,搜索并且安装插件
返回首页,点击" Manage Plugins
",然后进入页面中选择Configure System
,配置JDK
,Gradle
,Git
的选项。
设置JDK
首页点击"创建一个新任务
",如下图:
在接下来的页面中,输入工程的名字,并且选择"Freestyle project
"
设置Git
中源代码的路径,如果是使用SSH
证书认证的登陆,则在Credentials
中进行配置,如下图:
接下来配置触发构建的条件,目前我们设置为每天晚上3点,注意里面输入的是H 3 * * *
,每个字符之间都有一个英文的空格。
接下来,配置Gralde
的编译,在构建
项目中选择"Invoke Gradle Script
",如下图:
在选项的Tasks
栏目中输入clean build --stacktrace --debug
,如下图:
接下来,配置构建后操作
,一般增加Publish Android Lint results
,Archive the artifacts
这两项即可,具体的配置参考下图:
构建完成以后检查一下,如果在:
当前用户目录\.jenkins\jobs\Android\workspace\app\build\outputs\apk\
下面成功生成了APK
文件,则说明配置是成功的。
Mac 10.9 版本下使用Gradle 编译 Android 项目,报告错误
Failure initializing default system SSL context
使用 -debug 参数,得到的详细输出如下
2:34:15.273 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:50) 22:34:15.278 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:32) 22:34:15.279 [ERROR] [org.gradle.BuildExceptionReporter] at org.gradle.launcher.GradleMain.main(GradleMain.java:26) 22:34:15.281 [ERROR] [org.gradle.BuildExceptionReporter] Caused by: java.io.FileNotFoundException: /System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/security/cacerts (No such file or directory) 22:34:15.284 [ERROR] [org.gradle.BuildExceptionReporter] at java.io.FileInputStream.open(Native Method) 22:34:15.285 [ERROR] [org.gradle.BuildExceptionReporter] at java.io.FileInputStream.<init>(FileInputStream.java:106) 22:34:15.286 [ERROR] [org.gradle.BuildExceptionReporter] at org.apache.http.conn.ssl.SSLSocketFactory.createSystemSSLContext(SSLSocketFactory.java:299) 22:34:15.288 [ERROR] [org.gradle.BuildExceptionReporter] at org.apache.http.conn.ssl.SSLSocketFactory.createSystemSSLContext(SSLSocketFactory.java:366) 22:34:15.289 [ERROR] [org.gradle.BuildExceptionReporter] ... 147 more
可以看到,异常信息为
22:34:15.281 [ERROR] [org.gradle.BuildExceptionReporter] Caused by: java.io.FileNotFoundException: /System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Home/lib/security/cacerts (No such file or directory)
到具体的目录下面去看,这个文件果然是不存在的,有些版本链接到了一个不存在的目录,这种情况一般是经历过系统升级,往往会造成这个现象,另外,就是系统安装的Java 是Apple 提供的 Java 版本,而不是 Orcale 官网下载的独立版本。
解决方法是可以到Orcale 官网下载最新版本的 Java ,安装替换即可解决问题。
在命令行下使用
$ brew install FORMULANAME
就可以安装 FORMULANAME 对应的工具,它会处理好依赖关系,非常方便。默认情况下,安装最新版本。但是现在在使用 Gradle 的时候,最新版本的 Gradle 是1.10,但是编译Android 的项目失败,只能降级成 1.9 版本的才行,解决方法如下
1.查看 brew 支持哪些版本的 gradle
$ sudo brew versions gradle Warning: brew-versions is unsupported and may be removed soon. Please use the homebrew-versions tap instead: https://github.com/Homebrew/homebrew-versions 1.10 git checkout 2b10422 Library/Formula/gradle.rb 1.9 git checkout 5bab5e9 Library/Formula/gradle.rb 1.8 git checkout 9214e60 Library/Formula/gradle.rb 1.7 git checkout f826cc9 Library/Formula/gradle.rb 1.6 git checkout fff7c0b Library/Formula/gradle.rb 1.5 git checkout 57931e0 Library/Formula/gradle.rb 1.4 git checkout 0b7303a Library/Formula/gradle.rb 1.3 git checkout c259bda Library/Formula/gradle.rb 1.2 git checkout 9b7d294 Library/Formula/gradle.rb 1.1 git checkout 7941972 Library/Formula/gradle.rb 1.0 git checkout dff67fb Library/Formula/gradle.rb 1.0-rc-3 git checkout 5f9e348 Library/Formula/gradle.rb 1.0-rc-2 git checkout f72e33f Library/Formula/gradle.rb 1.0-rc-1 git checkout e2438cf Library/Formula/gradle.rb 1.0-milestone-9 git checkout c27c667 Library/Formula/gradle.rb 1.0-milestone-8a git checkout 69eb948 Library/Formula/gradle.rb 1.0-milestone-8 git checkout 34da975 Library/Formula/gradle.rb 1.0-milestone-7 git checkout 6a8c437 Library/Formula/gradle.rb 1.0-milestone-6 git checkout dae625d Library/Formula/gradle.rb 1.0-milestone-5 git checkout baff305 Library/Formula/gradle.rb 1.0-milestone-3 git checkout d9f2e06 Library/Formula/gradle.rb 1.0-milestone-4 git checkout 4b1230c Library/Formula/gradle.rb 1.0-milestone-2 git checkout 6801464 Library/Formula/gradle.rb 1.0-milestone-1 git checkout 0476235 Library/Formula/gradle.rb 0.9.2 git checkout 38b9338 Library/Formula/gradle.rb 0.9.1 git checkout f986d7d Library/Formula/gradle.rb 0.9 git checkout 45e09d7 Library/Formula/gradle.rb 0.8 git checkout e6f608f Library/Formula/gradle.rb
可以看到,支持 1.9 版本的。
2. 进入 brew 所在的git仓库
$ cd `brew --prefix`
3.复制粘贴刚才 brew versions gradle
命令的提示,我们需要1.9 版本的,因此执行
$ git checkout 5bab5e9 Library/Formula/gradle.rb
此时,本地仓库中的gradle 就被替换成了 1.9 的链接地址信息。
4. 安装
$ sudo brew install gradle ==> Downloading http://services.gradle.org/distributions/gradle-1.9-bin.zip
可以看到输出的信息已经是 1.9 的版本了。