# Compose_Kotlin **Repository Path**: zhangkede/compose_kotlin ## Basic Information - **Project Name**: Compose_Kotlin - **Description**: 常见面试题,以及使用示例 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-22 - **Last Updated**: 2026-07-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Compose_Kotlin Kotlin Multiplatform project targeting Android, iOS, Web, Desktop (JVM), Server, and HarmonyOS. ## 项目结构 ``` Compose_Kotlin/ ├── app/ │ ├── androidApp/ # Android 应用 │ ├── iosApp/ # iOS 应用 │ ├── desktopApp/ # 桌面应用 (JVM) │ ├── webApp/ # Web 应用 │ └── shared/ # Compose 共享 UI 模块 ├── core/ # 核心业务逻辑模块 ├── common/ # 通用实体和工具模块 └── server/ # Ktor 服务端应用 ``` ### 目录说明 - **`/app/iosApp`** - iOS 应用入口,用于 iOS 平台原生集成 - **`/app/shared`** - 跨平台 Compose UI 代码 - `commonMain` - 所有目标通用代码 - 其他平台文件夹存放特定平台代码 - **`/core`** - 跨平台业务逻辑 - `commonMain` - 通用业务逻辑实现 - `openHarmonyMain` - 鸿蒙平台导出函数(@CName) - **`/server`** - Ktor 服务端应用 --- ## 鸿蒙 (HarmonyOS) 适配 ### 方案概述 本项目采用 KMP 方案一:**仅共享业务逻辑**,使用 `linuxArm64("openHarmony")` 作为鸿蒙编译目标,通过 NAPI 供 ArkTS 调用。 ``` KMP 业务逻辑 ──编译──→ libcore.so ──NAPI──→ 鸿蒙 ArkTS 调用 ``` ### 环境要求 | 工具 | 版本 | |------|------| | Kotlin | 2.4.10+ | | Gradle | 9.0+ | | DevEco Studio | 5.0+ | | HarmonyOS SDK | API 12+ | ### 配置步骤 #### 1. 配置鸿蒙 SDK 路径 编辑 `local.properties`: ```properties sdk.dir=D\:\\Dev\\Android\\SDK ohos.sdk.dir=C:/Users/{user}/AppData/Local/Huawei/Sdk ``` #### 2. 配置 Gradle(已完成) `gradle.properties`: ```properties kotlin.mpp.ohos.targets.enabled=true ``` `core/build.gradle.kts`: ```kotlin val openHarmony = linuxArm64("openHarmony") openHarmony.apply { binaries { sharedLib { baseName = "core" } } } sourceSets { val openHarmonyMain by getting { dependsOn(commonMain) } } ``` ### 鸿蒙业务功能 #### 通用业务逻辑(commonMain) | 文件 | 功能 | |------|------| | `StringUtils.kt` | 字符串反转、拼接、大小写转换 | | `MathUtils.kt` | 两数相加、相乘、阶乘计算 | | `ConvertUtils.kt` | Int/Long 转字符串、时间戳格式化 | #### 鸿蒙导出函数(openHarmonyMain) 通过 `@CName` 注解导出,供鸿蒙 NAPI 调用: | 分类 | 导出名称 | 功能 | |------|----------|------| | 字符串 | `Kotlin_String_reverse` | 字符串反转 | | | `Kotlin_String_concat` | 字符串拼接 | | | `Kotlin_String_toUpper` | 转大写 | | | `Kotlin_String_toLower` | 转小写 | | 数字 | `Kotlin_Math_add` | 两数相加 | | | `Kotlin_Math_multiply` | 两数相乘 | | | `Kotlin_Math_factorial` | 计算阶乘 | | 转换 | `Kotlin_Convert_intToString` | Int 转字符串 | | | `Kotlin_Convert_longToString` | Long 转字符串 | | | `Kotlin_Convert_formatTimeMillis` | 格式化时间戳 | | 版本 | `Kotlin_getVersion` | 获取版本信息 | ### 编译与产物 ```bash # 编译鸿蒙 release 版本 ./gradlew :core:linkReleaseSharedOpenHarmony # 编译鸿蒙 debug 版本 ./gradlew :core:linkDebugSharedOpenHarmony # 运行单元测试 ./gradlew :core:jvmTest ``` 产物位置: ``` core/build/bin/openHarmony/releaseShared/ ├── libcore.so # 动态库 (ELF ARM64) └── libcore_api.h # C API 头文件 ``` ### 鸿蒙工程集成 #### 1. 复制 KMP 产物 ```bash cp core/build/bin/openHarmony/releaseShared/libcore.so \ HarmonyKmpApp/entry/src/main/cpp/libs/arm64-v8a/ cp core/build/bin/openHarmony/releaseShared/libcore_api.h \ HarmonyKmpApp/entry/src/main/cpp/include/ ``` #### 2. 创建 CMakeLists.txt ```cmake cmake_minimum_required(VERSION 3.4.1) project(kmp_napi) set(CMAKE_CXX_STANDARD 17) include_directories(include) import_library(libcore ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcore.so) set(SOURCES napi_init.cpp) add_library(kmp_napi SHARED ${SOURCES}) target_link_libraries(kmp_napi libcore ${NAPI_LIBRARIES}) ``` #### 3. 编写 NAPI 胶水层 `entry/src/main/cpp/napi_init.cpp`: ```cpp #include #include "libcore_api.h" Napi::Value ReverseString(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); std::string input = info[0].As().Utf8Value(); const char* result = Kotlin_String_reverse(input.c_str()); return Napi::String::New(env, result); } Napi::Object Init(Napi::Env env, Napi::Object exports) { exports.Set("reverseString", Napi::Function::New(env, ReverseString)); return exports; } NODE_API_MODULE(kmp_napi, Init) ``` #### 4. ArkTS 调用 `entry/src/main/ets/kmp/KmpBridge.ets`: ```typescript export interface KmpBridge { reverseString(str: string): string; addNumbers(a: number, b: number): number; // ... 其他函数 } const kmpBridge: KmpBridge = requireNapi('kmp_napi', 1); export default kmpBridge; ``` #### 5. 页面使用 ```typescript import kmpBridge from '../kmp/KmpBridge'; @Entry @Component struct Index { @State reversedText: string = ''; aboutToAppear() { this.reversedText = kmpBridge.reverseString('Hello'); } build() { Column() { Text(`结果: ${this.reversedText}`) } } } ``` ### 调用流程 ``` ArkTS 页面 NAPI 桥接 C++ 胶水层 KMP .so ───────── ────────── ────────── ─────── kmpBridge requireNapi ReverseString Kotlin_ .reverseStr ('kmp_napi') (info) String_ ing('Hello') reverse() │ │ ▼ ▼ "olleH" str.reversed() ``` ### 注意事项 1. **实验性特性**: `linuxArm64` 是变通方案,待 Kotlin 官方支持 `ohosArm64` 后可迁移 2. **参数限制**: `@CName` 仅支持基本类型(Int、Long、String),复杂类型需 JSON 序列化 3. **库兼容性**: 第三方库(Ktor、SQLDelight)需检查 Native 兼容性 4. **调试困难**: Native 层调试需要 lldb,建议多写单元测试验证逻辑 --- ## 运行应用 使用 IDE 工具栏的运行配置,或执行以下命令: - **Android**: `./gradlew :app:androidApp:assembleDebug` - **Desktop**: - Hot reload: `./gradlew :app:desktopApp:hotRun --auto` - Standard run: `./gradlew :app:desktopApp:run` - **Server**: `./gradlew :server:run` - **Web**: - Wasm target: `./gradlew :app:webApp:wasmJsBrowserDevelopmentRun` - JS target: `./gradlew :app:webApp:jsBrowserDevelopmentRun` - **iOS**: 在 Xcode 中打开 `/app/iosApp` 目录运行 - **鸿蒙**: 使用 DevEco Studio 打开鸿蒙工程运行 ## 运行测试 - **Android**: `./gradlew :app:shared:testAndroidHostTest` - **Desktop**: `./gradlew :app:shared:jvmTest` - **Server**: `./gradlew :server:test` - **Web**: - Wasm: `./gradlew :app:shared:wasmJsTest` - JS: `./gradlew :app:shared:jsTest` - **iOS**: `./gradlew :app:shared:iosSimulatorArm64Test` - **Core**: `./gradlew :core:jvmTest` --- ## Kotlin 高频面试题 ### 一、基础语法 1. **val 与 var 区别** - `val`:只读,**不可变**,对应 Java final - `var`:可变,可重新赋值 2. **lateinit 与 by lazy 懒加载** - `lateinit var`:延迟初始化**可变属性**,不能为基本类型,必须手动初始化 - `val xxx by lazy{}`:线程安全懒加载,**只读**,首次调用才初始化 3. **?. / ?: / !! 区别** - `?.`:安全调用,为空不执行 - `?:`:空值默认值 `a ?: b` - `!!`:强行非空,空直接崩溃,**慎用** 4. **空安全原理** Kotlin 区分**可空类型** `String?` 和**非空** `String`,编译期强制判空,减少 NPE。 5. **const val 与 val** - `const val`:编译期常量,只能顶层/伴生对象,Java 可直接访问 - `val`:运行时常量 ### 二、类与面向对象 6. **主构造函数、次构造函数** - 主构造:`class User(name:String)` - 次构造:`constructor()` 7. **open 关键字** Kotlin 类、方法**默认 final 不可继承**,加 open 才可被重写/继承。 8. **data class 数据类** 自动生成:`equals、hashCode、toString、copy、componentN`,只存数据用。 9. **sealed class 密封类** 限制子类范围,**同一文件内继承**,适合状态枚举、分支判断。 10. **object / companion object** - `object`:单例类 - `companion object`:类内静态成员,替代 Java static 11. **枚举类 enum class** 固定常量集合,可带属性、方法。 ### 三、函数与高阶函数 12. **顶层函数** 直接写在 .kt 文件,无需类包裹,全局调用。 13. **内联函数 inline** 消除函数调用开销,减少 lambda 匿名类生成,常用:`let、run、with、apply、also` 14. **5 大作用域函数区别** - `let`:传 it,返回最后结果,空判首选 - `run`:无 it,this 调用,返回结果 - `with`:传入对象,this 调用 - `apply`:返回自身,**初始化对象首选** - `also`:返回自身,it 调用,附加操作 15. **高阶函数** 参数/返回值是函数,Kotlin 核心特性。 16. **尾递归 tailrec** 优化递归,避免栈溢出,必须**最后一行调用自身**。 ### 四、集合 17. **List / MutableList** - List 只读,MutableList 可变 - 常用:`map、filter、flatMap、distinct、sortedBy` 18. **Sequence 序列** **懒执行**,大数据量遍历性能高于 List。 ### 五、协程(重中之重) 19. **协程是什么** 轻量级线程,开销小,线程框架封装,异步简洁。 20. **三大调度器** - `Dispatchers.Main` 主线程 - `Dispatchers.IO` 网络/IO - `Dispatchers.Default` 密集计算 21. **launch 与 async** - `launch`:无返回值,启动协程 - `async+await`:**有返回值**,获取结果 22. **withContext** 切换线程,**同步写法**,不用回调。 23. **协程取消** `job.cancel()`,主动取消,子协程跟随取消。 24. **viewModelScope / lifecycleScope** 页面销毁**自动取消协程**,防内存泄漏。 25. **Flow 数据流** 冷流,订阅才发射,替代 LiveData,支持多次订阅、组合流。 ### 六、进阶考点 26. **扩展函数/扩展属性** 不修改原类,新增方法,静态解析,不能访问私有成员。 27. **委托 by** - 类委托:快速复用另一个类实现 - 属性委托:lazy、共享属性、自定义委托 28. **泛型 out / in / \*** - `out` 协变:只读 - `in` 逆变:只写 - `*` 星投影 29. **Kotlin 与 Java 互调用** Kotlin 调用 Java 顺畅;Java 调用 Kotlin 需注意:伴生、顶层函数、可空类型。 30. **Kotlin 优缺点** 优点:空安全、简洁、协程、扩展函数、语法糖多 缺点:编译慢、低版本兼容、部分语法晦涩 ### 七、手写常考 1. 手写单例(饿汉、懒汉、双重校验锁、Kotlin object 单例) 2. 手写 apply 扩展函数 3. 协程网络请求简单封装 4. data class 数据类使用 5. Flow 简单使用 --- ## Kotlin 手写代码实战示例 ### 一、空安全全套写法 ```kotlin // 1. 可空与非空 var name: String = "张三" var nick: String? = null // 2. 安全调用 ?. nick?.length // 3. 空合并 ?: val realName = nick ?: "匿名" // 4. 非空断言 !! 慎用 nick!!.isEmpty() // 5. 空值提前返回 fun getInfo(str: String?): String { str ?: return "为空" return str } // 6. if判空 if (nick != null) { nick.length } ``` ### 二、lateinit / by lazy 实战 ```kotlin // 延迟初始化 可变变量 lateinit var content: String fun initData() { content = "已赋值" } // 懒加载 只读 线程安全 val userInfo by lazy { println("只初始化一次") "用户信息" } // 懒加载带参数 val config by lazy(LazyThreadSafetyMode.NONE) { "非线程安全" } ``` ### 三、构造函数示例 ```kotlin // 主构造函数 class Person(val name: String, var age: Int) // 主构造+初始化块 class Student(name: String) { init { println("初始化:$name") } } // 次构造函数 class Animal { var type: String = "" constructor(t: String) { type = t } } ``` ### 四、继承+重写 open ```kotlin open class Father { open fun say() { println("父亲") } } class Son : Father() { override fun say() { super.say() println("儿子") } } ``` ### 五、数据类 DataClass 全套用法 ```kotlin data class Book( val id: Int, val bookName: String, var price: Double ) // 使用 val b1 = Book(1, "Kotlin入门", 39.9) val b2 = b1.copy(price = 49.9) // 拷贝修改 println(b1.toString()) println(b1 == b2) // 自动 equals ``` ### 六、密封类实战(网络状态最常用) ```kotlin sealed class NetState { object Loading : NetState() data class Success(val data: T) : NetState() data class Error(val errMsg: String, val code: Int) : NetState() } // 使用 fun handleResult(state: NetState) { when (state) { is NetState.Loading -> {} is NetState.Success<*> -> {} is NetState.Error -> {} } } ``` ### 七、枚举类 ```kotlin enum class OrderStatus(val desc: String) { WAIT_PAY("待付款"), PAID("已付款"), CANCEL("已取消"); fun getTip() = "订单状态:$desc" } ``` ### 八、object 单例 & 伴生对象 ```kotlin // 全局单例 object AppConfig { const val BASE_URL = "https://api.com" fun printLog() {} } // 类内伴生对象 模拟 static class HttpUtil { companion object { var timeout = 5000 fun get() {} } } ``` ### 九、5 大作用域函数实战 ```kotlin data class User(var name: String = "", var age: Int = 0) // apply 初始化对象 val user = User().apply { name = "李四" age = 20 } // let 空判断+转换 val nameStr: String? = "测试" nameStr?.let { println(it.length) } // run 混合执行 val result = user.run { age + 10 } // also 附加操作 user.also { println(it.name) }.age = 30 // with 简化调用 with(user) { name = "新名字" println(age) } ``` ### 十、扩展函数 / 扩展属性 ```kotlin // String 扩展 fun String.toast() = println(this) fun String.isEmail(): Boolean { return matches(Regex("^\\w+@\\w+\\.\\w+$")) } // Int 扩展 fun Int.double(): Int = this * 2 // 扩展属性 var String.tag: String get() = this set(value) {} // 使用 "123@qq.com".isEmail() 10.double() ``` ### 十一、高阶函数 + Lambda ```kotlin // 定义高阶函数 fun calculate(a: Int, b: Int, block: (Int, Int) -> Int): Int { return block(a, b) } // 调用 val sum = calculate(2, 3) { x, y -> x + y } val sub = calculate(5, 1) { x, y -> x - y } // 无参 lambda fun doWork(block: () -> Unit) { block() } doWork { println("执行") } ``` ### 十二、内联函数 inline ```kotlin inline fun logMsg(msg: String, noinline block: () -> Unit) { println(msg) block() } ``` ### 十三、尾递归 tailrec ```kotlin // 求阶乘 tailrec fun factorial(n: Int, res: Long = 1): Long { if (n <= 1) return res return factorial(n - 1, n * res) } ``` ### 十四、集合常用函数全示例 ```kotlin val nums = listOf(1, 2, 3, 4, 5, 5) nums.filter { it > 2 } // 过滤 nums.map { it * 2 } // 映射 nums.flatMap { listOf(it, it) } // 扁平化 nums.distinct() // 去重 nums.sorted() // 升序 nums.sortedDescending() // 降序 nums.any { it == 3 } // 是否存在 nums.all { it > 0 } // 全部满足 nums.firstOrNull() // 安全取第一个 nums.groupBy { it % 2 } // 分组 ``` ### 十五、Sequence 懒序列 ```kotlin val seq = nums.asSequence() .map { it * 3 } .filter { it > 5 } .toList() // 只有最后才执行 ``` ### 十六、泛型 in 逆变 out 协变 ```kotlin // 协变 out 只能读 interface ReadData { fun get(): T } // 逆变 in 只能写 interface WriteData { fun set(data: T) } // 泛型函数 fun printAny(data: T) = println(data) ``` ### 十七、属性委托 by ```kotlin // 1. lazy 委托 val info by lazy { "委托内容" } // 2. 自定义属性委托 class MyDelegate { operator fun getValue(thisRef: Any?, property: kotlin.reflect.KProperty<*>): String { return "委托取值" } } var text: String by MyDelegate() ``` ### 十八、类委托 ```kotlin interface IClick { fun click() } class ClickImpl : IClick { override fun click() = println("点击") } // 直接委托实现类 class Button(click: IClick) : IClick by click ``` ### 十九、协程全套常用示例 ```kotlin // 1. 基础协程 launch(Dispatchers.IO) { delay(1000) withContext(Dispatchers.Main) { // 更新 UI } } // 2. async 并发请求 scope.launch { val a = async { delay(1000); 1 } val b = async { delay(1000); 2 } val total = a.await() + b.await() } // 3. withContext 简化切换 suspend fun getNetData(): String = withContext(Dispatchers.IO) { "网络数据" } // 4. 协程取消 val job = launch {} job.cancel() // 5. ViewModel 生命周期安全 viewModelScope.launch {} // 6. Flow 数据流 fun numFlow() = flow { emit(1) emit(2) }.flowOn(Dispatchers.IO) // 收集 lifecycleScope.launch { numFlow().collect { println(it) } } ``` ### 二十、const val 编译期常量 ```kotlin const val VERSION = 1 val NAME = "运行时常量" // 顶层常量 const val HOST = "127.0.0.1" ``` ### 二十一、解构声明 ```kotlin data class UserInfo(val id: Int, val name: String) val (id, name) = UserInfo(1, "小明") // 数组解构 val (a, b) = arrayOf(10, 20) ``` ### 二十二、区间 / 循环 ```kotlin for (i in 1..10) {} for (i in 1 until 10) {} for (i in 10 downTo 1) {} repeat(3) { println("重复") } ``` ### 二十三、Java 与 Kotlin 互调要点 ```kotlin // Kotlin 调用 Java 顺畅 // Java 调用 Kotlin // 1. 顶层函数:文件名.方法名 // 2. 伴生对象:类名.静态方法 // 3. 可空类型 Java 要判空 ``` ### 二十四、枚举 when 极简写法 ```kotlin fun checkType(type: OrderStatus) = when (type) { OrderStatus.WAIT_PAY -> "待付款" OrderStatus.PAID -> "已付款" else -> "其他" } ``` --- ## Jetpack Compose 高频面试题 ### 一、基础概念 1. **Compose 是什么** 声明式 UI 框架,抛弃 XML,纯 Kotlin 编写 UI,**数据驱动视图**,状态变 UI 自动刷新。 2. **@Composable 作用** 标记可组合函数,只能在**Composable 函数/预览/Activity**调用,不能普通函数直接调用。 3. **Compose 与 XML 区别** - XML:命令式,findViewById、setContentView、手动更新 - Compose:声明式,只管描述 UI,状态驱动自动刷新,代码更少 4. **重组(Recomposition)** 状态变化时,**局部刷新依赖该状态的 Composable**,不是全局重绘,高效更新 UI。 ### 二、状态 State 核心(必考) 5. **remember 作用** 组件重组时**保存数据**,防止重组重置变量。 6. **remember + mutableStateOf 原理** - `mutableStateOf`:创建**可观察状态** - `remember`:重组保留状态实例 - 组合使用:`var text by remember { mutableStateOf("") }` 7. **rememberSaveable 用途** 屏幕旋转、页面重建**保留数据**,比 remember 更持久,存 Bundle。 8. **State / MutableState 区别** - State:只读 - MutableState:可读写,修改触发重组 9. **by 委托写法原理** 简化`.value`读写,`var a by state`等价`a.value`。 10. **状态提升(State Hoisting)** 把内部状态抽到上层组件,**子组件只接收参数+回调**,实现解耦复用。 ### 三、布局与控件 11. **三大基础布局** - `Row`:水平排列 - `Column`:垂直排列 - `Box`:层叠布局(重叠) 12. **Modifier 执行顺序** **顺序影响效果**,靠前先执行: `Modifier.padding().background()` 不等于 `background().padding()` 13. **Modifier 常用属性** size、padding、background、clickable、align、weight、border、shadow 14. **权重平分写法** Row/Column 内 `Modifier.weight(1f)` 均分空间 15. **Spacer 作用** 空白占位,替代空 View,快速留白 ### 四、点击、手势、事件 16. **clickable 与 pointerInput 区别** - clickable:简单点击、长按 - pointerInput:复杂手势(拖动、滑动、多指) 17. **无波纹点击** ```kotlin indication = null, interactionSource = remember { MutableInteractionSource() } ``` ### 五、生命周期 & 副作用(高频难点) 18. **常用副作用函数** - **LaunchedEffect**:协程,重组执行挂起函数(网络请求、延时) - **SideEffect**:每次重组执行 - **DisposableEffect**:页面销毁/移除执行释放资源(关闭监听、取消订阅) - **rememberCoroutineScope**:组件作用域协程,组件销毁自动取消 19. **LaunchedEffect 传参规则** 传**key**,key 不变不重复执行,key 变化重新执行。 20. **组件进入页面只执行一次** ```kotlin LaunchedEffect(Unit) { 初始化逻辑 } ``` ### 六、列表 LazyColumn / LazyRow 21. **LazyColumn 优势** **复用 Item**,媲美 RecyclerView,高性能长列表 22. **列表滑动监听** `rememberLazyListState()` 监听滚动位置、是否到底 23. **列表置顶/滑动指定位置** `listState.scrollToItem(index)` 24. **item 与 items 区别** - item:单个条目 - items:批量集合数据 ### 七、导航 Compose Navigation 25. **Compose 导航核心** `NavHost + NavController + 路由 Route` 26. **页面跳转、传参** - 跳转:`navController.navigate("路由")` - 回退:`navController.popBackStack()` - 传参:路径拼接参数 / 序列化对象传参 ### 八、性能优化(面试重点) 27. **Compose 优化手段** 1. 稳定参数(添加 `@Stable`)减少不必要重组 2. 抽取小型独立 Composable,缩小重组范围 3. 复杂计算用 `remember` 缓存结果 4. 列表优先 Lazy 系列,禁止全渲染 Column 5. 避免 Composable 内创建对象、new 监听 6. 回调函数使用 `remember` 缓存 28. **什么是稳定类型 Stable** 数据不可变/变化可被 Compose 感知,不稳定类型极易引发无效重组。 29. **怎么查看重组次数** 开启**重组高亮**,绿色=重组,蓝色=无重组。 ### 九、兼容与混合开发 30. **Compose 嵌套 XML / XML 嵌套 Compose** - Compose 引入原生 View:`AndroidView` - XML 引入 Compose:`ComposeView` 31. **Compose 最低兼容版本** Android 5.0(API21) ### 十、手写常考代码题 1. **双向绑定输入框** ```kotlin var text by remember { mutableStateOf("") } TextField(value = text, onValueChange = { text = it }) ``` 2. **页面进入请求网络** ```kotlin LaunchedEffect(Unit) { // 挂起请求 } ``` 3. **点击计数案例** ```kotlin var count by remember { mutableStateOf(0) } Column { Text("$count") Button({ count++ }) { Text("点击") } } ``` 4. **简单横向列表** ```kotlin LazyRow { items(10) { Text("item$it") } } ``` ### 十一、进阶面试题 32. **Compose 重组原理** 基于**快照系统 Snapshot**,状态标记脏数据,智能比对只刷新依赖组件。 33. **横向/竖向滚动** `HorizontalScroll` / `VerticalScroll` 34. **沉浸式状态栏适配 Compose** 用 `WindowInsets` 适配系统栏,获取状态栏高度。 35. **Compose 预览报错常见原因** - 预览函数无参 - 调用非 Compose 环境代码 - 未添加 @Preview 注解 --- ## Jetpack Compose 工作实战踩坑问题 + 解决方案 ### 一、重组相关(最常遇到) 1. **无故频繁重组,页面卡顿** - **问题**:页面轻微操作全页面重组,UI 卡顿、性能差 - **原因**:传入不稳定参数、匿名回调每次重建、对象未加稳定标记 - **解决**: - 回调用 `remember` 缓存 `val click = remember{{}}` - 数据实体类加 `@Stable` - 拆分细粒度小组件,缩小重组范围 - 避免直接传临时创建集合 2. **remember 屏幕旋转数据丢失** - **问题**:切屏、分屏、进程重启状态清空 - **解决**:替换 `remember` → **rememberSaveable** - 复杂对象用 `mapSaver` 序列化保存 3. **重组后变量重置** - **问题**:Composable 内部普通变量重组归零 - **解决**:必须用 `remember + MutableState` 托管状态 ### 二、列表 LazyColumn 实战问题 4. **LazyColumn 滑动卡顿、item 错乱** - **原因**:item key 重复、item 内频繁创建对象、嵌套过多布局 - **解决**: - 手动指定 `key = { it.id }` 防止复用错乱 - item 内部减少嵌套,提前预处理数据 - 图片做内存缓存,禁止 item 内实时计算 5. **LazyColumn 嵌套滚动冲突** - **问题**:外层可滚动 + LazyColumn 滑动失灵 - **解决**:使用 `nestedScroll` 统一滚动协调 6. **列表到底自动加载更多失效** - **问题**:滑动到底监听不准、重复请求 - **解决**:通过 `lazyListState.layoutInfo` 判断最后可见条目,加加载中锁防重复请求 ### 三、状态与数据流转问题 7. **子组件无法修改父状态,耦合严重** - **问题**:多层组件状态传参繁琐 - **解决**:使用**状态提升**,子只接收参数+回调,不持有状态;复杂页面用 ViewModel 统一托管状态 8. **双向绑定输入框光标乱跑** - **问题**:TextField 输入文字光标跳末尾 - **原因**:状态更新时机不对、频繁重组刷新 - **解决**:统一单一数据源,不局部二次修改文本 9. **弹窗/Dialog 关闭状态异常** - **问题**:弹窗关闭后状态残留,再次打开异常 - **解决**:用布尔值精准控制显示隐藏,销毁时清空状态 ### 四、副作用与协程坑 10. **LaunchedEffect 重复执行多次** - **问题**:页面进来多次发起网络请求 - **原因**:key 频繁变化、重组反复触发 - **解决**:只传固定 key `LaunchedEffect(Unit)` 只执行一次;动态依赖只传必要字段 11. **协程内存泄漏** - **问题**:页面退出网络请求还在执行 - **解决**: - 组件内用 `rememberCoroutineScope` 自动跟随组件销毁取消 - DisposableEffect 做资源释放、取消订阅、关闭监听 12. **延时任务重组后重复触发** - **解决**:延时逻辑统一放入 LaunchedEffect,不写在直接 UI 代码里 ### 五、Modifier 布局踩坑 13. **Modifier 顺序错误导致样式异常** - **问题**:padding、background、clickable 效果错乱 - **经验**:**由内向外写** - padding → size → background → clickable → 对齐 14. **权重 weight 无效** - **原因**:外层没给固定宽高、嵌套层级不对 - **解决**:Row/Column 直接包裹,父布局填充屏幕 15. **点击区域大/无点击波纹** - 扩大点击:`Modifier.clickable().size()` - 去掉波纹:配置空 interactionSource ### 六、导航 Navigation 问题 16. **Compose 导航页面跳转闪退、参数丢失** - **解决**:路由地址统一常量管理,传参规范格式,非基础类型用序列化传递 17. **页面重复入栈,返回栈混乱** - **解决**:跳转时设置 `launchSingleTop = true` 防重复打开 ### 七、混合开发兼容问题 18. **XML 与 Compose 互相嵌套适配异常** - **问题**:原生 View 嵌套 Compose 尺寸错乱、触控冲突 - **解决**: - Compose 嵌入原生:`AndroidView` 精准测量宽高 - XML 嵌入 Compose:`ComposeView` 统一生命周期 19. **低版本机型 Compose 样式兼容差异** - **问题**:不同手机圆角、阴影、字体显示不一致 - **解决**:统一封装全局主题 Theme,统一尺寸、颜色、字体 ### 八、UI 适配与沉浸式 20. **状态栏/导航栏遮挡 Compose 页面** - **解决**:使用 `WindowInsets` 适配系统栏,填充安全区域,自动避开状态栏、挖孔屏 ### 九、图片与加载问题 21. **Coil 加载图片重组重复刷新** - **解决**:图片请求数据用 remember 缓存,避免重组重新请求网络图片 ### 十、预览调试问题 22. **Android Studio Compose 预览不生效、报错** - **常见原因**: - 预览函数带参数 - 内部调用上下文、生命周期代码 - 依赖版本不统一 - **解决**:纯 UI 无参预览函数,拆分业务逻辑与 UI ### 十一、整体性能优化实战总结(面试必说) 1. 拆分组件,减少重组范围 2. 稳定数据类 + 缓存回调 3. 列表必用 Lazy 系列,拒绝嵌套 ScrollView 4. 状态统一 ViewModel 管理,避免多层传值 5. 所有异步、网络、监听全部交由副作用函数管理 6. 页面退出及时释放所有资源,杜绝内存泄漏 --- ## Compose + MVVM + MVI 完整登录案例 ### 架构分层 1. **View层**:Compose UI 只发事件、渲染状态 2. **Intent**:用户行为意图 3. **ViewModel**:接收Intent、处理逻辑、更新状态 4. **State**:页面唯一状态 5. **Repository**:数据层(网络/本地) 6. **Model**:数据实体 --- ### 1. 登录意图 LoginIntent ```kotlin sealed class LoginIntent { // 账号输入 data class InputAccount(val account: String) : LoginIntent() // 密码输入 data class InputPwd(val pwd: String) : LoginIntent() // 点击登录 object LoginSubmit : LoginIntent() // 清空错误 object ClearError : LoginIntent() } ``` ### 2. 登录状态 LoginUiState ```kotlin data class LoginUiState( val account: String = "", val password: String = "", val isLoading: Boolean = false, val errorMsg: String? = null, val loginSuccess: Boolean = false ) ``` ### 3. 仓库层 LoginRepository ```kotlin class LoginRepository { // 模拟登录网络请求 suspend fun login(account: String, pwd: String): Result { delay(1000) return if (account == "123456" && pwd == "666666") { Result.success("登录成功") } else { Result.failure(Exception("账号或密码错误")) } } } ``` ### 4. MVI ViewModel ```kotlin class LoginViewModel : ViewModel() { private val repo = LoginRepository() // 页面状态 private val _uiState = MutableStateFlow(LoginUiState()) val uiState: StateFlow = _uiState // 接收用户意图 fun sendIntent(intent: LoginIntent) { when (intent) { is LoginIntent.InputAccount -> updateAccount(intent.account) is LoginIntent.InputPwd -> updatePwd(intent.pwd) LoginIntent.LoginSubmit -> doLogin() LoginIntent.ClearError -> clearError() } } private fun updateAccount(account: String) { _uiState.update { it.copy(account = account) } } private fun updatePwd(pwd: String) { _uiState.update { it.copy(password = pwd) } } private fun clearError() { _uiState.update { it.copy(errorMsg = null) } } private fun doLogin() { val state = _uiState.value if (state.account.isBlank()) { _uiState.update { it.copy(errorMsg = "请输入账号") } return } if (state.password.isBlank()) { _uiState.update { it.copy(errorMsg = "请输入密码") } return } viewModelScope.launch { _uiState.update { it.copy(isLoading = true, errorMsg = null) } val result = repo.login(state.account, state.password) result.onSuccess { _uiState.update { it.copy(isLoading = false, loginSuccess = true) } }.onFailure { _uiState.update { it.copy(isLoading = false, errorMsg = it.message) } } } } } ``` ### 5. Compose 登录UI页面 ```kotlin @Composable fun LoginPage( viewModel: LoginViewModel = viewModel(), onLoginSuccess: () -> Unit ) { // 收集状态 val uiState by viewModel.uiState.collectAsStateWithLifecycle() // 登录成功跳转 LaunchedEffect(uiState.loginSuccess) { if (uiState.loginSuccess) { onLoginSuccess() } } Column( modifier = Modifier .fillMaxSize() .padding(20.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { // 账号输入 TextField( value = uiState.account, onValueChange = { viewModel.sendIntent(LoginIntent.InputAccount(it)) viewModel.sendIntent(LoginIntent.ClearError) }, placeholder = { Text("请输入账号") }, singleLine = true, modifier = Modifier.fillMaxWidth() ) Spacer(modifier = Modifier.height(12.dp)) // 密码输入 TextField( value = uiState.password, onValueChange = { viewModel.sendIntent(LoginIntent.InputPwd(it)) viewModel.sendIntent(LoginIntent.ClearError) }, placeholder = { Text("请输入密码") }, singleLine = true, visualTransformation = PasswordVisualTransformation(), modifier = Modifier.fillMaxWidth() ) // 错误提示 uiState.errorMsg?.let { Text(text = it, color = Color.Red, modifier = Modifier.padding(top = 8.dp)) } Spacer(modifier = Modifier.height(30.dp)) // 登录按钮 Button( onClick = { viewModel.sendIntent(LoginIntent.LoginSubmit) }, modifier = Modifier .fillMaxWidth() .height(50.dp), enabled = !uiState.isLoading ) { if (uiState.isLoading) { CircularProgressIndicator( modifier = Modifier.size(20.dp), color = Color.White ) } else { Text("登录") } } } } ``` ### 6. Activity 入口调用 ```kotlin class LoginActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { LoginTheme { LoginPage(onLoginSuccess = { // 登录成功跳转主页 finish() }) } } } } ``` --- ### 面试口述要点 #### 1. MVI核心 - 单向数据流:**View → Intent → ViewModel → State → View** - 页面只有**唯一UiState**,所有UI变化统一由状态驱动 - 所有用户操作统一封装为Intent,职责清晰 #### 2. MVVM分工 - View:只负责UI展示、分发事件,无业务逻辑 - ViewModel:处理业务、调用仓库、流转状态 - Repository:统一数据源,隔离上层与网络/本地 #### 3. Compose适配点 - 使用 `collectAsStateWithLifecycle` 安全收集流,贴合生命周期 - 网络请求放在viewModelScope,自动取消防泄漏 - 输入框修改发送Intent,不直接修改状态 - 加载、错误、成功全状态统一管理 #### 4. 优点 - 逻辑解耦,易测试 - 状态统一,界面状态可回溯 - 适配 Compose 声明式 UI,完美契合重组机制 --- ## 鸿蒙 HarmonyOS NEXT(API12+/Stage模型)面试题库 适配**应用开发工程师**,分为【基础概念】【Stage模型&Ability】【ArkUI/ArkTS】【状态管理】【线程&存储】【分布式能力】【性能优化】【对比&开放性问题】,附带标准答案,适配 2026 面试(纯血鸿蒙,无 AOSP)。 > 说明:区分 **ArkTS V1(旧装饰器)、ArkTS V2(API14新语法)**,面试大概率两个都会问到。 ### 一、基础概念(初试必问) 1. **HarmonyOS 和 OpenHarmony 的区别?** - OpenHarmony:华为捐赠给开放原子开源基金会的**开源操作系统底座**,不含华为应用服务、HMS。任何人可获取源码、二次开发。 - HarmonyOS(商用手机/平板系统):基于 OpenHarmony,叠加华为私有组件、HMS Core、系统定制、分布式增强,面向消费设备。 - HarmonyOS NEXT:**纯血鸿蒙**,移除 Linux 内核、完全去掉 AOSP 安卓兼容层,只运行原生鸿蒙应用。 2. **ArkTS 和 TypeScript/JS 的区别?为什么主推 ArkTS?** - ArkTS 基于 TS 扩展,**强静态类型**;JS/TS 存在隐式类型转换。 - 方舟编译器做 AOT 预编译,可深度优化,性能高于 JS; - 强制限制动态特性(禁止动态新增对象属性),减少运行时异常; - 原生适配 ArkUI 响应式渲染,和鸿蒙系统能力深度打通; - 适合大型商业项目,便于静态检查、工程维护。 3. **方舟编译器(ArkCompiler)作用?AOT/JIT 区别** 方舟编译器统一编译 ArkTS、C/C++ 代码。 - AOT:预先编译成机器码,安装阶段完成,启动速度快; - JIT:运行时即时编译; 鸿蒙应用优先 AOT,提升冷启动性能。 4. **什么是一次开发,多端部署?** 一套 ArkUI 代码,自适应手机、平板、车机、穿戴设备;通过**响应式布局、媒体查询、窗口适配、条件编译**适配不同设备形态。 ### 二、Stage模型 & UIAbility(重中之重,高频) > HarmonyOS NEXT 只支持 Stage 模型,FA 模型已废弃 1. **Stage 模型和 FA 模型核心区别?** | 维度 | FA 模型 | Stage 模型 | |------|---------|-----------| | 引擎实例 | 每个 Ability 独享 ArkTS 引擎,内存开销大 | 同一进程共享引擎,内存更低 | | 组件 | PageAbility、ServiceAbility | UIAbility(界面)+ ExtensionAbility(卡片、服务、输入法等) | | 窗口管理 | Ability 和页面强耦合 | WindowStage 解耦窗口与业务逻辑 | | 生命周期 | 回调繁杂 | UIAbility 生命周期分层清晰,区分进程、窗口、页面 | | 新特性 | 不再迭代 | 元服务、任务流转、Extension 全部基于 Stage | 2. **UIAbility 完整生命周期(冷启动顺序)** ``` onCreate → onWindowStageCreate → onForeground ``` - 切后台:`onBackground` - 切回前台:`onForeground` - 销毁流程:`onBackground → onWindowStageDestroy → onDestroy` **各个回调最佳实践** - onCreate:进程创建,全局初始化(数据库、网络 SDK);**禁止操作窗口 UI** - onWindowStageCreate:获取 WindowStage,加载页面、设置沉浸式、窗口监听 - onForeground:应用可见,恢复动画、轮询、传感器 - onBackground:不可见,暂停定时任务、保存临时数据 - onDestroy:资源释放;⚠️系统低内存回收时**不会执行 onDestroy** 3. **冷启动 和 热启动(onNewWant 触发时机)** - 冷启动:进程不存在,新建 UIAbility,执行完整生命周期 onCreate - 热启动:进程存在,已有 UIAbility 实例,再次拉起**不会执行 onCreate**,触发 `onNewWant(want)` > 面试坑:页面参数更新必须在 onNewWant 处理路由参数! 4. **Want 是什么?作用?** Want 是组件间通信载体,用于启动 UIAbility、传递参数、跳转页面; 包含:bundleName、abilityName、parameters(传参)、flags。 5. **AbilityStage 是什么?** 模块入口类,整个 HAP 模块进程创建时**唯一实例**,早于所有 UIAbility 创建;适合模块全局初始化。 6. **ExtensionAbility 有哪些常见类型?** FormExtensionAbility(桌面卡片)、ServiceExtensionAbility、InputMethodExtension、WorkSchedulerExtension 等,用于后台服务、小组件。 ### 三、ArkUI 页面 & 组件生命周期 1. **@Entry 页面生命周期(页面级)** - `onPageShow`:页面显示(前台切换、路由返回都会触发) - `onPageHide`:页面隐藏(跳转、切后台) - `onBackPress`:拦截返回按键 2. **@Component 自定义组件生命周期(V1)** `aboutToAppear → build() → onDidBuild → aboutToDisappear` - aboutToAppear:组件构建前,发起网络请求首选位置 - aboutToDisappear:销毁前,**禁止修改@Link/@ObjectLink状态**,容易引发异常;用于取消定时器、解绑监听 3. **ForEach 和 LazyForEach 的区别?** - ForEach:一次性渲染全部子组件,大数据列表卡顿; - LazyForEach:**按需懒加载**,仅渲染可视区域 item,长列表必备; > 要求:数据源实现 IEnumerable,提供索引获取数据。 4. **路由两种方式:Router、Navigation 区别** - Router:页面路由,传统多页面跳转;页面互相独立; - Navigation:单栈路由(推荐),组件内路由,支持嵌套、转场动画、自定义标题栏,适合现代应用。 ### 四、状态管理(V1 / V2 两套必考) #### ArkTS V1(API12 主流) 1. **各个装饰器作用与数据流** - @State:组件内部私有状态 - @Prop:父→子单向传递,子本地修改不回传给父 - @Link:父子双向绑定,子修改同步父 - @ObjectLink:对象/数组双向绑定(解决@Link无法监听对象内部属性) - @Provide/@Consume:跨多层组件传值,不用逐层透传 - @StorageLink:持久化到本地 Preferences,应用重启保留 2. **AppStorage vs LocalStorage** - AppStorage:**应用全局单例**,所有页面/组件共享;内存存储,进程销毁丢失 - LocalStorage:页面/Ability 局部共享;多个实例隔离,适合模块内状态 #### ArkTS V2(API14 HarmonyOS NEXT 新版,现在面试热点) V2 废弃旧装饰器@State/@Link,全新体系: - @Local:组件内部私有状态(替代@State) - @Param:父向子单向传参(替代@Prop) - @Event:子向父回调,实现双向同步(替代@Link,不再提供内置双向绑定) - @ObservedV2 + @Trace:类对象监听,替代 ObjectLink - @Computed:计算属性,依赖变更自动刷新 面试官常问:**V2 相比 V1 改进了什么?** 消除隐式双向绑定带来的数据流混乱,强制单向数据流,更容易追踪状态变更,大型项目维护性更强。 ### 五、多线程、存储 1. **鸿蒙线程模型** - 主线程(UI 线程):只能 UI 操作,禁止耗时逻辑 - TaskPool:系统全局线程池,**推荐**执行网络、文件、解析;自动复用线程,代替手动 Worker - Worker:独立 JS 虚拟机线程,适合重度计算;创建销毁开销大,慎用 > 面试坑:TaskPool 不能直接操作 UI,需要发消息通知主线程修改状态。 2. **三种本地存储选型** 1. Preferences(首选项):轻量键值对,适合配置;全部加载进内存,**不适合大量数据** 2. RDB 关系型数据库:封装 SQLite,结构化数据、分页、事务,业务数据首选 3. 文件存储:图片、缓存、二进制资源 3. **如何避免主线程阻塞?** 网络请求、大文件读写、JSON 解析、图片解码全部放入 TaskPool;大列表使用 LazyForEach;图片异步加载。 ### 六、分布式能力(鸿蒙特色,必考题) 1. **鸿蒙分布式四大核心底座** ① **分布式软总线**:跨设备通信基座,自动设备发现、屏蔽蓝牙/WiFi/星闪差异,低时延组网; ② 分布式设备虚拟化:硬件共享(摄像头、屏幕、麦克风); ③ 分布式数据管理:跨设备数据同步; ④ 分布式任务调度:应用跨设备**任务流转**(接续打开页面)。 2. **什么是任务流转(跨设备迁移 UIAbility)** 用户在手机使用应用,可以无缝流转到平板继续运行,页面状态保留。底层依靠软总线+分布式任务调度,通过 want 实现。 3. **分布式数据管理 DDM 优势** 多设备数据视图一致,支持增量同步、冲突处理;不需要开发者自己实现网络同步。 ### 七、性能优化 & 内存问题(中高级) 1. **应用启动优化手段** - onCreate 只保留必要初始化,重任务延迟到 onForeground; - 大图延迟加载;减少首页面组件数量; - 合理分包,按需加载资源;避免同步阻塞操作。 2. **列表卡顿常见原因与优化** - 未使用 LazyForEach; - Item 布局层级太深、嵌套过多; - Item 内频繁创建闭包、复杂计算; - 图片尺寸过大、未做缓存; - 状态范围过大,滚动时整列表刷新。 3. **内存泄漏常见场景** - 定时器、事件监听、传感器订阅在 aboutToDisappear 没有取消; - 闭包长期持有组件实例; - 全局静态变量持有页面组件; - 图片资源没有及时释放。 4. **怎么监控性能?** DevEco Studio Profiler:CPU、内存、帧率、渲染耗时、启动耗时。 ### 八、开放性面试问答(手撕&项目问答) 1. **从 Android 转鸿蒙,最大思维转变是什么?** - 从命令式控件操作 → **声明式 UI、状态驱动视图**; - Activity 页面模型 → UIAbility+组件化,页面粒度更细; - 重视分布式、多端自适应; - 区分主线程,强化响应式状态管理,避免直接操作 UI。 2. **路由传参有几种方案?优缺点?** - want.parameters(Ability 跳转); - RouterInfo 参数 / Navigation 路由参数; - AppStorage 临时存储; ⚠️ 注意:复杂大对象不建议路由传参,优先全局缓存。 3. **页面返回如何携带数据回传上一页?** Router.back({参数}) + 上一页 onPageShow 监听;或使用事件总线/AppStorage 通信。 ### 九、手写代码常考题 1. LazyForEach 简易实现列表 2. TaskPool 异步请求封装 3. UIAbility 基础模板 4. Navigation 页面路由跳转 5. Preferences 持久化封装 --- ## 鸿蒙 HarmonyOS NEXT 面试题库(精简背诵版 + 情景题 + V2 专项) ### 一、精简背诵版(一页速背八股) #### 1. 基础概念 1. **OpenHarmony**:开源底座,开放原子基金会,无 HMS;**HarmonyOS** 商用版叠加华为服务;**HarmonyOS NEXT** 纯血鸿蒙,无安卓 AOSP、Linux 兼容层。 2. **ArkTS**:TS 超集,强静态类型,方舟 AOT 预编译,禁止动态属性,原生适配 ArkUI,大型项目更稳定。 3. **方舟编译器**:统一编译 C/C++/ArkTS;AOT 安装期编译机器码,启动更快;JIT 运行时编译,NEXT 优先 AOT。 4. **一次开发多端**:ArkUI 响应式布局、媒体查询、条件编译适配手机/车机/穿戴。 #### 2. Stage 模型(NEXT 唯一支持) 1. FA 废弃,Stage 核心:UIAbility 界面、ExtensionAbility 卡片/后台服务;多 Ability 共享引擎,内存更低。 2. **UIAbility 生命周期** - 冷启动:`onCreate`(进程初始化,禁止 UI)→ `onWindowStageCreate`(加载页面、窗口)→ `onForeground`(可见) - 切后台:`onBackground`;切前台 `onForeground`;销毁:`onBackground → onWindowStageDestroy → onDestroy` 3. **onNewWant**:复用已有 Ability 实例热启动,无 onCreate,路由参数更新必须在此处理。 4. **Want**:跨组件通信载体,存 bundleName、abilityName、parameters 传参。 5. **AbilityStage**:HAP 模块全局单例,进程最先创建,模块统一初始化。 6. **ExtensionAbility 常用**:FormExtension 桌面卡片、ServiceExtension 后台服务。 #### 3. ArkUI 页面 & 组件 1. **@Entry 页面生命周期**:`onPageShow`、`onPageHide`、`onBackPress` 拦截返回。 2. **@Component(V1)**:`aboutToAppear`(请求接口)→ `build` → `onDidBuild` → `aboutToDisappear`(销毁定时器/监听)。 3. **ForEach** 全量渲染;**LazyForEach** 懒加载可视区域,长列表必备,需实现 IEnumerable 数据源。 4. **Router** 多页面路由;**Navigation** 单栈嵌套路由,支持动画、自定义标题,官方推荐。 #### 4. 状态管理 V1 / V2 ##### V1(API12 主流) - @State:组件内私有状态 - @Prop:父→子单向,子修改不回传 - @Link:父子双向绑定 - @ObjectLink:对象/数组深度监听 - @Provide/@Consume:跨多层传参,无需逐层透传 - @StorageLink:持久化 Preferences - AppStorage 全局共享;LocalStorage 模块局部隔离 ##### V2(API14 新规范,面试热点) 废弃双向绑定装饰器,强制单向数据流 - @Local 替代 @State;@Param 替代 @Prop;@Event 子回调通信 - @ObservedV2 + @Trace 监听对象;@Computed 计算属性 - **优势**:数据流清晰,大型项目易维护,减少状态异常。 #### 5. 线程与存储 1. **主线程**仅允许 UI 操作; - **TaskPool**:系统线程池,推荐网络/文件解析,轻量复用; - **Worker**:独立虚拟机,重度计算,开销大慎用; - 子线程不可直接更新 UI。 2. **存储三选一** - **Preferences**:轻量键值配置,全量加载,不适合大数据; - **RDB**:SQLite 封装,结构化数据、事务分页,业务首选; - **文件**:图片、二进制缓存。 3. **主线程阻塞优化**:耗时逻辑丢 TaskPool、列表懒加载、图片异步。 #### 6. 分布式(鸿蒙特色) 1. **四大底座**:分布式软总线(跨设备通信,屏蔽蓝牙/WiFi)、设备虚拟化、分布式数据管理 DDM、分布式任务调度(任务流转)。 2. **任务流转**:跨设备无缝接续应用,页面状态保留。 3. **DDM**:多设备数据自动同步,增量更新、冲突处理。 #### 7. 性能优化 & 内存泄漏 1. **启动优化**:onCreate 只做必要初始化,延迟耗时任务;分包、大图懒加载。 2. **列表卡顿**:未用 LazyForEach、布局层级深、滚动频繁刷新、大图未缓存。 3. **内存泄漏**:定时器/监听未取消、全局静态持有页面、图片资源未释放。 4. **性能工具**:DevEco Profiler 查看 CPU/内存/帧率。 #### 8. 高频问答 1. **Android 转鸿蒙**:命令式控件 → 声明式状态驱动 UI;Activity → UIAbility;重视多端、分布式。 2. **路由传参**:want 参数、Navigation/Router 参数、AppStorage;大对象禁止路由传递。 3. **返回传参**:Router.back 携带参数,onPageShow 监听接收。 --- ### 二、10 道情景面试题(项目深挖) #### 1. 项目中做桌面卡片 FormExtension 遇到什么坑?如何解决? **答**:卡片进程独立,无法直接共享页面状态;需用 Preferences/DDM 跨进程同步数据;卡片尺寸有限,布局精简;卡片无页面生命周期,定时刷新使用 FormAgent,避免频繁更新耗电。 #### 2. 长列表滚动卡顿,完整排查优化流程? **答**:①替换 ForEach 为 LazyForEach;②简化 Item 布局,减少嵌套组件;③图片使用异步缓存,限制图片尺寸;④缩小状态刷新范围,避免全局状态变更整列表重绘;⑤列表内移除实时复杂计算、频繁创建闭包。 #### 3. UIAbility 切换前后台,定时器错乱如何处理? **答**:onForeground 启动轮询定时器;onBackground 销毁清除定时器;全局定时器存于 AbilityStage 统一管理,页面销毁强制清空,防止内存泄漏与重复请求。 #### 4. 跨多层组件需要传递用户信息,用 Provide/Consume 还是全局 AppStorage?怎么选? **答**:临时页面局部状态用 Provide/Consume,隔离不污染全局;登录态、用户信息全局通用数据用 AppStorage;大量持久化用户配置搭配 @StorageLink。 #### 5. 子组件修改对象内部属性,父页面不刷新,V1 如何解决? **答**:@Link 仅监听变量地址变更,对象内部属性修改无法触发刷新,改用 @ObjectLink 深度监听对象;或拷贝全新对象覆盖原状态触发更新。 #### 6. TaskPool 子线程拿到接口数据,怎么更新页面 UI? **答**:子线程不可直接操作状态;请求成功后通过 AppStorage、全局事件总线、回调通知主线程修改 @State 状态,驱动 UI 刷新。 #### 7. 应用冷启动慢,从代码层面给出优化方案? **答**:1. onCreate 只初始化 SDK 基础实例,网络、数据库连接延迟到 onForeground;2. 首页拆分组件,非首屏组件延迟加载;3. 大图、矢量图压缩懒加载;4. 同步文件 IO 全部放入 TaskPool 异步;5. 分包加载非核心资源。 #### 8. 多个设备同步用户数据,不用后端接口如何实现? **答**:使用分布式数据管理 DDM,创建分布式数据库,设备登录同一华为账号自动增量同步,内置冲突策略,无需手动处理跨设备网络通信。 #### 9. 页面返回拦截,同时携带参数回传给上一页,实现思路? **答**:重写 onBackPress 拦截返回逻辑,Router.back({key:value}) 携带参数;上一页 onPageShow 生命周期读取路由参数,更新页面数据。 #### 10. 内存泄漏如何定位与修复? **答**:Profiler 内存快照查看页面实例持续存活;常见泄漏点:定时器未 clear、传感器/网络监听未解绑、静态变量持有组件实例、大图未释放;修复:aboutToDisappear 统一销毁所有订阅与定时任务,不使用全局变量缓存页面组件。 --- ### 三、ArkTS V2 专项面试题(API14 HarmonyOS NEXT 新版) #### 基础问答 ##### 1. ArkTS V2 相比 V1 最大改动是什么? **答**:移除 @State/@Link/@ObjectLink 等双向绑定装饰器,强制单向数据流;拆分状态、传参、事件逻辑,代码逻辑更清晰,大型项目可维护性大幅提升,减少隐式刷新带来的未知 bug。 ##### 2. @Local、@Param、@Event 各自作用? - **@Local**:组件内部私有状态,替代 V1 @State,仅当前组件可用; - **@Param**:父组件单向传入数据,只读,替代 @Prop;子组件无法直接修改; - **@Event**:自定义回调事件,子组件触发事件传值给父,替代双向绑定实现父子数据同步。 ##### 3. V2 如何监听对象、数组内部属性变化? **答**:搭配 @ObservedV2 标记实体类,属性添加 @Trace 装饰;修改类内部字段自动触发 UI 刷新,替代 V1 @ObjectLink。 ##### 4. @Computed 计算属性使用场景? **答**:依赖多个基础状态派生数据(总价、过滤列表),依赖值变更自动重新计算,无需手动监听,缓存结果减少重复计算。 ##### 5. V2 跨组件传值方案? - 多层透传:@Param 逐层传递; - 跨层级无透传:全局 AppStorage、LocalStorage; - 全局实体对象:@ObservedV2 全局单例类。 #### 实操问题 ##### 1. V2 如何实现父子双向数据同步? V1 靠 @Link 内置双向绑定,V2 无该能力;父定义 @Local 状态,通过 @Param 传给子,子触发 @Event 回调把修改后的值传回父,父更新自身状态完成同步。 ##### 2. V2 为什么取消内置双向绑定? 隐式双向绑定会造成数据流混乱,多层组件修改状态难以追溯数据来源;V2 单向数据流,数据变更路径清晰,便于调试、单元测试。 ##### 3. V2 LocalStorage/AppStorage 还能用吗? 完全兼容,搭配 @StorageLink 持久化逻辑不变,仅组件本地状态装饰器更新。 #### 手写代码考点 1. @ObservedV2 + @Trace 实体类 2. @Local + @Param + @Event 父子组件通信完整示例 --- ## 鸿蒙 HarmonyOS NEXT 面试题库(续) ### 四、状态管理 V2(API14 新版,面试热点) 核心:取消隐式双向绑定,强制单向数据流 1. **@Local**:替代 @State,组件本地状态 2. **@Param**:替代 @Prop,父单向传参(只读) 3. **@Event**:子回调,实现父子数据同步 4. **@ObservedV2 + @Trace**:监听对象内部属性 5. **@Computed**:计算属性,自动缓存派生值 **优化点**:数据流可追溯,大型项目维护性更强,减少未知 UI 刷新 bug。 --- ### 五、线程模型 & 本地存储 #### 1. 线程分类 1. **主线程(UI 线程)**:仅允许 UI 渲染、状态修改;禁止耗时操作 2. **TaskPool**:系统全局线程池,推荐网络、文件解析,轻量高效 3. **Worker**:独立虚拟机线程,重度计算使用,创建销毁开销大 > 注意:子线程不能直接更新 UI,需通知主线程修改状态。 #### 2. 三种存储方案 1. **Preferences**:轻量键值配置,全量加载进内存,不适合海量数据 2. **RDB(SQLite 封装)**:结构化业务数据,支持事务、分页查询 3. **文件存储**:图片、二进制缓存资源 --- ### 六、分布式能力(鸿蒙特色) #### 1. 四大核心底座 - **分布式软总线**:跨设备通信基座,自动发现设备,屏蔽蓝牙/WiFi/星闪底层差异 - **分布式设备虚拟化**:跨设备共享摄像头、屏幕、麦克风 - **分布式数据管理 DDM**:多设备自动增量同步数据,内置冲突处理 - **分布式任务调度**:任务流转,跨设备无缝接续应用 #### 2. 任务流转 手机应用迁移至平板,页面状态完整保留。 --- ### 七、性能优化 & 内存泄漏 #### 1. 启动优化 onCreate 仅保留核心初始化,耗时任务延迟至 onForeground;首页组件懒加载、资源分包、大图异步加载。 #### 2. 列表卡顿优化 使用 LazyForEach;简化 Item 布局层级;图片缓存压缩;缩小状态刷新范围,避免全局重绘。 #### 3. 内存泄漏场景 定时器、传感器/网络监听未解绑;全局静态变量持有页面组件;大图资源未释放;页面销毁未清空订阅。 #### 4. 性能工具 DevEco Studio Profiler:监控 CPU、内存、帧率、渲染耗时、启动耗时。 --- ### 八、高频项目情景面试题(10 道) 1. 桌面卡片 FormExtension 踩坑与解决方案 2. 长列表滚动卡顿完整排查优化流程 3. 前后台切换定时器错乱处理方案 4. Provide/Consume 与 AppStorage 选用场景 5. V1 对象内部属性修改页面不刷新解决办法 6. TaskPool 子线程接口数据更新 UI 实现思路 7. 应用冷启动慢完整优化方案 8. 无后端多设备数据同步实现(DDM) 9. 拦截返回键并携带参数回传上一页 10. 内存泄漏定位、排查、修复流程 --- ### 九、ArkTS V2 专项面试题 1. V2 对比 V1 核心改动 2. @Local/@Param/@Event 作用与通信方式 3. 对象深度监听 @ObservedV2+@Trace 使用 4. @Computed 计算属性场景 5. V2 如何实现父子双向同步 6. 取消内置双向绑定的设计目的 --- ### 十、手写代码高频考点 1. LazyForEach 长列表实现 2. TaskPool 网络请求封装 3. UIAbility 标准生命周期模板 4. Navigation 嵌套路由跳转 5. Preferences 持久化工具类 6. V2 @ObservedV2 实体类 + 父子组件通信 --- ## 相关资源 - [Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html) - [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/) - [Kotlin/Wasm](https://kotl.in/wasm/) - [HarmonyOS Developer](https://developer.harmonyos.com/)