Kotlin-KCP的应用-第二篇

Andorid&Kotlin编译速度原理剖析(上) 可以看到在每次的运行构建编译后会对每个gradleTask进行耗时的打印,因此可以针对耗时任务严重的Task做针对性的优化处理还可以针对耗时超过一定时间的任务做监控,如果触发了临界值就会做报警处理这样就保证了以后的Task一直处于较低的耗时,因为内容比较多这个监控方案第二章的时候会详细讲解。其他生命周期的方法以省略,具体代码如下:@Override@Override@Override复制代码四、编译优化常规方案俗话说的好“预先善其事,必先利其器”、“磨刀不误砍柴工” 、“先谋而后动”等。 阅读详情

Kotlin-KCP的应用-第二篇

前言

接Kotlin-KCP的应用-第一篇,本文是第二篇,以下是本文的目标:

  1. 记录如何简单搭建 KCP 开发环境
  2. 使用 KCP 解决第一篇中的问题

何为KCP?为何不使用KSP?

KSP

KSP 即 Kotlin Symbol Processing(Kotlin符号处理器),KSP 目前只能生成代码,不能修改字节码,第一篇中的问题需要修改字节码,因此 KSP 不能满足需求

KCP

KCP 即 Kotlin Compiler Plugin(Kotlin编译器插件),在 kotlinc 过程中提供 hook 时机,在此期间可以生成代码、修改字节码等

标准的 KCP 架构如下1:

KCP架构

Plugin

  • Gradle 插件,与 Kotlin 无关,在 build.gradle 脚本中提供一个入口
  • 通过 Gradle 扩展设置配置信息

Subplugin

  • 介于 Gradle 和 Kotlin 直接的 APIs 接口
  • 读取 Gradle 扩展配置信息并写入 SubpluginOptions
  • 定义编译器插件的唯一ID
  • 定义 Kotlin 插件的 Maven 坐标信息,便于编译器下载它

CommandLinProcessor

  • 设置 Kotlin 插件唯一 ID
  • 读取 kotlinc -Xplugin 参数
  • 读取 SubpluginOptions 配置信息,并写入 CompilerConfigurationKeys

ComponentRegistrar

  • 读取 CompilerConfigurationKeys
  • 注册 Extension 到各编译流程

Extension

  • 生成代码
  • 修改字节码
  • 多种类型的扩展,比如
    • ExpressionCodegenExtension
    • ClassBuilderInterceptorExtension
    • StorageComponentContainerContributor
    • IrGenerationExtension

实现KCP

目标

根据 KCP 的架构,下面一一进行实现

image-20220512095927050

上图是本仓库架构,旨在通过 KCP 在 Java 字节码中 @Hide 注解目标上设置 ACC_SYNTHETIC 标识,使其在 Java 中不能正常调用,达到隐藏 API 的效果

build.gradle - project level

在项目级别的 build.gradle 脚本中配置插件依赖

buildscript {
    // 配置 Kotlin 插件唯一ID
    ext.kotlin_plugin_id = "com.guodong.android.mask.kcp"
    
    // 配置 Kotlin 插件版本
    ext.plugin_version = 'x.x.x'
}

plugins {
    // 配置 Gradle 发布插件,可以不再写 META-INF
    id("com.gradle.plugin-publish") version "0.16.0" apply false
    
    // 配置生成 BuildConfig 插件
    id("com.github.gmazzo.buildconfig") version "3.0.3" apply false
    id 'org.jetbrains.kotlin.jvm' version '1.6.10' apply false
}

plugin-gradle

接下来编写 Gradle 插件,此插件对应 KCP 架构中的 Plugin 和 Subplugin

首先配置下 build.gradle.kts 脚本

build.gradle.kts - module level
plugins {
    id("java-gradle-plugin")
    kotlin("jvm")
    id("com.github.gmazzo.buildconfig")
}

dependencies {
    implementation(kotlin("gradle-plugin-api"))
}

