赞
踩
先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新HarmonyOS鸿蒙全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注鸿蒙)
KotlinAndroidPlugin
是插件真正的入口,在这里完成compileKotlin Task
相关的配置工作
internal open class KotlinAndroidPlugin(
private val registry: ToolingModelBuilderRegistry
) : Plugin {
override fun apply(project: Project) {
checkGradleCompatibility()
project.dynamicallyApplyWhenAndroidPluginIsApplied()
}
private fun preprocessVariant(
variantData: BaseVariant,
compilation: KotlinJvmAndroidCompilation,
project: Project,
rootKotlinOptions: KotlinJvmOptionsImpl,
tasksProvider: KotlinTasksProvider
) {
val configAction = KotlinCompileConfig(compilation)
configAction.configureTask { task ->
task.useModuleDetection.value(true).disallowChanges()
// 将kotlin 编译结果存储在tmp/kotlin-classes/
v
a
r
i
a
n
t
D
a
t
a
N
a
m
e
目录下,会作为
j
a
v
a
c
o
m
p
i
l
e
r
的
c
l
a
s
s
−
p
a
t
h
输入
t
a
s
k
.
d
e
s
t
i
n
a
t
i
o
n
D
i
r
e
c
t
o
r
y
.
s
e
t
(
p
r
o
j
e
c
t
.
l
a
y
o
u
t
.
b
u
i
l
d
D
i
r
e
c
t
o
r
y
.
d
i
r
(
"
t
m
p
/
k
o
t
l
i
n
−
c
l
a
s
s
e
s
/
variantDataName目录下,会作为java compiler的class-path输入 task.destinationDirectory.set(project.layout.buildDirectory.dir("tmp/kotlin-classes/
variantDataName目录下,会作为javacompiler的class−path输入task.destinationDirectory.set(project.layout.buildDirectory.dir("tmp/kotlin−classes/variantDataName"))
}
tasksProvider.registerKotlinJVMTask(project, compilation.compileKotlinTaskName, compilation.kotlinOptions, configAction)
}
}
省略了一些代码,主要做了几件事:
kotlin-android
插件KotlinCompileConfig
来配置KotlinCompile Task
,设置destinationDirectory
作为Kotlin编译结果存储目录,后续会作为java compiler
的classpath
输入要实现增量编译,最重要的一点就是配置输入输出,当输入输出没有发生变化时,Task就可以被跳过,而KotlinCompile
输入输出的配置,主要是在KotlinCompileConfig
中完成的
configureTaskProvider { taskProvider ->
// 是否开启classpathSnapthot
val useClasspathSnapshot = propertiesProvider.useClasspathSnapshot
val classpathConfiguration = if (useClasspathSnapshot) {
// 注册 Transform
registerTransformsOnce(project)
project.configurations.detachedConfiguration(
project.dependencies.create(objectFactory.fileCollection().from(project.provider { taskProvider.get().libraries }))
)
} else null
taskProvider.configure { task ->
// 配置输入属性
task.classpathSnapshotProperties.useClasspathSnapshot.value(useClasspathSnapshot).disallowChanges()
if (useClasspathSnapshot) {
// 通过TransformAction读取输入
val classpathEntrySnapshotFiles = classpathConfiguration!!.incoming.artifactView {
it.attributes.attribute(ARTIFACT_TYPE_ATTRIBUTE, CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE)
}.files
task.classpathSnapshotProperties.classpathSnapshot.from(classpathEntrySnapshotFiles).disallowChanges()
task.classpathSnapshotProperties.classpathSnapshotDir.value(getClasspathSnapshotDir(task)).disallowChanges()
} else {
task.classpathSnapshotProperties.classpath.from(task.project.provider { task.libraries }).disallowChanges()
}
}
}
可以看出,主要做了这么几件事
classpathSnapthot
,这也是支持跨模块增量编译的开关,如果开启了就注册Transform
TransformAction
获取输入,并配置给Task相应的属性下面我们着重来看下TransformAction在这里做了什么工作?
private fun registerTransformsOnce(project: Project) {
val buildMetricsReporterService = BuildMetricsReporterService.registerIfAbsent(project)
project.dependencies.registerTransform(ClasspathEntrySnapshotTransform::class.java) {
it.from.attribute(ARTIFACT_TYPE_ATTRIBUTE, JAR_ARTIFACT_TYPE)
it.to.attribute(ARTIFACT_TYPE_ATTRIBUTE, CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE)
}
project.dependencies.registerTransform(ClasspathEntrySnapshotTransform::class.java) {
it.from.attribute(ARTIFACT_TYPE_ATTRIBUTE, DIRECTORY_ARTIFACT_TYPE)
it.to.attribute(ARTIFACT_TYPE_ATTRIBUTE, CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE)
}
}
了解了前置知识中的TransformAction
,可以看出这就是注册了只变换ArtifactType
的变换,主要涉及JAR_ARTIFACT_TYPE
和DIRECTORY_ARTIFACT_TYPE
转换为CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE
也就是说依赖的jar和类目录都会转换为CLASSPATH_ENTRY_SNAPSHOT_ARTIFACT_TYPE
类型,也就可以获取我们依赖的所有classpath
的abi
了
接下来我们看下ClasspathEntrySnapshotTransform
的实现
abstract class ClasspathEntrySnapshotTransform : TransformAction<ClasspathEntrySnapshotTransform.Parameters> {
@get:Classpath
@get:InputArtifact
abstract val inputArtifact: Provider
override fun transform(outputs: TransformOutputs) {
val classpathEntryInputDirOrJar = inputArtifact.get().asFile
val snapshotOutputFile = outputs.file(classpathEntryInputDirOrJar.name.replace(‘.’, ‘_’) + “-snapshot.bin”)
val granularity = getClassSnapshotGranularity(classpathEntryInputDirOrJar, parameters.gradleUserHomeDir.get().asFile)
val snapshot = ClasspathEntrySnapshotter.snapshot(classpathEntryInputDirOrJar, granularity, metrics)
ClasspathEntrySnapshotExternalizer.saveToFile(snapshotOutputFile, snapshot)
}
/**
关于自定义TransformAction
,其实跟Task
一样,也主要看3个部分,输入,输出,执行方法体
ClasspathEntrySnapshotTransform
的输入就是模块依赖的jar或者文件目录-snapshot.bin
结尾的文件ClasspathEntrySnapshotter
计算出claspath
的快照并保存,如果是aar依赖,计算的粒度为class,如果是项目内的类,计算的粒度是class_member_level
ClasspathEntrySnapshotter
内部是如何计算classpath
快照的我们这就不看了,我们简单看下下面这样一个类计算的快照是怎样的
class MyTest {
fun startTest(text: String) {
println(text)
test1(1)
}
private fun test1(index: Int) {
println(“here test126$index”)
}
}
MyTest
类计算出来的快照如图所示,主要classId,classAbiHash,classHeaderStrings
等内容
可以看出private
函数的声明也是abi
的一部分,当public
或者private
的函数声明发生变化时,classAbiHash
都会发生变化,而只修改函数体时,snapshot
不会发生任何变化。
在配置完成之后,接下来我们就来看下KotlinCompile
是怎么执行编译的
abstract class KotlinCompile @Inject constructor(
override val kotlinOptions: KotlinJvmOptions,
workerExecutor: WorkerExecutor,
private val objectFactory: ObjectFactory
) : AbstractKotlinCompile(objectFactory {
// classpathSnapshot入参
@get:Nested
abstract val classpathSnapshotProperties: ClasspathSnapshotProperties
abstract class ClasspathSnapshotProperties {
@get:Classpath
@get:Incremental
@get:Optional // Set if useClasspathSnapshot == true
abstract val classpathSnapshot: ConfigurableFileCollection
}
// 增量编译参数
override val incrementalProps: List
get() = listOf(
sources,
javaSources,
classpathSnapshotProperties.classpathSnapshot
)
override fun callCompilerAsync(inputChanges: InputChanges) {
// 获取增量编译环境变量
val icEnv = if (isIncrementalCompilationEnabled()) {
IncrementalCompilationEnvironment(
changedFiles = getChangedFiles(inputChanges, incrementalProps),
classpathChanges = getClasspathChanges(inputChanges),
)
} else null
val environment = GradleCompilerEnvironment(incrementalCompilationEnvironment = icEnv)
compilerRunner.runJvmCompilerAsync(
(kotlinSources + scriptSources).toList(),
commonSourceSet.toList(),
javaSources.files,
environment,
)
}
// 查找改动了的input
protected fun getChangedFiles(
inputChanges: InputChanges,
incrementalProps: List
) = if (!inputChanges.isIncremental) {
ChangedFiles.Unknown()
} else {
incrementalProps
.fold(mutableListOf() to mutableListOf()) { (modified, removed), prop ->
inputChanges.getFileChanges(prop).forEach {
when (it.changeType) {
ChangeType.ADDED, ChangeType.MODIFIED -> modified.add(it.file)
ChangeType.REMOVED -> removed.add(it.file)
else -> Unit
}
}
modified to removed
}
.run {
ChangedFiles.Known(first, second)
}
}
// 查找改变了的classpath
private fun getClasspathChanges(inputChanges: InputChanges): ClasspathChanges = when {
!classpathSnapshotProperties.useClasspathSnapshot.get() -> ClasspathSnapshotDisabled
else -> {
when {
!inputChanges.isIncremental -> NotAvailableForNonIncrementalRun(classpathSnapshotFiles)
inputChanges.getFileChanges(classpathSnapshotProperties.classpathSnapshot).none() -> NoChanges(classpathSnapshotFiles)
!classpathSnapshotFiles.shrunkPreviousClasspathSnapshotFile.exists() -> {
NotAvailableDueToMissingClasspathSnapshot(classpathSnapshotFiles)
}
else -> ToBeComputedByIncrementalCompiler(classpathSnapshotFiles)
}
}
}
}
对于KotlinCompile
,我们也可以从入参,出参,TaskAction
的角度来分析
classpathSnapshotProperties
是个包装类型的输入,内部包括@Classpath
类型的输入,使用@Classpath输入时,如果输入文件名发生变化而内容没有发生变化时,不会触发Task重新运行,这对classpath来说非常重要incrementalProps
是组件后的增量编译输入参数,包括kotlin
输入,java
输入,classpath
输入等CompileKotlin
的TaskAction
,它最后会执行到callCompilerAsync
方法,在其中通过getChangedFiles
与getClasspathChanges
获取改变了的输入与classpath
getClasspathChanges
方法通过inputChanges
获取一个已经改变与删除的文件的PairgetClasspathChanges
则根据增量编译是否开启,是否有文件发生更改,历史snapshotFile是否存在,返回不同的ClassPathChanges
密封类在增量编译参数拼装完成后,接下来就是跟着逻辑走,最后会走到GradleKotlinCompilerWork
的 compileWithDaemmonOrFailbackImpl
private fun compileWithDaemonOrFallbackImpl(messageCollector: MessageCollector): ExitCode {
val executionStrategy = kotlinCompilerExecutionStrategy()
if (executionStrategy == DAEMON_EXECUTION_STRATEGY) {
val daemonExitCode = compileWithDaemon(messageCollector)
if (daemonExitCode != null) {
return daemonExitCode
}
}
val isGradleDaemonUsed = System.getProperty(“org.gradle.daemon”)?.let(String::toBoolean)
return if (executionStrategy == IN_PROCESS_EXECUTION_STRATEGY || isGradleDaemonUsed == false) {
compileInProcess(messageCollector)
} else {
compileOutOfProcess()
}
}
可以看出,kotlin
编译有三种策略,分别是
Gradle daemon
进程间共享compileWithDaemon
会调用到 Kotlin Compile
里执行真正的编译逻辑:
val exitCode = try {
val res = if (isIncremental) {
incrementalCompilationWithDaemon(daemon, sessionId, targetPlatform, bufferingMessageCollector)
} else {
nonIncrementalCompilationWithDaemon(daemon, sessionId, targetPlatform, bufferingMessageCollector)
}
} catch (e: Throwable) {
null
}
到这里会执行 org.jetbrains.kotlin.daemon.CompileServiceImpl
的 compile
方法,这样就终于调到了Kotlin编译器内部
经过这么多步骤,终于走到Kotlin编译器内部了,下面我们来看下Kotlin编译器的增量编译逻辑
protected inline fun <ServicesFacadeT, JpsServicesFacadeT, CompilationResultsT> compileImpl(){
//…
CompilerMode.INCREMENTAL_COMPILER -> {
when (targetPlatform) {
CompileService.TargetPlatform.JVM -> withIC(k2PlatformArgs) {
doCompile(sessionId, daemonReporter, tracer = null) { _, _ ->
execIncrementalCompiler(
k2PlatformArgs as K2JVMCompilerArguments,
gradleIncrementalArgs,
//…
)
}
}
}
如上代码,会判断输入的编译参数,如果是增量编译并且是JVM平台的话,就会执行execIncrementalCompiler
方法,最后会调用到sourcesToCompile
方法
private fun sourcesToCompile(
caches: CacheManager,
changedFiles: ChangedFiles,
args: Args,
messageCollector: MessageCollector,
dependenciesAbiSnapshots: Map<String, AbiSnapshot>
): CompilationMode =
when (changedFiles) {
is ChangedFiles.Known -> calculateSourcesToCompile(caches, changedFiles, args, messageCollector, dependenciesAbiSnapshots)
is ChangedFiles.Unknown -> CompilationMode.Rebuild(BuildAttribute.UNKNOWN_CHANGES_IN_GRADLE_INPUTS)
is ChangedFiles.Dependencies -> error(“Unexpected ChangedFiles type (ChangedFiles.Dependencies)”)
}
private fun calculateSourcesToCompileImpl(
caches: IncrementalJvmCachesManager,
changedFiles: ChangedFiles.Known,
args: K2JVMCompilerArguments,
abiSnapshots: Map<String, AbiSnapshot> = HashMap(),
withAbiSnapshot: Boolean
): CompilationMode {
val dirtyFiles = DirtyFilesContainer(caches, reporter, kotlinSourceFilesExtensions)
// 初始化dirtyFiles
initDirtyFiles(dirtyFiles, changedFiles)
// 计算变化的classpath
val classpathChanges = when (classpathChanges) {
is NoChanges -> ChangesEither.Known(emptySet(), emptySet())
// classpathSnapshot可用时
is ToBeComputedByIncrementalCompiler -> reporter.measure(BuildTime.COMPUTE_CLASSPATH_CHANGES) {
computeClasspathChanges(
classpathChanges.classpathSnapshotFiles,
caches.lookupCache,
storeCurrentClasspathSnapshotForReuse,
ClasspathSnapshotBuildReporter(reporter)
).toChangesEither()
}
is NotAvailableDueToMissingClasspathSnapshot -> ChangesEither.Unknown(BuildAttribute.CLASSPATH_SNAPSHOT_NOT_FOUND)
is NotAvailableForNonIncrementalRun -> ChangesEither.Unknown(BuildAttribute.UNKNOWN_CHANGES_IN_GRADLE_INPUTS)
// classpathSnapshot不可用时
is ClasspathSnapshotDisabled -> reporter.measure(BuildTime.IC_ANALYZE_CHANGES_IN_DEPENDENCIES) {
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile)
getClasspathChanges(
args.classpathAsList, changedFiles, lastBuildInfo, modulesApiHistory, reporter, abiSnapshots, withAbiSnapshot,
caches.platformCache, scopes
)
}
is NotAvailableForJSCompiler -> error(“Unexpected type for this code path: ${classpathChanges.javaClass.name}.”)
}
// 将结果添加到dirtyFiles
val unused = when (classpathChanges) {
is ChangesEither.Unknown -> {
return CompilationMode.Rebuild(classpathChanges.reason)
}
is ChangesEither.Known -> {
dirtyFiles.addByDirtySymbols(classpathChanges.lookupSymbols)
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
spathChanges) {
is ChangesEither.Unknown -> {
return CompilationMode.Rebuild(classpathChanges.reason)
}
is ChangesEither.Known -> {
dirtyFiles.addByDirtySymbols(classpathChanges.lookupSymbols)
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注鸿蒙)
[外链图片转存中…(img-mguhe4ZB-1713305704919)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。