【源码篇】安卓源码解析(持续总结)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 在Linux中,一切皆为文件。安卓底层也是基于Linux开发的。在我们了解源码目录的时候,我们要先知道这些源码的后缀名代表的是什么文件,什么意思,才能更好的去了解其中的原理。

前言


在Linux中,一切皆为文件。安卓底层也是基于Linux开发的。

在我们了解源码目录的时候,我们要先知道这些源码的后缀名代表的是什么文件,什么意思,才能更好的去了解其中的原理。



关于文件的后缀名(重要)


当您下载 Android 源码并解压缩后,会看到各种后缀名的文件。以下是一些常见的文件后缀名及其含义:

1..c:C 语言源代码文件。


2..cc/.cpp:C++ 语言源代码文件。


3..h/.hpp:头文件,用于声明函数、变量和类等信息。


4..mk:Makefile 文件,用于构建安卓系统和应用程序。


5..bp:Blueprint 文件,通常用于构建 Android 应用程序和库。


6..java:Java 语言源代码文件,常用于编写 Android 应用程序和框架层代码。


7..aidl:Android 接口定义语言文件,用于定义跨进程通信接口。


8..pro:Qt 项目文件,通常用于构建 Qt 应用程序。


9..sh:Shell 脚本文件,用于在 Linux 和 Unix 系统中执行命令。


10..o:编译生成的目标可执行文件,用于构建整个安卓系统链接成一个.img镜像文件。


11..s:在Linux源码中,.s后缀名的文件通常是汇编语言源代码文件。

它们包含了汇编语言程序的代码,可以被汇编器(如GNU Assembler)转换成可执行的二进制文件。通常,这些文件是为特定的处理器架构编写的,因此需要使用特定的汇编器来将其转换成机器码。在Linux内核开发中,.s文件常常用于编写内核代码的底层汇编实现。

12..so:共享库文件,在 Android 系统中广泛使用;


13..py:Python 脚本文件,在 Android 编译工具链等方面使用广泛;


14..rc:资源(固件)配置脚本;


15..txt/.md/.rst:文本(或文档)格式的说明或指南等。


16..dex :是一种二进制文件。它是 Android 应用的可执行文件格式之一,基于 Dalvik 虚拟机所使用的指令集,包含了应用程序在编译期间通过 Java 语言编写的代码、资源文件和元数据信息等内容,并经过编译成二进制形式后打包为 .dex 文件。具体来讲,.dex 文件的内部结构采用了压缩和索引化技术,以减小文件大小并提高访问速度。因此,.dex 文件无法使用文本编辑器进行查看和修改,需要使用专门的工具例如 dexdump 或 jadx 等才能够进行反汇编和分析。


17..img :是一个常见的文件扩展名,它表示磁盘映像文件。磁盘映像文件是一个包含了磁盘、硬盘或者光盘等设备中所有数据和元数据的单一文件。通常情况下,这些文件包含了操作系统、文件系统、应用程序和其他数据。通过将整个磁盘内容存储在一个文件中,可以方便地复制、备份和传输磁盘中的所有数据和设置。例如,针对嵌入式系统和虚拟化环境,开发人员经常使用磁盘映像文件来创建和部署硬件镜像和虚拟机等系统。除了 .img 扩展名,也有其他类型的磁盘映像文件,如 .iso、.vhd、.qcow2、.dsk 等。


总体来说,Android 操作系统开发需要涉及多种技术方向,所需的相关工具、库、平台等也是多样而复杂的。因此,在不同层级、场景或模块中所涉及的各种文件类型也不尽相同。


1)详解.o文件(目标文件)


在Android源代码中,.o文件是指编译生成的目标文件,其包含二进制机器码和符号表等信息。当我们使用GCC等编译器将C或C++源代码编译成可执行文件时,编译器的输出文件是一系列.o文件。这些.o文件包含目标代码(机器指令)和符号表。

在Android系统中,这些.o文件通常被静态链接成一个单独的可执行文件或动态链接库(.so文件)。当我们构建整个Android系统时,所有的.o文件会被链接成一个.img镜像文件,该镜像文件是最终的系统映像,可以部署到设备上运行。

在开发过程中,对于某些特殊情况下需要分析和调试代码时,也可以使用工具如objdump等将.o文件反汇编为汇编代码以进行分析或调试。


详情参考博主本篇文章。



Android 源码中所有目录的作用简介:


1.build: 包含构建 Android 系统及应用所需的 Makefile 文件和脚本等,用于构建 Android 系统。


2.bionic: 包含 Android 的标准 C 库实现,用于支持 Android 系统的运行。


3.bootable: 包含 Android 启动引导相关代码,包括了 bootloader、启动器等,用于启动 Android 系统。


4.cts: 包含兼容性测试套件(CTS),主要用于验证 Android 设备是否符合 Android 应用编写规范以及 API 是否正确实现等问题。


5.developers: 包含一些向开发者提供的工具和示例程序代码。


6.development: 包含 Android 开发 SDK、NDK 等相关代码。


7.device: 主要包含各个设备制造厂商、不同型号设备的驱动代码,以及由 Google 提供的 Nexus 系列设备的驱动程序和硬件抽象层(HAL)实现。


8.docs/source.android.com/reference/:包括所有 Android 开发者文档中提到过的 API 参考文档,文档内容涵盖了多个版本的 SDK 和 NDK 以及各种重要类库和系统服务。


9.external/xxx:包括很多第三方开源库或工具,如 WebKit、SQLite、OpenSSL 等,用于支持 Android 系统的运行。


10.frameworks:包含了 Android 操作系统核心框架及应用程序相关源码,包含 Android 系统的核心框架,包括应用程序框架、系统服务和应用程序接口。其中:


11.base 目录包括了 Android 框架核心实现及系统应用程序。


12.samples 目录包含一些列举某些应用场景使用案例示例代码 ,主要为对这些API使用方法进行说明。


13.opt 目录下包含 Google 官方推出的一些非必需但值得关注的组件和应用程序如 Google Play services,Google Maps API 等。


14.tvdpi 目录下是针对 TV 设备而设计的高分辨率图标集合。


15.hardware: 接收来自底层硬件抽象层(HAL)的方法调用,并将其转换为适当格式并发送到 Linux 用户空间上层服务。该目录还包括与硬件相关联的其他组件,如视频编解码器、音频处理器等,用于支持硬件设备的访问。


16.kernel: 包括 Linux 内核源码及针对 Android 设备所做出修改后源码,用于支持 Android 系统的运行。


17.libcore:Java 类库核心代码实现部分。Android 使用自行开发的类库替代了原生 Java 实现,为此将 JRE 分为两部分:核心类库和 Dalvik 虚拟机。


18.out: 是编译后生成可执行文件 (binaries) 的输出目录。


19.packages/apps:包含大量系统预装应用程序和 Google 应用程序,例如电话、短信、浏览器等。


20.prebuilts: 包含经过编译后并打好标记压缩成 tar.gz 或 zip 格式文件以供下载使用的二进制文件。该目录下有两个子目录:


21.sdk, 额外提供了 SDK Manager 和 AVD Manager 工具,帮助开发人员管理所需要版本 APK 和模拟器镜像;


22.ndk,构建 C/C++ 模块需要使用 NDK 工具链,里面装着能够生成可执行库(so)格式文件所需安装环境。


23.system/core: 包含操作系统基础组件, 如 init 进程管理器、logd 日志记录守护进程、adb 外围设备调试守护进程等 。


24.vendor: 主要包含了供应商提供的驱动程序和硬件抽象层(HAL)实现。


25.dalvik:包含 Dalvik 虚拟机和相关库,用于运行 Android 应用程序。

以上为大致概述 ,更加详细信息请参考 Android 官方网站。



关于shell脚本


1) 安卓源码使是用shell脚本编写的吗?


安卓源码不是用Shell脚本编写的。Android操作系统是使用C、C++和Java等编程语言编写的。其中,内核层面主要使用C和汇编语言进行编写,应用层面则主要使用Java和C++编写。


除了这些主要的编程语言之外,Android还涉及到其他一些辅助性质的语言工具,比如XML、HTML、JavaScript等。此外,在Android开发中也会用到Shell脚本,但这些脚本更多地被用于构建Android应用或管理系统设置,而不是直接编写Android源代码。


2)在安卓脚本中哪里会用到shell脚本?


在Android源码中,Shell脚本常常用于构建Android应用或管理系统设置。以下是一些常见的使用Shell脚本的场景:

1.编译构建:Android源码中使用了大量的Shell脚本来自动化编译和构建Android系统。这些脚本通常被保存在项目的根目录下,并且都有相应的Makefile文件。

2.系统初始化:在安装完一个新的Android系统之后,我们需要进行一些初始化工作来配置系统环境、安装软件等。这时候可以使用Shell脚本来完成这些任务,比如在启动时运行一个定制化的Shell脚本。

3.系统升级:当我们需要对已经安装的Android系统进行更新时,通常需要执行一些升级操作,比如备份数据、下载升级包、解压文件并覆盖原有文件等。这些操作通常也是通过Shell脚本实现的。

4.应用部署:在开发Android应用时,我们通常需要将应用程序打包为APK文件并部署到设备上进行测试。这个过程同样可以通过Shell脚本自动完成。

总之,在构建、管理和部署Android系统或应用时,Shell脚本是一个非常有用的工具。


3)shell脚本就是一堆命令行的集合吗


是的,Shell脚本通常被定义为一组命令行的集合。Shell脚本是由一系列的命令、流程控制语句和函数等组成,用于实现特定的任务或操作。比如,在Linux系统中,我们可以使用Shell脚本来执行诸如创建目录、复制文件、备份数据等操作,并且可以将这些操作组合在一起以实现更复杂的任务。

Shell脚本可以通过编辑器创建和编辑,并保存为扩展名为.sh的文件。然后,我们可以在命令行界面中执行该脚本,让程序自动完成一系列预设好的任务。

需要注意的是,Shell脚本并不是所有的命令行都能够支持的,在不同的操作系统中可能会存在差异性。此外,在编写Shell脚本时也需要注意语法和错误处理等问题。


关于bootimage


1)bootimage是啥东西


Android系统中的boot image(启动镜像)是一个包含了一些固定功能的镜像文件。在 Android 设备上,boot image 负责加载 bootloader(引导程序),在设备加电后第一个被加载的程序。

因此,boot image 对于整个系统的启动过程非常关键。



Android 的 boot image 与 Linux 的 boot image 类似。它包含了 kernel、ramdisk 和一些启动参数,例如命令行参数和硬件信息等。

kernel 是操作系统的核心部分,ramdisk 则是一个临时文件系统用来提供一些系统运行时需要用到的文件和配置。在默认情况下,boot image 还会包括对 ROM 签名的验证信息以确保设备只能使用由认证的厂商发布的 ROM。

每个 Android 设备有其特定的 boot image,并且这些 Image 可能会因为不同产品型号、制造商、操作系统版本等而有所不同。若需要修改设备启动流程时,通常需要先解开 System Partition 的限制来可以修改 boot image 文件。

通俗易懂版:

当我们打开一部 Android 设备时,首先会加载一个叫做 “boot image” 的文件。这个文件的作用是启动设备并加载操作系统的核心部分(kernel)以及一些必要的临时文件。它还包含设备的一些硬件信息和启动参数,确保设备只能使用认证厂商发布的系统版本。

每个 Android 设备都有自己特定的 boot image 文件,它可能因为型号、制造商或操作系统版本不同而有所差别。如果我们需要改变设备的启动流程,通常需要解除一些限制才能修改 boot image 文件。

2)镜像文件是啥


镜像文件(image file)是指一个包含着整个文件系统、操作系统等一系列数据的文件。通常情况下,我们可以通过制作一个镜像文件将某个设备或目录的所有数据备份下来。镜像文件可以方便地对数据进行传输和存储,并可以用于恢复被损坏或删除的数据。

在计算机科学领域中,镜像文件还经常用于创建虚拟化环境,使得我们能够在同一台机器上模拟多个硬件或软件环境。例如,在虚拟机中运行操作系统时就需要使用到操作系统的镜像文件。



关于.mk文件


1)为什么有些目录中有.mk文件,有些目录没有


在 Android 源代码中,每个模块的目录中都可能会包含一个或多个 .mk 文件。.mk 文件是 Android 自动构建系统使用的文件,在编译和构建过程中发挥重要作用。

一些目录因为没有可编译的源代码或者它们不是被直接编译的模块,所以没有 .mk 文件。这些目录通常包含与特定项目或功能相关的资源文件、文档或测试代码等其他杂项文件。

每个 .mk 文件通常定义了一个变量集合,这些变量被用于指定该模块的源文件、依赖项、编译选项等信息。当 Android 编译系统构建应用程序时,它会自动找到适当的 .mk 文件并使用其中定义的信息来确定需要编译哪些源文件、如何链接库和应用程序,以及生成哪些输出文件等。

总之,每个模块的目录是否有 .mk 文件取决于该模块是否需要被编译和构建,并且 .mk 文件是帮助 Android 构建系统自动化处理这些工作所必需的。


2)有的目录同时有Android.bp和Android.mk文件,不都是用于构建安卓系统和应用程序的吗?


是的,Android.mk和Android.bp文件都用于构建Android系统和应用程序。

Android.mk文件是旧版的构建脚本,它基于GNU Make构建系统。这个构建脚本非常灵活,能够使用各种工具链、处理复杂的编译和链接选项,并可以扩展自定义编译规则。

但是,由于它是基于GNU Make的语法,可能会难以理解和维护。

相比之下,Android.bp文件是新一代的构建脚本,使用了Soong构建系统。它采用了更简单、更规范的语法,并提供了更高的可读性和可维护性。此外,Soong还支持并行化编译、增量编译和资源优化等功能。


在实际开发中,有些目录同时存在这两种构建脚本可能是因为遗留问题或者需要兼容旧版本的需求。但新项目或者更新后的Android版本应该优先使用Android.bp文件进行构建。

3)CleanSpec.mk文件有什么用


CleanSpec.mk文件是Android源代码中的一个文件,它包含了区分不同设备的清除规则。当使用make clean命令清除Android源代码时,它会根据这些规则来删除不必要的文件,以释放磁盘空间并加快构建速度。

具体来说,CleanSpec.mk文件主要定义了以下内容:

1.定义需要清除的目录和文件

CleanSpec.mk可以定义一组要清除的目录和文件列表,以便删除临时数据、构建输出、缓存和其他不必要的文件。

2.定义设备类型和特定目录

针对特定型号或厂商的设备,可能需要执行不同的清理操作。CleanSpec.mk提供了针对特定目录和设备类型进行规则定义的功能。

3.定义保留文件和目录

在进行清理时也需要确保一些重要的目录或者文件得到保留,以免误删数据。因此,在CleanSpec.mk中还可以定义保留列表。

4.定制化清理操作

用户可以在CleanSpec.mk中自定义一些清理操作脚本,以满足个性化需求。

总之,CleanSpec.mk是构建Android源码过程中非常重要的一个文件,在节省时间和磁盘空间上都有很大作用。



关于.flags文件


1)proguard.flags文件是什么


ProGuard是一个用于缩减、优化和混淆Java代码的工具,可以在打包前对Java应用程序进行混淆、压缩和优化,以减小应用程序的大小并提高应用程序的安全性。

proguard.flags文件是一个文本文件,其中包含了配置ProGuard工具执行优化、缩减和混淆时所需的参数。

该文件被放置在Android Studio项目根目录下,并且在构建APK时使用。默认情况下,该文件为空,需要手动添加要使用的ProGuard选项。

在proguard.flags文件中,可以使用一系列指令来配置ProGuard的行为。例如:


# 去除无用类
-dontwarn javax.activation.**
-dontwarn org.apache.xpath.**
# 保留特定类或方法不被混淆
-keep class com.example.model.** { *; }
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
# 指定入口点类
-dontoptimize
-dontobfuscate
-keep class com.example.MainActivity {
  public static void main(java.lang.String[]);
}
# 导出所有公共类和方法
-keep public class * {
  public protected *;
}

这些指令可以告诉ProGuard如何处理代码,例如去除无用类、保留特定的类或方法、指定入口点类等等。在构建apk时,Android Studio将使用此文件中的指令自动调用ProGuard来处理代码,并生成一个新的APK。



GNU Make构建系统和Soong构建系统


1)GNU Make构建系统和Soong构建系统区别


看完第二个问题你肯定有些疑问,什么是GNU Make构建系统,什么是Soong构建系统?

那我们就来讲讲。

GNU Make是一个经典的构建系统,它基于Makefile文件,并使用一系列的规则和指令定义了如何编译源代码、生成可执行文件等步骤。使用Make可以轻松地构建C/C++项目,但对于Android应用程序来说,Make的构建过程较为繁琐。

Soong是Google在Android 8.0中引入的新一代构建系统,它基于脚本语言 (Python)编写,可以自动检测依赖项并快速构建Android应用程序。与GNU Make不同,Soong把所有相关文件都放在同一个目录下,并支持如下特性:

● 支持静态和动态库

支持64位代码生成

支持多个ABI

自动化检测依赖项

此外,Soong还支持同时使用多个不同版本的NDK工具链。相对于GNU Make而言,Soong比较新且灵活,它更适合用于构建大型、复杂的Android应用程序。


2)GNU Make的语法规则


GNU make是一种构建自动化工具,它可以自动查找和更新文件的依赖关系,并且只执行需要更新的部分。下面是makefile的基本语法:

target: dependencies
    commands

其中,target是要生成的目标文件名、dependencies是生成目标文件所需要依赖的文件名列表,commands是用于生成目标文件的命令序列。

举个例子,下面是一个简单的Makefile例子:

hello: hello.o
    gcc -o hello hello.o
hello.o: hello.c
    gcc -c hello.c
clean:
    rm -f hello.o hello


这个Makefile定义了三个目标:hellohello.oclean

hello.o 依赖于 hello.c, 执行命令 gcc -c hello.c, 表示将源码编译成目标文件。

hello 依赖于 hello.o, 执行命令 gcc -o hello hello.o, 表示将目标文件链接成可执行程序。

clean 不依赖于任何其他目标,执行命令 rm -f hello.o hello, 表示删除编译过程中产生的中间文件。

在终端中执行 make targetName 即可根据 Makefile 中定义的规则来生成对应的目标。

除此之外,还可以使用变量、函数、条件判断等高级特性来构建更为复杂的 Makefile。


3)Soong构建系统的语法规则


Soong是Android构建系统中使用的一个工具,用于管理构建时所需的资源、源代码和其他文件。它具有以下语法规则:

1.每个Soong模块都必须包含一个Android.bp文件,其中定义了该模块的属性和依赖项。

2.Soong支持基本的数据类型,如字符串、整数、布尔值和列表等。

3.属性可以是可选的或必需的,并可以由默认值或空值指定。

4.依赖关系可以指向其他模块或文件,并可以通过标签指定特定属性。

5.Soong还支持与其他系统交互,如CMake、ndk-build和Java Gradle插件等。

6.在Android.bp文件中可以使用if-else语句来控制构建逻辑。这使得开发人员可以根据特定条件自定义构建过程。

7.Soong支持与Makefile相似的include语句,以包含其他文件或模块属性。

以上是Soong构建系统的一些语法规则。更详细的信息,您可以参考Google Android文档中有关Soong的介绍。

4)Android.bp文件的基本语法


Android.bp文件是Soong构建系统中使用的文件,用于定义模块的属性和依赖项。以下是Android.bp文件的基本语法:

1.每个模块都必须由一个rule来定义,rule包含一个名称和一组属性。

2.属性可以是可选的或必需的,并可以由默认值或空值指定。

3.Soong支持基本的数据类型,如字符串、整数、布尔值和列表等。

4.依赖关系可以指向其他模块或文件,并可以通过标签指定特定属性。

5.如有需要,Soong还支持if-else语句和函数调用。

6.在一个Android.bp文件中,可以定义多个rule,每个rule代表一个不同的模块。

下面是一个简单的Android.bp文件示例:

cc_binary {
    name: "hello",
    srcs: ["hello.c"],
}


在这个示例中,cc_binary是一个规则,用于编译C语言二进制文件。其中name属性表示该模块的名称为"hello",srcs属性表示该模块源代码为"hello.c"。

5)GNU Make构建系统是怎么构建安卓系统的

当使用GNU Make构建一个包含多个源代码文件的C/C++项目时,一般需要定义一个Makefile文件。该文件中包含了一系列规则和指令,用于描述如何编译源代码、生成目标文件、生成可执行文件等步骤。

以下是一个示例Makefile文件:

CC = gcc
CFLAGS = -Wall -g
app: main.o foo.o bar.o
  $(CC) $(CFLAGS) -o app main.o foo.o bar.o
main.o: main.c common.h
  $(CC) $(CFLAGS) -c main.c
foo.o: foo.c common.h
  $(CC) $(CFLAGS) -c foo.c
bar.o: bar.c common.h
  $(CC) $(CFLAGS) -c bar.c


这个Makefile文件定义了三个目标:appmain.ofoo.o。其中,app是可执行文件,依赖于main.ofoo.obar.o三个目标;而后两者分别代表编译好的目标二进制文件,分别依赖于相应的源代码文件和头文件。

在终端中执行命令 make app 后,GNU Make会按照规则依次执行各个指令来构建整个项目。具体过程如下:

1.检查是否需要重新编译 main.c, foo.c, bar.c

2.编译 main.c, 生成目标文件 main.o

3.编译 foo.c, 生成目标文件 foo.o

4.编译 bar.c, 生成目标文件 bar.o

5.连接三个目标二进制文件,生成可执行文件 App

以上就是 GNU Make 构建 C/C++ 项目的基本流程。当源码数目增多时,需要定义更多的规则和指令来方便管理整个项目。


关于app中目录


1)在安卓源码中/packages/apps/Camera2/中有个res和一个res_p有区别吗?


在Android源码中,/packages/apps/Camera2/目录下包含两个res目录:res和res_p。这两个目录之间的区别如下所述:

1.res目录:这是Camera2应用程序的主资源文件夹,其中包含应用程序的所有资源,如布局、字符串、图像等。

2.res_p目录:这是Camera2应用程序的“product”资源文件夹,包含针对特定产品或设备的资源文件,例如分辨率、采样率、相机驱动等。根据不同的产品或设备要求,res_p目录中的资源可能会有所不同。

因此,Camera2应用程序将其通用资源放置在res目录中,并根据需要在res_p目录中添加特定于产品或设备的资源以使其更加适配。

目录
相关文章
|
23天前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
58 0
|
23天前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
48 0
|
23天前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
54 0
|
23天前
|
安全 Java 程序员
Collection-Stack&Queue源码解析
Collection-Stack&Queue源码解析
68 0
|
4天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
21 3
|
21天前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
48 5
|
23天前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
|
23天前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
5天前
|
安全 5G Android开发
安卓与iOS的较量:技术深度解析
【10月更文挑战第24天】 在移动操作系统领域,安卓和iOS无疑是两大巨头。本文将深入探讨这两个系统的技术特点、优势和不足,以及它们在未来可能的发展方向。我们将通过对比分析,帮助读者更好地理解这两个系统的本质和内涵,从而引发对移动操作系统未来发展的深思。
15 0
|
23天前
|
算法 Java 程序员
Map - TreeSet & TreeMap 源码解析
Map - TreeSet & TreeMap 源码解析
29 0

推荐镜像

更多