buildConfig {
    // 配置 BuildConfig 的包名
    packageName("com.guodong.android.mask.kcp.gradle")
    
    // 设置 Kotlin 插件唯一 ID
    buildConfigField("String", "KOTLIN_PLUGIN_ID", "\"${rootProject.extra["kotlin_plugin_id"]}\"")
    
    // 设置 Kotlin 插件 GroupId
    buildConfigField("String", "KOTLIN_PLUGIN_GROUP", "\"com.guodong.android\"")
    
    // 设置 Kotlin 插件 ArtifactId
    buildConfigField("String", "KOTLIN_PLUGIN_NAME", "\"mask-kcp-kotlin-plugin\"")
    
    // 设置 Kotlin 插件 Version
    buildConfigField("String", "KOTLIN_PLUGIN_VERSION", "\"${rootProject.extra["PLUGIN_VERSION"]}\"")
}

gradlePlugin {
    plugins {
        create("Mask") {
            id = rootProject.extra["kotlin_plugin_id"] as String // `apply plugin: "com.guodong.android.mask.kcp"`
            displayName = "Mask Kcp"
            description = "Mask Kcp"
            implementationClass = "com.guodong.android.mask.kcp.gradle.MaskGradlePlugin" // 插件入口类
        }
    }
}

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = "1.8"
}
MaskGradlePlugin

创建 MaskGradlePlugin 实现 KotlinCompilerPluginSupportPlugin 接口

class MaskGradlePlugin : KotlinCompilerPluginSupportPlugin {

    override fun apply(target: Project) = with(target) {
        logger.error("Welcome to guodongAndroid mask kcp gradle plugin.")
        
        // 此处可以配置 Gradle 插件扩展
        // 本插件没有配置项, 无需配置扩展
    }

    // 是否适用, 默认True
    override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = true

    // 获取 Kotlin 插件唯一ID
    override fun getCompilerPluginId(): String = BuildConfig.KOTLIN_PLUGIN_ID

    // 获取 Kotlin 插件 Maven 坐标信息
    override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact(
        groupId = BuildConfig.KOTLIN_PLUGIN_GROUP,
        artifactId = BuildConfig.KOTLIN_PLUGIN_NAME,
        version = BuildConfig.KOTLIN_PLUGIN_VERSION,
    )

    // 读取 Gradle 插件扩展信息并写入 SubpluginOption
    // 本插件没有扩展信息,所以返回空集合
    override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider<List<SubpluginOption>> {
        val project = kotlinCompilation.target.project
        return project.provider { emptyList() }
    }
}

至此 Gradle 插件编写完成,是不是很简单😄

plugin-kotlin

接下来编写 Kotlin 编译器插件,此插件对应 KCP 架构中的 CommandLineProcessor 、 ComponentRegistrar 和 Extension

首先配置下 build.gradle.kts 脚本

build.gradle.kts - module level
plugins {
    kotlin("jvm")
    kotlin("kapt")
    id("com.github.gmazzo.buildconfig")
}

dependencies {
    // 依赖 Kotlin 编译器库
    compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable")

    // 依赖 Google auto service
    kapt("com.google.auto.service:auto-service:1.0")
    compileOnly("com.google.auto.service:auto-service-annotations:1.0")
}

buildConfig {
    // 配置 BuildConfig 的包名
    packageName("com.guodong.android.mask.kcp.kotlin")
    
    // 设置 Kotlin 插件唯一 ID
    buildConfigField("String", "KOTLIN_PLUGIN_ID", "\"${rootProject.extra["kotlin_plugin_id"]}\"")
}

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = "1.8"
}

根据 KCP 架构,下面实现 CommandLineProcessor

MaskCommandLineProcessor

创建 MaskCommandLineProcessor 实现 CommandLineProcessor 接口

@AutoService(CommandLineProcessor::class)
class MaskCommandLineProcessor : CommandLineProcessor {

    // 配置 Kotlin 插件唯一 ID
    override val pluginId: String = BuildConfig.KOTLIN_PLUGIN_ID

    // 读取 `SubpluginOptions` 参数,并写入 `CliOption`
    // 本插件没有配置信息,返回空集合
    override val pluginOptions: Collection<AbstractCliOption> = emptyList()
    
    // 处理 `CliOption` 写入 `CompilerConfiguration`
    // 本插件没有配置信息,此处没有实现
    override fun processOption(
        option: AbstractCliOption,
        value: String,
        configuration: CompilerConfiguration
    ) {
        super.processOption(option, value, configuration)
    }
}

CommandLineProcessor 编写完成

接下来实现 ComponentRegistrar

MaskComponentRegistrar

创建 MaskComponentRegistrar 实现 ComponentRegistrar 接口

@AutoService(ComponentRegistrar::class)
class MaskComponentRegistrar : ComponentRegistrar {

    override fun registerProjectComponents(
        project: MockProject,
        configuration: CompilerConfiguration
    ) {
        // 获取日志收集器
        val messageCollector =
            configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)

        // 输出日志,查看是否执行
        // CompilerMessageSeverity.INFO - 没有看到日志输出
        // CompilerMessageSeverity.ERROR - 编译过程停止执行
        messageCollector.report(
            CompilerMessageSeverity.WARNING,
            "Welcome to guodongAndroid mask kcp kotlin plugin"
        )

        // 此处在 `ClassBuilderInterceptorExtension` 中注册扩展
        ClassBuilderInterceptorExtension.registerExtension(
            project,
            MaskClassGenerationInterceptor(messageCollector)
        )
    }
}

最后实现 Extension

MaskClassGenerationInterceptor

创建 MaskClassGenerationInterceptor 实现 ClassBuilderInterceptorExtension 接口

class MaskClassGenerationInterceptor(
    private val messageCollector: MessageCollector,
) : ClassBuilderInterceptorExtension {

    // 拦截 ClassBuilderFactory
    override fun interceptClassBuilderFactory(
        interceptedFactory: ClassBuilderFactory,
        bindingContext: BindingContext,
        diagnostics: DiagnosticSink
        // 自定义 ClassBuilderFactory 委托给 源ClassBuilderFactory
    ): ClassBuilderFactory = object : ClassBuilderFactory by interceptedFactory {
        
        // 复写 newClassBuilder,自定义 ClassBuilder
        override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder {
            // 传入源ClassBuilder
            return MaskClassBuilder(messageCollector, interceptedFactory.newClassBuilder(origin))
        }
    }
}
MaskClassBuilder

创建 MaskClassBuilder 继承 DelegatingClassBuilder,实现 getDelegate 方法,复写 newField 和 newMethod 方法

getDelegate
class MaskClassBuilder(
    private val messageCollector: MessageCollector,
    private val delegate: ClassBuilder
) : DelegatingClassBuilder() {

    // @Hide注解的完全限定名(fully qualified name)
    private val annotations: List<FqName> = listOf(
        FqName("com.guodong.android.mask.api.kt.Hide"),
        FqName("com.guodong.android.mask.api.Hide")
    )

    // 返回代理
    override fun getDelegate(): ClassBuilder = delegate
}
newField

处理 @Hide 注解目标为字段

class MaskClassBuilder(
    private val messageCollector: MessageCollector,
    private val delegate: ClassBuilder
) : DelegatingClassBuilder() {

    // @Hide注解的完全限定名(fully qualified name)
    private val annotations: List<FqName> = listOf(
        FqName("com.guodong.android.mask.api.kt.Hide"),
        FqName("com.guodong.android.mask.api.Hide")
    )

    override fun newField(
        origin: JvmDeclarationOrigin,
        access: Int,
        name: String,
        desc: String,
        signature: String?,
        value: Any?
    ): FieldVisitor {
        // 判断描述符是否是字段
        val field = origin.descriptor as? FieldDescriptor
        	?: return super.newField(origin, access, name, desc, signature, value)

        // 判断字段上是否有`@Hide`注解
        if (annotations.none { field.annotations.hasAnnotation(it) }) {
            return super.newField(origin, access, name, desc, signature, value)
        }

        // 输出字段源访问标志
        messageCollector.report(
            CompilerMessageSeverity.WARNING,
            "Mask Class = ${delegate.thisName}, fieldName = $name, originalAccess = $access"
        )

        // 增加`ACC_SYNTHETIC`标识
        val maskAccess = access + Opcodes.ACC_SYNTHETIC

        // 输出字段Mask后访问标志
        messageCollector.report(
            CompilerMessageSeverity.WARNING,
            "Mask Class = ${delegate.thisName}, fieldName = $name, maskAccess = $maskAccess"
        )

        // 传入Mask后访问标志
        return super.newField(origin, maskAccess, name, desc, signature, value)
    }

}
newMethod

