opoojkk

没有什么困难是不能打败我的!

C++ 学习笔记:指针与引用

指针是什么 # 在 Java 和 Kotlin 这些语言里,你不能直接操作内存地址,只需要关心生命周期,避免被生命周期更长的对象引用,进而导致内存不能释放。这是一种内存管理方案。而 C++ 则允许显式地操作内存——更具体一点,是允许直接处理指针或你手动分配的内存。Rust 又走了另一条路:归属模型。一块内存只能被一个变量拥有,其他变量想要用它必须“借”,目的是规避 C/C++ 中指针带来的野指针、悬空指针之类的问题。

从模拟信号到补码:计算机如何存储和计算数字

模电 # 总常听到计算机的世界中只有0和1这两个,现实也是这样。但是自然界中的数据通常不是这样不是0就是1这般非黑即白的。像是声音、电流等等,都是连续的。 为了能存储这些数据,就要转换成计算机的方式存储,那就是用二进制表示,怎么将连续的数值转换成二进制呢?先后经过模电(模拟电路)中的采样、量化、编码。

AAR 构建全解析:从解包到 APK 的完整生命周期

依赖里有一堆 AAR,最终 APK 里却不见它们的影子。 不是它们消失了,而是它们一进构建系统就被解包,残片分别流向: dex manifest resources.arsc assets so 下面按真实的 AGP 源码线把整个事件复盘一遍。 AAR 生命周期的起点: ExtractAarTransform # AAR 被下载后,首先交给一个类处理:

命名的艺术:从驼峰到蛇形

不同编程语言往往有不同的命名约定。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 { ... } 两者的区别似乎仅在于构造函数不同。点开源码一看,差别的确只有一个方法: