赞
踩
创建项目的时候 IDE 在项目的根目录会生成 gradlew 可执行文件,gradlew
中很多逻辑是判断系统类型的,gradlew.bat 是
windows下的实现思路一样的。这里的主要的逻辑:
先定义了 java 程序执行需要的 CLASSPATH 目录
然后把 JAVACMD 设置为 java 的入口文件
最后把开发者定义的 JAVA_OPTS、DEFAULT_JVM_OPTS、进程名都拼接为一串参数执行
文件最后一行的 exec 开始打包,可以替换为 echo 看具体执行的命令是啥
上文中提到的 CLASSPATH 是 gradle 的入口 jar 实际目录在项目根目录
./gradle/wrapper/ 下,并不是 gradle 的编译主逻辑,根据 gradlew
可执行文件指定的入口类 org.gradle.wrapper.GradleWrapperMain
在里面找到了 main 函数,主要逻辑是:
加载项目目录下的 gradle.properties 到环境变量
解析同目录下的 gradle-wrapper.properties 用 Install 下载指定的url 到本地
BootstrapMainStarter 加载真正的 org.gradle.launcher.GradleMain
进行打包,如果看源码去目录
~/.gradle/wrapper/dists/gradle-6.7.1-bin/bwlcbys1h7rz3272sye1xwiv6/gradle-6.7.1/lib
这样设计的好处应该是把 gradle 版本和项目解耦,方便替换 gradle
的版本,如果想自定义 gradle 版本或者因为国内访问 url
超时,可以直接替换配置文件的 distributionUrl
GradleUserHomeLookup 里主要声明了默认目录 ~/.gradle
WrapperExecutor 并不是线程池,gradle 代码特色是只要切换一层逻辑就会有
xxxExecutor,Install 和 BootstrapMainStarter 实际是串行的
这里使用 org.gradle.debug=true 打断点不生效,因为这里是纯 java 进程没有处理任何
gradle 参数,应该使用 java
-agentlib:jdwp=transport=dt_socket,server=y,address=5006 -classpath
./gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain
installdebug 和 jdb -attach localhost:5006
两个命令行进行断点调试
GradleMain.main-> ProcessBootstrap.run 这是一个通用的替换类加载器的反射
org.gradle.launcher.Main.run-> .doAction
DefaultCommandLineActionFactory.convert->WithLogging.execute
ExceptionReportingAction.execute
NativeServicesInitializingAction.execute
DefaultCommandLineActionFactory.ParseAndBuildAction.execute
DefaultExecActionFactory.newExec->DefaultExecHandleBuilder
DefaultExecHandle.start
ProcessBuilder.start 开启了新的线程
GradleDaemon.main > ProcessBootstrap.run 是新进程的入口,这里可以用
org.gradle.debug=true 打断点调试了
DaemonMain.doAction 创建 Daemon 和 DaemonServices,同时在 DaemonServices
的构造方法初始化 GlobalScopeServices 内部使用 DefaultServiceLocator 加载
“META-INF/services/”配置的所有 PluginServiceRegistry,确定了 gradle
能处理哪些任务
DaemonServerConnector
的实现类DaemonTcpServerConnector(TcpIncomingConnector).start()
当每次执行./gradlew installDebug 的时候 TcpIncomingConnector 会收到
ConnectCompletion 再包装成 SynchronizedDispatchConnection(SocketConnection)
分发给 DefaultIncomingConnectionHandler.handle 切换线程转到
ConnectionWorker.run ,经过DefaultDaemonConnection.receive 读取到Command
实际类型为
org.gradle.launcher.daemon.protocol.Build(ExecuteBuildAction(StartParameterInternal(DefaultTaskExecutionRequest(args))))
里面包含里命令的字符串,然后handleCommand
处理数据。这是一个死循环,这个进程前面的逻辑只执行一次
DaemonCommandExecuter.executeCommand-> DaemonCommandExecution. proceed
顺序执行下面的 action,都是在 DaemonServices.createDaemonCommandActions 创建
Daemon的时候准备好的,实例创建一次执行多次。前面都是验证是否可以执行的,最后一个
ExecuteBuild 触发 BuildExecuter。这种一层套一层的设计思路在 gradle
源码中十分普遍
继续往下走主流程,又是一层套一层的执行逻辑,代码风格上 16
点像装饰,这条像代理。我猜不是同一个开发写的,经过同一个老板 review
的,代码风格不一样但是执行逻辑完全一样
LauncherServices.createActionExecuter 创建了下面这些Runner
ServiceRegistry 的主要实现类有
DaemonServices、DaemonClientServices、ConnectorServiceRegistry
都是随进程存活
PluginServiceRegistry 的主要实现类有
DependencyServices、LauncherServices、ExecutionServices、MavenPublishServices、XcodeServices
分别对应了 gradle
的不同能力,每种能力对不同的生命周期可以添加不同的处理方式,生命周期从长到短依次为:
gradle 里的 execute 大部分不是创建线程执行,只是因为当前逻辑需要分层。可能是要加 log,可能是为了抓
Exception 不让进程崩溃,也可能是为了统计执行时间等等
gradle 里的接口定义的十分多,为有能力的开发者提供了足够的扩展性,现有的实现类一般都以
Default+接口名
初始化 setting.gradle 根据 include 的目录层级创建树状关系的
DefaultProjectDescriptor
每一个 build.gradle 初始化一个 DefaultProject
构建任务树生成 TaskGraph
执行构建任务
prepareProjects 解析 build.gradle 阶段
DefaultScriptPluginFactory$ScriptPluginImpl.apply 继续往下看
DefaultPluginRequestApplicator.applyPlugins ->
defineScriptHandlerClassScope
DefaultScriptHandler.getScriptClassPath
DefaultScriptClassPathResolver.resolveClassPath
CompositeBuildClassPathInitializer.execute
DefaultConfiguration$ConfigurationArtifactCollection.getArtifacts ->
ensureResolved
DefaultLenientConfiguration.visitArtifacts
ParallelResolveArtifactSet$VisitingSet.visit
ParallelResolveArtifactSet$VisitingSet$StartVisitAction.execute
CompositeResolvedArtifactSet.startVisit
ArtifactBackedResolvedVariant$SingleArtifactSet.startVisit
如果需要下载到本地
DefaultBuildOperationQueue.add 内部有线程池 下面的调用都为异步执行
DownloadArtifactFile.run
DefaultResolvedArtifact.getFile
DefaultArtifactSet$LazyArtifactSource.create
RepositoryChainArtifactResolver.resolveArtifact ->
getLocalAccess() 加载本地缓存
getRemoteAccess() 如果本地缓存没读到 下载依赖并缓存
JavaCompile 只编译改过的类
执行自定义插件
………… 后面还有很多 Android build tools 流程,未完待续
自定义插件
AnnoationProcessor 是 javac 的一种代码注入技术,在 JavaCompile 期间处理。比如ButterKnife
GradlePlugin 安卓编译流程的一部分 后面也会讲一下具体实现流程
提高编译速度
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。