处理 @Hide 注解目标为方法/函数,处理逻辑与字段类似

override fun newMethod(
    origin: JvmDeclarationOrigin,
    access: Int,
    name: String,
    desc: String,
    signature: String?,
    exceptions: Array<out String>?
): MethodVisitor {
    // 判断是否是方法/函数描述符
    val function = origin.descriptor as? FunctionDescriptor
    	?: return super.newMethod(origin, access, name, desc, signature, exceptions)

    // 判断方法/函数上是否有`@Hide`注解
    if (annotations.none { function.annotations.hasAnnotation(it) }) {
        return super.newMethod(origin, access, name, desc, signature, exceptions)
    }

    // 输出方法/函数源访问标志
    messageCollector.report(
        CompilerMessageSeverity.WARNING,
        "Mask Class = ${delegate.thisName}, methodName = $name, originalAccess = $access"
    )

    // 增加`ACC_SYNTHETIC`标识
    val maskAccess = access + Opcodes.ACC_SYNTHETIC

    // 输出方法/函数Mask后访问标志
    messageCollector.report(
        CompilerMessageSeverity.WARNING,
        "Mask Class = ${delegate.thisName}, methodName = $name, maskAccess = $maskAccess"
    )

    // 传入Mask后访问标志
    return super.newMethod(origin, maskAccess, name, desc, signature, exceptions)
}

至此 Kotlin 编译器插件完成:happy:

应用KCP

build.gradle - project level

buildscript {
    ext.plugin_version = 'x.x.x'
    dependencies {
        classpath "com.guodong.android:mask-kcp-gradle-plugin:${plugin_version}"
    }
}

lib-kotlin/build.gradle - module level

# lib-kotlin
plugins {
    id 'com.android.library'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'maven-publish'
    // id 'com.guodong.android.mask' // use kcp
    id 'com.guodong.android.mask.kcp'
}

lib-kotlin

interface InterfaceTest {

    // 使用api-kt中的注解
    @Hide
    fun testInterface()
}
class KotlinTest(a: Int) : InterfaceTest {

    // 使用api-kt中的注解
    @Hide
    constructor() : this(-2)

    companion object {

        @JvmStatic
        fun newKotlinTest() = KotlinTest()
    }

    private val binding: LayoutKotlinTestBinding? = null

    // 使用api-kt中的注解
    var a = a
        @Hide get
        @Hide set

    fun getA1(): Int {
        return a
    }

    fun test() {
        a = 1000
    }

    override fun testInterface() {
        println("Interface function test")
    }
}
app
# MainActivity.java

private void testKotlinLib() {
    // 创建对象时不能访问无参构造方法,可以访问有参构造方法或访问静态工厂方法
    KotlinTest test = KotlinTest.newKotlinTest();
    // 调用时不能访问`test.getA()`方法,仅能访问`getA1()方法
    Log.e(TAG, "testKotlinLib: before --> " + test.getA1());
    test.test();
    Log.e(TAG, "testKotlinLib: after --> " + test.getA1());
    
    test.testInterface();
    
    InterfaceTest interfaceTest = test;
    // Error - cannot resolve method 'testInterface' in 'InterfaceTest'
    interfaceTest.testInterface();
}

happy:happy:

