Be open to new experiences!
命名的艺术:从驼峰到蛇形
不同编程语言往往有不同的命名约定。Java、Python、C++ 各自形成了独特的命名文化。许多重构类书籍在讲解前都会先强调命名,因为命名绝不是一件简单的事。好的命名要能准确传达含义,让人一眼就能看出变量或函数的用途。 这些命名方式的共同目标,都是为了增强可读性——让名字更像语言,而不是符号。
函数与方法:一个容易被忽视的编程概念差异
从学习C语言接触计算机开始,我总以为函数和方法不过是两个不同的名称而已,只不过是习惯不同。直到阅读Rust语言的文档时,同时出现了"方法"和"函数"两个术语,我才意识到这其中的差异。
Android依赖管理解析:从ext到 Version Catalog的演进
在 Android 多模块项目中,依赖管理(Dependency Management)往往被低估。 最开始你可能只是写几个 implementation "xxx:xxx:1.0.0",但随着模块数量增长、团队协作增加、版本升级频繁,依赖的统一管理会直接影响项目的可维护性与构建效率。
一次 Android APK 体积优化实录
现状及问题 # 最近工作上有些变动,新接手的项目中发现 apk 的 Debug 包有 170 多 MB,Release 包也要 140 MB。 仔细看项目内容,其实并没有那么多东西。想要减小体积,首先得搞清楚从哪里下手,于是决定先分析一下应用的体积构成。
理解 Kotlin Flow:冷热流与背压处理
使用场景 # 1. 持续输出数据(如定时器、进度条) # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 fun timerFlow(): Flow<Int> = flow { for (i in 1..5) { delay(1000) emit(i) // 每秒发出一个进度 } } fun main() = runBlocking { timerFlow() .onEach { println("Progress: $it/5") } .onCompletion { println("Done!") } .collect() } // 输出: // Progress: 1/5 // Progress: 2/5 // Progress: 3/5 // Progress: 4/5 // Progress: 5/5 // Done! 2. 在不同线程中执行 # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fun fetchData(): Flow<String> = flow { println("Running in thread: ${Thread.currentThread().name}") delay(1000) emit("Data from network") }.flowOn(Dispatchers.IO) // 在 IO 线程执行 fun main() = runBlocking { fetchData() .onEach { println("Collect on thread: ${Thread.currentThread().name}") } .collect { println("Received: $it") } } // 输出: // Running in thread: DefaultDispatcher-worker-1 // Collect on thread: main // Received: Data from network 3. 合并多个异步请求 # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fun getUser(): Flow<String> = flow { delay(500) emit("User: Alice") } fun getMessagesCount(): Flow<Int> = flow { delay(800) emit(42) } fun main() = runBlocking { combine(getUser(), getMessagesCount()) { user, count -> "$user, Unread messages: $count" }.collect { println(it) } } // 输出: // User: Alice, Unread messages: 42 这些特性让人想起了 RxJava…
Kotlin 编译器插件(译)
原文:https://kt.academy/article/ak-compiler-plugin 这是《Advanced Kotlin》一书的一个章节。您可以在 LeanPub 或 Amazon 上找到它。 Kotlin 编译器是一个用于编译 Kotlin 代码的程序,同时也被 IDE 用于提供代码补全、警告等分析功能。与许多程序一样,Kotlin 编译器可以使用插件来改变其行为。我们通过扩展一个名为扩展(extension)的特殊类来定义 Kotlin 编译器插件,然后使用注册器(registrar)来注册它。每个扩展在编译器工作的特定阶段被调用,从而可能改变该阶段的结果。例如,你可以注册一个插件,当编译器为类生成父类型时被调用,从而向结果添加额外的父类型。当我们编写编译器插件时,我们受限于所支持的扩展允许我们做的事情。我们很快会讨论当前可用的扩展,但让我们先从一些关于编译器如何工作的基础知识开始。
Kotlin 协程:Job 与 SupervisorJob 的差异
我们都知道,在 Kotlin 协程中,可以使用 Job 或 SupervisorJob 来构建一个作用域(CoroutineScope): 1 2 3 4 5 6 7 8 9 10 11 // SupervisorJob val scope = CoroutineScope(SupervisorJob()) scope.launch { ... } // Job val scope = CoroutineScope(Job()) scope.launch { ... } 两者的区别似乎仅在于构造函数不同。点开源码一看,差别的确只有一个方法:
Paging3核心解析:Kotlin Flow 如何实现内存与数据双高效
Paging 库是什么? # Paging 库的核心作用是帮助 Android 应用高效地加载和展示大数据集。 人话解释: 它就像一个聪明的服务员,不会一次性把所有菜都端上来(避免内存爆炸),而是根据你的需求(分页)逐步、按需从本地数据库或网络获取一小部分数据。这样能显著降低内存压力,提供流畅的用户体验。
深入 Compose:从 setContent 到 LayoutNode 绘制原理
在之前,我一直以为 Compose 是深不可测、难以理解的东西,直到我打开一个 Compose 项目的 setContent 方法,才发现它其实并没有那么复杂。 Compose 和 XML 的不同,只是写 UI 的方式不同而已。而关键差别,就在 setContent 方法上。注意,这里的 setContent 与我们平时在 Activity 中调用的、传入一个 View 或布局 id 的 setContentView 并不是同一个方法。它只是名字相同,实际上完全是另一套实现:
Jetpack DataStore:Android应用中的现代数据存储方法
在学习 DataStore 之前,需要先了解 Kotlin 属性委托 的概念。 Kotlin 属性委托简介 # Kotlin 支持通过 属性委托 (by) 来委托属性的获取和设置逻辑。 基本规则是:如果一个对象实现了如下方法: 1 operator fun getValue(thisRef: Any?, property: KProperty<*>): A 那么就可以用 by 实例 的方式将属性委托给该对象。 示例: