环境:
操作系统: macOS 11.6
shell: zsh 5.8 (x86_64-apple-darwin20.1.0)
Android Studio 版本: Android Studio Bumblebee | 2021.1.1
JDK: openjdk version "11.0.14" 2022-01-18
一、起因
2021年4月分左右 Android Studio 4.2 (Android Studio 以下简称AS)发布了。AS 4.2 对其绑定的JDK进行了升级,开始使用OpenJDK11,之前使用的是OpenJDK8,这些信息在安卓开发者网站上可以看到,如下:
AS2.2.1+ 绑定 OpenJDK8
https://developer.android.google.cn/studio/releases#new_1
AS4.2+ 绑定 OpenJDK11
https://developer.android.google.cn/studio/releases#ki-key-keystore-warning
tips: AS是在 Jetbrains 的 IntelliJ (IntelliJ社区版是开源的) 的基础上二次开发而来的。IntelliJ 是用java开发,因此要运行它,机器上就必须有一个java运行时环境,为了方便使用者,IntelliJ 就绑定了一个 JRE(Java Runtime Environment)。位于 /Applications/Android Studio.app/Contents/jre
目录。
为了跟上谷哥的步伐,我升级了JDK。从Oracle的JDK8 升级到了OpenJDK11。
这一升级,就导致了在AS中无法查看Java标准库的源代码。之前 Command+Click
可以跳转到类的源代码,现在Command+Click
虽然可以跳转到源代码处,但是源代码是反编译出来的 —— 没有注释说明,格式混乱,根本没法阅读!
二、导致问题的具体原因
简单来说就是,AS到不到标准库中的类所对应的源代码文件 (源代码缺失)。
具体原因:
OpenJDK11发布的时候将源代码 和 二进制文件 进行了拆分,即OpenJDK11的二进制包中是不包含源代码的。说的更具体一点就是:OpenJDK_HOME
目录下是没有src.zip文件的。AS会加载JAVA_HOME/src.zip
,并建立索引,当我们在代码中点击 (Command+Click
) 标准库中的某个类(如HashMap)时,就会寻找src.zip中相应的类的源文件,然后展示。当JAVA_HOME
目录下没有src.zip文件时,AS无法找到类对应的源文件,于是AS会反编译字节码文件然后展示。
说明:
- JAVA_HOME 我们配置的环境变量,其实就是JDK的HOME目录。如,我的机器上的JAVA_HOME配置的就是
/Users/stone/my_program/AdoptOpenJDK-11/Contents/Home
- OpenJDK_HOME 是 JDK的安装目录。例如我的机器上这个目录是
/Users/stone/my_program/AdoptOpenJDK-11/Contents/Home
- src.zip 是JDK源代码(更准确的说 src.zip压缩文件是Java标准库的源代码,后面还会说到,就不展开了)
Oracle的JDK8的Home目录下有源代码文件,即 有src.zip。
OpenJDK8的Home目录下也是有src.zip文件的。
$ cd $JAVA8_HOME
$ ls
COPYRIGHT include
LICENSE javafx-src.zip
README.html jre
THIRDPARTYLICENSEREADME-JAVAFX.txt lib
THIRDPARTYLICENSEREADME.txt man
bin release
db src.zip
# JAVA8_HOME 是我设置的环境变量, 值为:
# /Library/Java/JavaVirtualMachines/jdk1.8.0_77.jdk/Contents/Home
OpenJDK 11的Home目录下没有src.zip文件
$ cd $JAVA11_HOME
$ ls
NOTICE bin bundle conf include jmods legal lib man release
# JAVA11_HOME 是我设置的环境变量,值为:
# /Users/stone/my_program/AdoptOpenJDK-11/Contents/Home
AS 默认的 jre 中也是没有源代码的,即jre目录下没有 src.zip
$ cd $AS_JRE_HOME
$ ls
bin conf legal lib release
# AS_JRE_HOME 表示AS所绑定的jre的目录,例如,我的机器上其值为:
# /Applications/Android Studio.app/Contents/jre
三、解决
解决:
下载JDK对应版本的源代码 并 放入到 JDK_HOME 目录下,即可!
具体操作:
0x01、下载JDK对应的源代码
下载地址:https://adoptium.net/releases.html?variant=openjdk11&jvmVariant=hotspot
选择版 OpenJDK11 (如果要下载JDK的二进制包,需要选择对应的操作系统 和 相应的CPU架构)
如果你之前安装的是比较老旧的JDK二进制包,那么你可以重新下新的二进制包 (推荐下载.tar.gz
格式的二进制包,.tar.gz
就是个压缩文件,解压就可以使用了,而.pkg
格式的二进制包需要安装,安装时会在文件系统的不同目录写入文件,这是侵入式的,卸载的时后还得找到安装时写入的文件逐个删除,太麻烦了),如下:
Mac系统中,网络上下载的OpenJDK解压后,命令行工具是无法执行的, 需要进行授权:
$ sudo xattr -r -d com.apple.quarantine /Users/stone/my_program/AdoptOpenJDK-11
# 最后一个参数指定到JDK解压后的根目录即可,当然也可以指定到Home目录
# 不授权执行JDK中的命令行工具
$ java # 或者 javac, javah, jstat, ...
不授权执行命令行工具,会出现下面的提示框 (偷下懒,用了之前安装GraalVM时截的图):
注意: 源代码和JDK的版本必须一致,否则查看标准库的源代码时,跳转到源代码的所在行可能是错误的!!
因为不同版本的JDK的源代码之间存在一定的差异!
例如,你编写的程序中使用的JDK的版本是11.0.13
,而源代码的版本是11.1.72
,当你在自己编写的Java源文件中 Command+Click
查看某个标准库中的类的源码时,会打开一个11.1.72
版本的Java源文件,如果你 Command+Click
点击的是一个方法,那么跳转到源文件的行数可能就是错误的,因为两个内容存在差异的文件的同一个方法所在的行就有可能不在同一行 (当然也有可能在同一行)!!
0x02、解压前一步所下载的JDK源代码压缩文件 并 制作Java标准类库的源代码压缩包src.zip
前一步下载下来的源代码并不能直接使用,因为这个压缩文件中不仅包含Java标准库的源代码,还包括hotspot、java命令行工具 ... 等等一系列东西的源代码。
因此,我们需要解压这个压缩包,然后只打包Java标准库的那部分源代码。
例如: 我下载的是JDK源代码文件是 OpenJDK11U-sources_11.0.14_9.tar.gz
,在finder中双击解压 (默认是调用的系统归档试用工具.app
应用解压)即可,当然,你也可以使用tar/gzip/gunzip
等命令来解压。
解压后的目录结构如下:
这个目录下的源文件就是Java标准库的源文件,因此我们进入到此目录下,让后制作src.zip文件。
# 我们是如何确定src.zip文件的内部结构的??
# Oracle的JDK8不是有src.zip源文件嘛,我们直接查看一下不就可以确认了
# 进入到Oracle JDK8中确认src.zip文件的内部结构。
$ cd $JAVA8_HOME
$ unzip -l src.zip # 输出了其他无用信息, 我们换一种方式来查看zip包中的结构
# 查看src.zip包中的结构
$ tar -tf src.zip
$ tar -tf src.zip | head -n 20 # 输出太多,限制一下,只显示前20条
# 经过上面的操作,可以确认,src.zip中就是java类的源文件 (按package对应的目录结构存放),没有其他目录路结构。
# 这样就可以确认 /src/java.base/share/classes 目录下的内容,就是我们要打包的内容
# 那么接下来就制作一个src.zip
$ cd /src/java.base/share/classes # 进入到目标目录, 这个是JDK的解压目录,可以按自己的具体情况进行替换
$ zip -r src.zip ./* # 打包当前目录下的所有内容
# 也许你会有一个疑问,zip打包当前目录下的所有内容,而生成内容也在当前目录下,那么生成内容会不会也被打包呢??
# 答案是否定的。
# 我们可以对生成的src.zip进行验证,验证方法如下:
$ tar -tf src.zip > ~/temp/dir-list.txt # 列出刚刚生成的src.zip中的内容并输入到一个filelist.txt的文件中
# 接下来打开filelist.txt文件查看一下其中有没有src.zip。
# 当然,filelist.txt文件中并没有出现src.zip
0x03 将前一步制作的 src.zip
文件复制到缺少源代码的JDK的HOME目录中。
然后重启一下AS (其实也不用重启,我复制完src.zip后,发现AS马上就在重建索引了 —— AS底部可以看到)。
至此,我们就可以愉快的阅读OpenJDK的源代码啦 !!
前面说过,下载JDK源代码和二进制包的时候,二者版本必须相同,否则就不能准确的查看源代码了!这个需要特别注意!
tips: AS是是如何使用指定版本的JDK来编译我们的java代码的呢?
编译我们自己写的Java源文件所使用的JDK,是可以动态配置,操作如下:
在AS中, 按 Command+,
打开 Preferences
界面, 然后设置JAVA_HOME 即可!! AS就是使用JAVA_HOME
所指定的JDK的 。