参考文献


  1. [Writing Your First Kotlin Compiler Plugin (jetbrains.com)](https://resources.jetbrains.com/storage/products/kotlinconf2018/slides/5_Writing Your First Kotlin Compiler Plugin.pdf) ↩︎

BuildKonfig:Kotlin多平台项目的BuildConfig 立即下载

相关推荐

手把手教你为智能手表DIY一个离线导航:LVGL + SD卡地图实战

本文详细介绍了如何为智能手表DIY一个离线导航系统,结合LVGL图形库和SD卡地图数据。从硬件选型、地图数据处理到LVGL地图引擎实现,提供了完整的实战指南,特别适合户外运动爱好者和嵌入式开发者。文章还分享了功耗优化、性能提升等实用技巧,帮助读者打造高效的离线导航解决方案。

weixin_42570484的博客 206

安卓Kotlin: 获取配置,BuildConfig类找不到

今天要使用环境变量,访问不到BuildConfig类,import导包也找不到类,具体设置如下:但是buildConfig文件却能看到,已经生成。

简单Cere 939

【一文读懂】卫星轨道的轨道参数(六根数)和位置速度矢量转换及其在终端距离、相对速度和多普勒频移计算中的应用

本文首先介绍了卫星轨道的六根数参数:半长轴 a、偏心率 e、轨道倾角 i、升交点赤经 Ω、近地点幅角 ω 以及真近点角 ν,并探讨了如何利用这些参数计算卫星与地面终端之间的距离、相对速度及由此产生的多普勒频移。利用开普勒运动公式计算当前瞬间的轨道半径;根据真近点角 ν 在围心坐标系中计算位置矢量和速度矢量;利用旋转矩阵(依次旋转 ω、i、Ω)将围心坐标系的状态矢量转换到地心惯性坐标系;最终结合地面终端信息,计算卫星与终端的欧氏距离、沿视线的相对速度及多普勒频移。

u011376987的博客 9495

BuildKonfig 使用教程

BuildKonfig 是一个用于 Kotlin Multiplatform Project (KMPP) 的 Gradle 插件,旨在生成 BuildConfig 对象。这个插件允许你在 Kotlin 多平台项目中存储和访问构建变量,如 API 密钥等敏感信息。通过 BuildKonfig,你可以将这些信息安全地存储在 `local.properties` 文件中,并在代码中轻松访问它们。 #...

gitblog_00340的博客 619

图形学基础知识:着色(三)Rendering Pipeline与Shader

管线(Pipeline) 什么是管线? 管线我们可以理解为流水线,流水线大家都懂,就是工厂里面生产东西的一整套流程。例如饮料厂要生产饮料,需要洗瓶子,倒饮料,加盖,包装等等,这一整套过程就是流水线。那么我们要把一个三维场景渲染成一幅二维的图像,同样需要一个先干嘛再干嘛然后干嘛最后干嘛的过程,这个过程或流水线,我们就称之为管线。 实时渲染管线(Real-time Rendering Pipeline) 通过前面几篇的学习,我们知道了要把一个三维场景渲染成一幅二维的图像大致分为以下几个步骤: M.

王王王渣渣的博客 2532

Kafka 系列 —(17)Kafka 流式处理:Kafka Streams架构、使用场景、框架选择

本文介绍了KafkaStreams的架构特点、应用场景及与其他流处理框架的比较。KafkaStreams作为嵌入式分布式流处理库,其核心架构包含流处理器、任务、拓扑和本地状态存储,具备自动容错和伸缩能力。适用于实时监控、ETL、聚合、Join、物化视图等低延迟场景。与Flink、Spark相比,KafkaStreams适合简单架构和微服务,而Flink适合大规模处理,Spark更适合批处理。选择框架需根据具体场景需求,如微服务推荐KafkaStreams,大规模处理推荐Flink。

moton2017的博客 1631

Kotlin-KCP的应用-第一篇

前言 KCP的应用计划分两篇,本文是第一篇 本文主要记录从发现问题到使用KCP解决问题的折腾过程,下一篇记录KCP的应用 背景 Kotlin 号称百分百兼容 Java ,所以在 Kotlin 中一些修饰符,比如 internal ,在编译后放在纯 Java 的项目中使用(没有Kotlin环境),Java 仍然可以访问被 internal 修饰的类、方法、字段等 在使用 Kotlin 开发过程中需要对外提供 SDK 包,在 SDK 中有一些 API 不想被外部调用,并且已经添加了 internal 修饰,但是

孫小逗 1377

脑电分析:为什么大脑信号会“变同步”?——事件相关同步化与去同步化 (ERS/ERD) 全景解析

脑电分析中的事件相关同步化(ERS)和去同步化(ERD)是研究大脑动态节律的重要方法,反映特定任务下脑电频段能量的变化。ERD表示神经元活动分散(功率下降),提示脑区活跃处理信息;ERS则反映神经元同步(功率增强),常与静息状态相关。本文详解了ERS/ERD的生理机制、计算流程(包括基线校正和功率谱分析),并提供了Python实现示例,展示如何通过Welch法和STFT进行时频分析,生成ERD/ERS热力图。该技术广泛应用于运动想象脑机接口、癫痫检测等领域,为理解大脑功能状态提供了量化工具。

qq_32516809的博客 1395

Kotlin-KCP的应用-修改SDK版本号

背景 在 SDK 开发中,一般会暴露获取 SDK 版本号的接口,获取的版本号一般为 String 类型,比如: // sdk接口 interface Sdk { fun getVersion(): String } // sdk调用方 sdk.getVersion() 上述方式可以通过在 gradle.properties 中配置版本号,然后在 build.gradle 中读取版本号生成至 BuildConfig.java 中,例如: // gradle.properties VERSION=1

孫小逗 1192

元宇宙通证- 九、IT/ICT发展史全景长图

九、IT/ICT发展史全景长图 (一)综述 IT是Information Technology的缩写,意为”信息技术“,包含现代计算机、网络、通信等信息领域技术。自1844年人类历史上发出来第一份电报,实现了信息的长图传输,信息技术发展的历史大幕随之拉开。信息技术的普遍应用,是进入信息社会的标志。 IT技术最早发源于半导体,然后发展处计算机、通信网和互联网,进而演进到现在的AI、XR、计算机、大数据、物联网、工业互联网、区块链、量子计算等。 有人说元宇宙是下一代互联网,有人说元宇宙是互联网的终极形态。 我们

沧海之巅的专栏 1万+

Altium Designer应用技巧5: Altium Live账号的获得

为了更好的应用Altium Designer,我们需要得到一个Altium Live账号,但从其英文或者中文官方网站取得,都需要7个工作日的时间,而且对于个人用户来说,并不是100%都能够获取到。本文介绍一种,从俄罗斯Altium官方网站获取账号的方法,可以立即使用。     1、点击http://live.altium-ru.com/#Join网址进入俄罗斯Altium官方网站,Altiu

snmplink的博客 1万+

深入浅出 Compose Compiler(1) Kotlin Compiler & KCP

Compose 的语法简洁、代码效率非常高,这主要得益于 Compose Compiler 的一系列编译期魔法,帮开发者生成了很多样板代码。但编译期插桩也阻碍了我们对于 Compose 运行原理的认知,想要真正读懂 Compose 就必须先了解它的 Compiler。本系列文章将带大家揭开 Compose Compiler 的神秘面纱。Compose 是一个 Kotlin Only 框架,所以 Compose Compiler 的本质是一个 KCP(Kotlin Compiler Plugin)。

移动端开发干货分享 2575

OpenClaw实战系列·进阶特辑02:多智能体协同——打造企业级AI中台

**企业级AI中台面临多Agent协同的挑战,本文提出基于多智能体协同的解决方案。通过分析单Agent的局限性(上下文溢出、专业度稀释等)和多Agent的核心优势(任务专注度、故障隔离等),提出三种协作架构模式(指挥型、联邦型、点对点型),并强调人格化设计的重要性。文章详细介绍Agent间通讯机制(同步/异步会话、文件系统协作)和"身脑分离"的解耦架构设计,提供可落地的企业级AI中台实施方案。

PeterPan的博客 493

Kotlin 元编程之 KSP 全面突破

没想到吧,这世上除了元宇宙,还有元编程,如果没有接触过,可能会有点懵,不过没关系,简单的说就是用代码来生成代码。实现元编程的传统常见手段主要是使用 APT注解处理器 +JavaPoet组合拳,如果你是作为一名Android 开发者,一定在曾经或者现在使用过很多知名的开源库,比如ARouter等,这些都是基于 注解处理器 +JavaPoet的方式实现的元编程,是的,虽然元编程这个词很高大上,但是可能你已经默默的使用了很多年了。

川峰的博客 5082

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Kotlin 移动端跨平台是种怎样的体验

前言 近些年,移动端无论是在技术还是在市场方面都已经逐步成熟,效率成为各家争夺存量 、开拓疆土的利器,因而跨平台技术的热度也如日中天。其中Flutter算得上是跨平台技术的网红,易上手、UI像素级同步、性能优异,引起一大波粉丝(各大厂)为它打call,各大厂商都为Flutter社区带来了很大贡献,迅速扩大了它的技术生态。实不相瞒,个人也是Flutter的小粉丝,既然Flutter这么好,那我还在...

a357664853的专栏 1251

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Sword - 为 Kotlin 函数增加代理功能(一)

Sword:一个可以给 Kotlin 函数增加代理的第三方库,基于 KCP 实现。

m0_64420071的博客 331

社区说 | Kotlin 编译器插件: 我们究竟在期待什么?

活动时间7月8日(本周四)20:00pm-21:00pm活动日程20:00-20:45《Kotlin 编译器插件:我们究竟在期待什么?》对比常见的 Kotlin 元编程的方案,包括反射、注...

Android开发者 394

社区说 | Kotlin 编译器插件:我们究竟在期待什么?

活动时间7月8日(本周四)20:00pm-21:00pm活动日程20:00-20:45《Kotlin 编译器插件:我们究竟在期待什么?》对比常见的 Kotlin 元编程的方案,包括反射、注...

谷歌开发者 571

拥抱Kotlin Symbol Processing(KSP),手把手带你实现Kotlin的专有注解处理

好玩系列 | 拥抱Kotlin Symbol Processing(KSP),项目实战 写在最前 这一篇,我们抱着拥抱新事物的心态,尝试一些新事物。笔者在这一次历程中,对三项事物进行了尝鲜: 手动迁移一个小规模的Gradle项目,由 Groovy Script 转为 Kotlin Script Kotlin Symbol Processing Kotlin Poet 这次的 重点是KSP ,Kotlin Poet学习成本比较低,迁移 Kotlin Script 仅仅是比较繁琐。 既然要实战,那么就需要一

leobert.lan的博客 2625

Kotlin Symbol Processing(KSP)使用初体验

近日,android官方发布了Kotlin Symbol Processing(KSP)的alpha版本。 Announcing Kotlin Symbol Processing (KSP) Alpha 为什么使用KSP? 很多人在使用Kotlin时的痛点之一就是编译速度过慢。 很多常见的三方库都通过注解简化模板代码,例如Room、Dagger、Retrofit等, Kotlin使用KAPT处理注解,KAPT没有专门的注解处理器,需要借助APT实现的,因为APT只能处理Java,所以KAPT需要生成A.

移动端开发干货分享 7556

@Compose 注解到底做了什么?了解一下

/ 今日科技快讯 /近日,由IDC、浪潮信息、清华大学全球产业研究院联合编制的《2021-2022全球计算力指数评估报告》在北京发布,量化揭示了全球主要国家GDP、数字经济与计算力...

郭霖 447

手把手带你实现Kotlin的专有注解处理

/ 今日科技快讯 /据TechWeb消息,国外媒体报道称,分析师估计,由于需求增加,电动汽车制造商特斯拉今年第二季度可能交付约20万辆汽车,同比增长100%。今年4月初,特斯拉宣布...

郭霖 1160

Kotlin cacheable - 缓存一切函数

在 Kotlin 中,Lazy 是我们经常用到的操作,当我们需要用到时才创建对象,只需要一个 lazy 即可搞定,如下:

大模型训练 1021

从 Kotlin 到 KSP 再到项目实践 KtorfitX:一段探索之路

本文讲述了作者从初识Kotlin到深入探索KSP技术的历程。2021年接触Kotlin后,作者逐渐从使用语法糖转向研究其生态扩展能力,特别是KSP技术。基于KSP,作者开发了ktorfitx项目,解决了Kotlin Multiplatform中网络请求重复编码的问题。这段经历让作者深刻体会到技术真正的价值在于解放开发者创造力,而非单纯追求语法优雅。文章展现了Kotlin从语言到开发平台的演进,以及如何通过技术创新解决实际问题。

a2965502228的博客 507
上一篇: Kotlin-KCP的应用-第一篇
下一篇: Java并发编程-Android的UI框架为什么是单线程的
guodongAndroid
博客等级 码龄10年 16粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值