code-abyss 1.6.16 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +8 -6
  2. package/bin/install.js +59 -163
  3. package/bin/lib/ccline.js +82 -0
  4. package/bin/lib/utils.js +61 -0
  5. package/package.json +5 -2
  6. package/skills/SKILL.md +24 -16
  7. package/skills/domains/ai/SKILL.md +2 -2
  8. package/skills/domains/ai/prompt-and-eval.md +279 -0
  9. package/skills/domains/architecture/SKILL.md +2 -3
  10. package/skills/domains/architecture/security-arch.md +87 -0
  11. package/skills/domains/data-engineering/SKILL.md +188 -26
  12. package/skills/domains/development/SKILL.md +1 -4
  13. package/skills/domains/devops/SKILL.md +3 -5
  14. package/skills/domains/devops/performance.md +63 -0
  15. package/skills/domains/devops/testing.md +97 -0
  16. package/skills/domains/frontend-design/SKILL.md +12 -3
  17. package/skills/domains/frontend-design/claymorphism/SKILL.md +117 -0
  18. package/skills/domains/frontend-design/claymorphism/references/tokens.css +52 -0
  19. package/skills/domains/frontend-design/engineering.md +287 -0
  20. package/skills/domains/frontend-design/glassmorphism/SKILL.md +138 -0
  21. package/skills/domains/frontend-design/glassmorphism/references/tokens.css +32 -0
  22. package/skills/domains/frontend-design/liquid-glass/SKILL.md +135 -0
  23. package/skills/domains/frontend-design/liquid-glass/references/tokens.css +81 -0
  24. package/skills/domains/frontend-design/neubrutalism/SKILL.md +141 -0
  25. package/skills/domains/frontend-design/neubrutalism/references/tokens.css +44 -0
  26. package/skills/domains/infrastructure/SKILL.md +174 -34
  27. package/skills/domains/mobile/SKILL.md +211 -21
  28. package/skills/domains/orchestration/SKILL.md +1 -0
  29. package/skills/domains/security/SKILL.md +4 -6
  30. package/skills/domains/security/blue-team.md +57 -0
  31. package/skills/domains/security/red-team.md +54 -0
  32. package/skills/domains/security/threat-intel.md +50 -0
  33. package/skills/orchestration/multi-agent/SKILL.md +195 -46
  34. package/skills/run_skill.js +139 -0
  35. package/skills/tools/gen-docs/SKILL.md +6 -4
  36. package/skills/tools/gen-docs/scripts/doc_generator.js +363 -0
  37. package/skills/tools/lib/shared.js +98 -0
  38. package/skills/tools/verify-change/SKILL.md +8 -6
  39. package/skills/tools/verify-change/scripts/change_analyzer.js +289 -0
  40. package/skills/tools/verify-module/SKILL.md +6 -4
  41. package/skills/tools/verify-module/scripts/module_scanner.js +171 -0
  42. package/skills/tools/verify-quality/SKILL.md +5 -3
  43. package/skills/tools/verify-quality/scripts/quality_checker.js +337 -0
  44. package/skills/tools/verify-security/SKILL.md +7 -5
  45. package/skills/tools/verify-security/scripts/security_scanner.js +283 -0
  46. package/skills/__pycache__/run_skill.cpython-312.pyc +0 -0
  47. package/skills/domains/COVERAGE_PLAN.md +0 -232
  48. package/skills/domains/ai/model-evaluation.md +0 -790
  49. package/skills/domains/ai/prompt-engineering.md +0 -703
  50. package/skills/domains/architecture/compliance.md +0 -299
  51. package/skills/domains/architecture/data-security.md +0 -184
  52. package/skills/domains/data-engineering/data-pipeline.md +0 -762
  53. package/skills/domains/data-engineering/data-quality.md +0 -894
  54. package/skills/domains/data-engineering/stream-processing.md +0 -791
  55. package/skills/domains/development/dart.md +0 -963
  56. package/skills/domains/development/kotlin.md +0 -834
  57. package/skills/domains/development/php.md +0 -659
  58. package/skills/domains/development/swift.md +0 -755
  59. package/skills/domains/devops/e2e-testing.md +0 -914
  60. package/skills/domains/devops/performance-testing.md +0 -734
  61. package/skills/domains/devops/testing-strategy.md +0 -667
  62. package/skills/domains/frontend-design/build-tools.md +0 -743
  63. package/skills/domains/frontend-design/performance.md +0 -734
  64. package/skills/domains/frontend-design/testing.md +0 -699
  65. package/skills/domains/infrastructure/gitops.md +0 -735
  66. package/skills/domains/infrastructure/iac.md +0 -855
  67. package/skills/domains/infrastructure/kubernetes.md +0 -1018
  68. package/skills/domains/mobile/android-dev.md +0 -979
  69. package/skills/domains/mobile/cross-platform.md +0 -795
  70. package/skills/domains/mobile/ios-dev.md +0 -931
  71. package/skills/domains/security/secrets-management.md +0 -834
  72. package/skills/domains/security/supply-chain.md +0 -931
  73. package/skills/domains/security/threat-modeling.md +0 -828
  74. package/skills/run_skill.py +0 -153
  75. package/skills/tests/README.md +0 -225
  76. package/skills/tests/SUMMARY.md +0 -362
  77. package/skills/tests/__init__.py +0 -3
  78. package/skills/tests/__pycache__/test_change_analyzer.cpython-312.pyc +0 -0
  79. package/skills/tests/__pycache__/test_doc_generator.cpython-312.pyc +0 -0
  80. package/skills/tests/__pycache__/test_module_scanner.cpython-312.pyc +0 -0
  81. package/skills/tests/__pycache__/test_quality_checker.cpython-312.pyc +0 -0
  82. package/skills/tests/__pycache__/test_security_scanner.cpython-312.pyc +0 -0
  83. package/skills/tests/test_change_analyzer.py +0 -558
  84. package/skills/tests/test_doc_generator.py +0 -538
  85. package/skills/tests/test_module_scanner.py +0 -376
  86. package/skills/tests/test_quality_checker.py +0 -516
  87. package/skills/tests/test_security_scanner.py +0 -426
  88. package/skills/tools/gen-docs/scripts/__pycache__/doc_generator.cpython-312.pyc +0 -0
  89. package/skills/tools/gen-docs/scripts/doc_generator.py +0 -520
  90. package/skills/tools/verify-change/scripts/__pycache__/change_analyzer.cpython-312.pyc +0 -0
  91. package/skills/tools/verify-change/scripts/change_analyzer.py +0 -529
  92. package/skills/tools/verify-module/scripts/__pycache__/module_scanner.cpython-312.pyc +0 -0
  93. package/skills/tools/verify-module/scripts/module_scanner.py +0 -321
  94. package/skills/tools/verify-quality/scripts/__pycache__/quality_checker.cpython-312.pyc +0 -0
  95. package/skills/tools/verify-quality/scripts/quality_checker.py +0 -481
  96. package/skills/tools/verify-security/scripts/__pycache__/security_scanner.cpython-312.pyc +0 -0
  97. package/skills/tools/verify-security/scripts/security_scanner.py +0 -374
@@ -1,834 +0,0 @@
1
- ---
2
- name: kotlin
3
- description: Kotlin 开发技术。Jetpack Compose、Coroutines、Flow、Android 开发、协程并发。当用户提到 Kotlin、Jetpack Compose、Coroutines、Flow、Android 开发时使用。
4
- ---
5
-
6
- # 🤖 Kotlin 开发 · Kotlin Development
7
-
8
- ## 生态架构
9
-
10
- ```
11
- Kotlin Coroutines
12
-
13
- ┌─────────┼─────────┐
14
- │ │ │
15
- Flow Channel StateFlow
16
- │ │ │
17
- └─────────┼─────────┘
18
-
19
- Jetpack Compose
20
-
21
- ┌─────────┼─────────┐
22
- ViewModel Room Retrofit
23
- ```
24
-
25
- ## Kotlin 语言特性
26
-
27
- ### 空安全
28
- ```kotlin
29
- // 可空类型
30
- var name: String? = null
31
- val length = name?.length ?: 0 // Elvis 操作符
32
-
33
- // 安全调用链
34
- val city = user?.address?.city
35
-
36
- // 非空断言 (谨慎使用)
37
- val length = name!!.length
38
-
39
- // let 作用域函数
40
- name?.let {
41
- println("Name is $it")
42
- }
43
-
44
- // 智能转换
45
- fun process(value: Any) {
46
- if (value is String) {
47
- println(value.length) // 自动转换为 String
48
- }
49
- }
50
- ```
51
-
52
- ### 数据类与密封类
53
- ```kotlin
54
- // 数据类
55
- data class User(
56
- val id: String,
57
- val name: String,
58
- val email: String
59
- ) {
60
- fun isAdmin() = email.endsWith("@admin.com")
61
- }
62
-
63
- val user = User("1", "John", "john@example.com")
64
- val updated = user.copy(name = "Jane")
65
-
66
- // 密封类 (类型安全的枚举)
67
- sealed class Result<out T> {
68
- data class Success<T>(val data: T) : Result<T>()
69
- data class Error(val message: String) : Result<Nothing>()
70
- object Loading : Result<Nothing>()
71
- }
72
-
73
- fun handleResult(result: Result<User>) {
74
- when (result) {
75
- is Result.Success -> println(result.data)
76
- is Result.Error -> println(result.message)
77
- Result.Loading -> println("Loading...")
78
- }
79
- }
80
- ```
81
-
82
- ### 扩展函数
83
- ```kotlin
84
- // 为现有类添加方法
85
- fun String.isEmail(): Boolean {
86
- return this.contains("@") && this.contains(".")
87
- }
88
-
89
- fun List<Int>.average(): Double {
90
- return if (isEmpty()) 0.0 else sum().toDouble() / size
91
- }
92
-
93
- // 使用
94
- val email = "test@example.com"
95
- if (email.isEmail()) {
96
- println("Valid email")
97
- }
98
-
99
- val numbers = listOf(1, 2, 3, 4, 5)
100
- println(numbers.average()) // 3.0
101
- ```
102
-
103
- ### 高阶函数与 Lambda
104
- ```kotlin
105
- // 高阶函数
106
- fun <T> List<T>.customFilter(predicate: (T) -> Boolean): List<T> {
107
- val result = mutableListOf<T>()
108
- for (item in this) {
109
- if (predicate(item)) {
110
- result.add(item)
111
- }
112
- }
113
- return result
114
- }
115
-
116
- // 使用
117
- val numbers = listOf(1, 2, 3, 4, 5)
118
- val evens = numbers.customFilter { it % 2 == 0 }
119
-
120
- // 函数类型参数
121
- fun performOperation(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
122
- return operation(x, y)
123
- }
124
-
125
- val sum = performOperation(5, 3) { a, b -> a + b }
126
- val product = performOperation(5, 3) { a, b -> a * b }
127
- ```
128
-
129
- ## Jetpack Compose
130
-
131
- ### 组合函数
132
- ```kotlin
133
- import androidx.compose.runtime.*
134
- import androidx.compose.material3.*
135
- import androidx.compose.foundation.layout.*
136
-
137
- @Composable
138
- fun UserProfile(user: User) {
139
- var isExpanded by remember { mutableStateOf(false) }
140
-
141
- Card(
142
- modifier = Modifier
143
- .fillMaxWidth()
144
- .padding(16.dp)
145
- .clickable { isExpanded = !isExpanded }
146
- ) {
147
- Column(modifier = Modifier.padding(16.dp)) {
148
- Text(
149
- text = user.name,
150
- style = MaterialTheme.typography.headlineMedium
151
- )
152
-
153
- Text(
154
- text = user.email,
155
- style = MaterialTheme.typography.bodyMedium,
156
- color = MaterialTheme.colorScheme.secondary
157
- )
158
-
159
- AnimatedVisibility(visible = isExpanded) {
160
- Text(
161
- text = user.bio,
162
- modifier = Modifier.padding(top = 8.dp)
163
- )
164
- }
165
- }
166
- }
167
- }
168
- ```
169
-
170
- ### 状态管理
171
- ```kotlin
172
- // remember - 组合内状态
173
- @Composable
174
- fun Counter() {
175
- var count by remember { mutableStateOf(0) }
176
-
177
- Button(onClick = { count++ }) {
178
- Text("Count: $count")
179
- }
180
- }
181
-
182
- // rememberSaveable - 配置变更后保留
183
- @Composable
184
- fun SaveableCounter() {
185
- var count by rememberSaveable { mutableStateOf(0) }
186
-
187
- Button(onClick = { count++ }) {
188
- Text("Count: $count")
189
- }
190
- }
191
-
192
- // ViewModel 状态
193
- class UserViewModel : ViewModel() {
194
- private val _users = MutableStateFlow<List<User>>(emptyList())
195
- val users: StateFlow<List<User>> = _users.asStateFlow()
196
-
197
- fun loadUsers() {
198
- viewModelScope.launch {
199
- _users.value = repository.getUsers()
200
- }
201
- }
202
- }
203
-
204
- @Composable
205
- fun UserList(viewModel: UserViewModel = viewModel()) {
206
- val users by viewModel.users.collectAsState()
207
-
208
- LaunchedEffect(Unit) {
209
- viewModel.loadUsers()
210
- }
211
-
212
- LazyColumn {
213
- items(users) { user ->
214
- UserItem(user)
215
- }
216
- }
217
- }
218
- ```
219
-
220
- ### 列表与导航
221
- ```kotlin
222
- @Composable
223
- fun ItemList(items: List<Item>, onItemClick: (Item) -> Unit) {
224
- LazyColumn {
225
- items(items, key = { it.id }) { item ->
226
- ItemRow(
227
- item = item,
228
- onClick = { onItemClick(item) }
229
- )
230
- }
231
- }
232
- }
233
-
234
- @Composable
235
- fun ItemRow(item: Item, onClick: () -> Unit) {
236
- Row(
237
- modifier = Modifier
238
- .fillMaxWidth()
239
- .clickable(onClick = onClick)
240
- .padding(16.dp),
241
- horizontalArrangement = Arrangement.SpaceBetween
242
- ) {
243
- Column {
244
- Text(
245
- text = item.name,
246
- style = MaterialTheme.typography.titleMedium
247
- )
248
- Text(
249
- text = item.description,
250
- style = MaterialTheme.typography.bodySmall
251
- )
252
- }
253
-
254
- Icon(
255
- imageVector = Icons.Default.ChevronRight,
256
- contentDescription = null
257
- )
258
- }
259
- }
260
-
261
- // Navigation
262
- @Composable
263
- fun AppNavigation() {
264
- val navController = rememberNavController()
265
-
266
- NavHost(navController, startDestination = "home") {
267
- composable("home") {
268
- HomeScreen(
269
- onNavigateToDetail = { id ->
270
- navController.navigate("detail/$id")
271
- }
272
- )
273
- }
274
- composable(
275
- route = "detail/{id}",
276
- arguments = listOf(navArgument("id") { type = NavType.StringType })
277
- ) { backStackEntry ->
278
- val id = backStackEntry.arguments?.getString("id")
279
- DetailScreen(id = id)
280
- }
281
- }
282
- }
283
- ```
284
-
285
- ### 副作用处理
286
- ```kotlin
287
- @Composable
288
- fun EffectsExample() {
289
- // LaunchedEffect - 启动协程
290
- LaunchedEffect(key1 = Unit) {
291
- // 组合进入时执行一次
292
- loadData()
293
- }
294
-
295
- // DisposableEffect - 清理资源
296
- DisposableEffect(Unit) {
297
- val listener = setupListener()
298
- onDispose {
299
- listener.remove()
300
- }
301
- }
302
-
303
- // SideEffect - 发布状态到非 Compose 代码
304
- SideEffect {
305
- analytics.trackScreenView("Home")
306
- }
307
-
308
- // derivedStateOf - 派生状态
309
- val items = remember { mutableStateListOf<Item>() }
310
- val hasItems by remember {
311
- derivedStateOf { items.isNotEmpty() }
312
- }
313
- }
314
- ```
315
-
316
- ## Kotlin Coroutines
317
-
318
- ### 基础协程
319
- ```kotlin
320
- import kotlinx.coroutines.*
321
-
322
- // 启动协程
323
- fun main() = runBlocking {
324
- launch {
325
- delay(1000L)
326
- println("World!")
327
- }
328
- println("Hello,")
329
- }
330
-
331
- // async/await 并发
332
- suspend fun fetchUserData(userId: String): UserData = coroutineScope {
333
- val userDeferred = async { fetchUser(userId) }
334
- val postsDeferred = async { fetchPosts(userId) }
335
- val friendsDeferred = async { fetchFriends(userId) }
336
-
337
- UserData(
338
- user = userDeferred.await(),
339
- posts = postsDeferred.await(),
340
- friends = friendsDeferred.await()
341
- )
342
- }
343
-
344
- // 超时控制
345
- suspend fun fetchWithTimeout() {
346
- try {
347
- withTimeout(5000L) {
348
- fetchData()
349
- }
350
- } catch (e: TimeoutCancellationException) {
351
- println("Request timed out")
352
- }
353
- }
354
- ```
355
-
356
- ### 协程作用域
357
- ```kotlin
358
- class MyViewModel : ViewModel() {
359
- // ViewModel 作用域
360
- fun loadData() {
361
- viewModelScope.launch {
362
- try {
363
- val data = repository.fetchData()
364
- _state.value = State.Success(data)
365
- } catch (e: Exception) {
366
- _state.value = State.Error(e.message)
367
- }
368
- }
369
- }
370
- }
371
-
372
- class MyActivity : AppCompatActivity() {
373
- // 生命周期作用域
374
- override fun onCreate(savedInstanceState: Bundle?) {
375
- super.onCreate(savedInstanceState)
376
-
377
- lifecycleScope.launch {
378
- repeatOnLifecycle(Lifecycle.State.STARTED) {
379
- viewModel.uiState.collect { state ->
380
- updateUI(state)
381
- }
382
- }
383
- }
384
- }
385
- }
386
-
387
- // 自定义作用域
388
- class DataRepository {
389
- private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
390
-
391
- fun fetchData() {
392
- scope.launch {
393
- // 后台任务
394
- }
395
- }
396
-
397
- fun cleanup() {
398
- scope.cancel()
399
- }
400
- }
401
- ```
402
-
403
- ### 调度器
404
- ```kotlin
405
- // Dispatchers.Main - UI 线程
406
- lifecycleScope.launch(Dispatchers.Main) {
407
- updateUI()
408
- }
409
-
410
- // Dispatchers.IO - IO 操作
411
- withContext(Dispatchers.IO) {
412
- val data = database.query()
413
- }
414
-
415
- // Dispatchers.Default - CPU 密集型
416
- withContext(Dispatchers.Default) {
417
- val result = complexCalculation()
418
- }
419
-
420
- // 切换调度器
421
- suspend fun loadData() {
422
- val data = withContext(Dispatchers.IO) {
423
- fetchFromNetwork()
424
- }
425
- withContext(Dispatchers.Main) {
426
- displayData(data)
427
- }
428
- }
429
- ```
430
-
431
- ## Flow 流式编程
432
-
433
- ### Flow 基础
434
- ```kotlin
435
- import kotlinx.coroutines.flow.*
436
-
437
- // 创建 Flow
438
- fun simpleFlow(): Flow<Int> = flow {
439
- for (i in 1..3) {
440
- delay(100)
441
- emit(i)
442
- }
443
- }
444
-
445
- // 收集 Flow
446
- suspend fun collectFlow() {
447
- simpleFlow().collect { value ->
448
- println(value)
449
- }
450
- }
451
-
452
- // Flow 操作符
453
- fun transformFlow(): Flow<String> = flow {
454
- emit(1)
455
- emit(2)
456
- emit(3)
457
- }.map { value ->
458
- "Number: $value"
459
- }.filter { text ->
460
- text.contains("2")
461
- }
462
- ```
463
-
464
- ### StateFlow 与 SharedFlow
465
- ```kotlin
466
- class UserRepository {
467
- // StateFlow - 状态流 (有初始值)
468
- private val _users = MutableStateFlow<List<User>>(emptyList())
469
- val users: StateFlow<List<User>> = _users.asStateFlow()
470
-
471
- // SharedFlow - 事件流 (无初始值)
472
- private val _events = MutableSharedFlow<Event>()
473
- val events: SharedFlow<Event> = _events.asSharedFlow()
474
-
475
- suspend fun loadUsers() {
476
- val result = api.getUsers()
477
- _users.value = result
478
- _events.emit(Event.UsersLoaded)
479
- }
480
- }
481
-
482
- // 在 ViewModel 中使用
483
- class UserViewModel(private val repository: UserRepository) : ViewModel() {
484
- val users = repository.users
485
- .stateIn(
486
- scope = viewModelScope,
487
- started = SharingStarted.WhileSubscribed(5000),
488
- initialValue = emptyList()
489
- )
490
-
491
- init {
492
- viewModelScope.launch {
493
- repository.events.collect { event ->
494
- handleEvent(event)
495
- }
496
- }
497
- }
498
- }
499
- ```
500
-
501
- ### Flow 操作符链
502
- ```kotlin
503
- class SearchViewModel : ViewModel() {
504
- private val _searchQuery = MutableStateFlow("")
505
- val searchQuery: StateFlow<String> = _searchQuery
506
-
507
- val searchResults: StateFlow<List<Result>> = searchQuery
508
- .debounce(300)
509
- .filter { it.length >= 3 }
510
- .distinctUntilChanged()
511
- .flatMapLatest { query ->
512
- repository.search(query)
513
- .catch { emit(emptyList()) }
514
- }
515
- .stateIn(
516
- scope = viewModelScope,
517
- started = SharingStarted.WhileSubscribed(5000),
518
- initialValue = emptyList()
519
- )
520
-
521
- fun onSearchQueryChanged(query: String) {
522
- _searchQuery.value = query
523
- }
524
- }
525
- ```
526
-
527
- ## Android 架构
528
-
529
- ### MVVM 模式
530
- ```kotlin
531
- // Model
532
- data class User(
533
- val id: String,
534
- val name: String,
535
- val email: String
536
- )
537
-
538
- // Repository
539
- class UserRepository(
540
- private val api: ApiService,
541
- private val dao: UserDao
542
- ) {
543
- fun getUsers(): Flow<List<User>> = flow {
544
- // 先发射缓存数据
545
- emit(dao.getAll())
546
-
547
- // 然后获取网络数据
548
- val users = api.getUsers()
549
- dao.insertAll(users)
550
- emit(users)
551
- }
552
- }
553
-
554
- // ViewModel
555
- class UserViewModel(
556
- private val repository: UserRepository
557
- ) : ViewModel() {
558
- private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
559
- val uiState: StateFlow<UiState> = _uiState.asStateFlow()
560
-
561
- init {
562
- loadUsers()
563
- }
564
-
565
- private fun loadUsers() {
566
- viewModelScope.launch {
567
- repository.getUsers()
568
- .catch { e ->
569
- _uiState.value = UiState.Error(e.message ?: "Unknown error")
570
- }
571
- .collect { users ->
572
- _uiState.value = UiState.Success(users)
573
- }
574
- }
575
- }
576
- }
577
-
578
- sealed class UiState {
579
- object Loading : UiState()
580
- data class Success(val users: List<User>) : UiState()
581
- data class Error(val message: String) : UiState()
582
- }
583
-
584
- // View (Compose)
585
- @Composable
586
- fun UserScreen(viewModel: UserViewModel = viewModel()) {
587
- val uiState by viewModel.uiState.collectAsState()
588
-
589
- when (val state = uiState) {
590
- is UiState.Loading -> LoadingIndicator()
591
- is UiState.Success -> UserList(state.users)
592
- is UiState.Error -> ErrorMessage(state.message)
593
- }
594
- }
595
- ```
596
-
597
- ## Room 数据库
598
-
599
- ### 实体与 DAO
600
- ```kotlin
601
- // Entity
602
- @Entity(tableName = "users")
603
- data class UserEntity(
604
- @PrimaryKey val id: String,
605
- @ColumnInfo(name = "name") val name: String,
606
- @ColumnInfo(name = "email") val email: String,
607
- @ColumnInfo(name = "created_at") val createdAt: Long
608
- )
609
-
610
- // DAO
611
- @Dao
612
- interface UserDao {
613
- @Query("SELECT * FROM users")
614
- fun getAll(): Flow<List<UserEntity>>
615
-
616
- @Query("SELECT * FROM users WHERE id = :userId")
617
- suspend fun getById(userId: String): UserEntity?
618
-
619
- @Insert(onConflict = OnConflictStrategy.REPLACE)
620
- suspend fun insert(user: UserEntity)
621
-
622
- @Insert
623
- suspend fun insertAll(users: List<UserEntity>)
624
-
625
- @Update
626
- suspend fun update(user: UserEntity)
627
-
628
- @Delete
629
- suspend fun delete(user: UserEntity)
630
-
631
- @Query("DELETE FROM users WHERE created_at < :timestamp")
632
- suspend fun deleteOlderThan(timestamp: Long)
633
- }
634
-
635
- // Database
636
- @Database(entities = [UserEntity::class], version = 1)
637
- abstract class AppDatabase : RoomDatabase() {
638
- abstract fun userDao(): UserDao
639
-
640
- companion object {
641
- @Volatile
642
- private var INSTANCE: AppDatabase? = null
643
-
644
- fun getDatabase(context: Context): AppDatabase {
645
- return INSTANCE ?: synchronized(this) {
646
- val instance = Room.databaseBuilder(
647
- context.applicationContext,
648
- AppDatabase::class.java,
649
- "app_database"
650
- ).build()
651
- INSTANCE = instance
652
- instance
653
- }
654
- }
655
- }
656
- }
657
- ```
658
-
659
- ## Retrofit 网络请求
660
-
661
- ### API 定义
662
- ```kotlin
663
- interface ApiService {
664
- @GET("users")
665
- suspend fun getUsers(): List<User>
666
-
667
- @GET("users/{id}")
668
- suspend fun getUser(@Path("id") userId: String): User
669
-
670
- @POST("users")
671
- suspend fun createUser(@Body user: CreateUserRequest): User
672
-
673
- @PUT("users/{id}")
674
- suspend fun updateUser(
675
- @Path("id") userId: String,
676
- @Body user: UpdateUserRequest
677
- ): User
678
-
679
- @DELETE("users/{id}")
680
- suspend fun deleteUser(@Path("id") userId: String)
681
-
682
- @GET("search")
683
- suspend fun search(@Query("q") query: String): SearchResult
684
- }
685
-
686
- // Retrofit 配置
687
- object RetrofitClient {
688
- private val okHttpClient = OkHttpClient.Builder()
689
- .addInterceptor { chain ->
690
- val request = chain.request().newBuilder()
691
- .addHeader("Authorization", "Bearer $token")
692
- .build()
693
- chain.proceed(request)
694
- }
695
- .connectTimeout(30, TimeUnit.SECONDS)
696
- .readTimeout(30, TimeUnit.SECONDS)
697
- .build()
698
-
699
- val api: ApiService = Retrofit.Builder()
700
- .baseUrl("https://api.example.com/")
701
- .client(okHttpClient)
702
- .addConverterFactory(GsonConverterFactory.create())
703
- .build()
704
- .create(ApiService::class.java)
705
- }
706
- ```
707
-
708
- ## 依赖注入 (Hilt)
709
-
710
- ### 模块配置
711
- ```kotlin
712
- @HiltAndroidApp
713
- class MyApplication : Application()
714
-
715
- @Module
716
- @InstallIn(SingletonComponent::class)
717
- object AppModule {
718
- @Provides
719
- @Singleton
720
- fun provideApiService(): ApiService {
721
- return RetrofitClient.api
722
- }
723
-
724
- @Provides
725
- @Singleton
726
- fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
727
- return AppDatabase.getDatabase(context)
728
- }
729
-
730
- @Provides
731
- fun provideUserDao(database: AppDatabase): UserDao {
732
- return database.userDao()
733
- }
734
- }
735
-
736
- // 注入使用
737
- @HiltViewModel
738
- class UserViewModel @Inject constructor(
739
- private val repository: UserRepository
740
- ) : ViewModel() {
741
- // ...
742
- }
743
-
744
- @AndroidEntryPoint
745
- class MainActivity : ComponentActivity() {
746
- private val viewModel: UserViewModel by viewModels()
747
-
748
- override fun onCreate(savedInstanceState: Bundle?) {
749
- super.onCreate(savedInstanceState)
750
- // ...
751
- }
752
- }
753
- ```
754
-
755
- ## 测试
756
-
757
- ### 单元测试
758
- ```kotlin
759
- class CalculatorTest {
760
- private lateinit var calculator: Calculator
761
-
762
- @Before
763
- fun setup() {
764
- calculator = Calculator()
765
- }
766
-
767
- @Test
768
- fun `addition should return correct result`() {
769
- val result = calculator.add(2, 3)
770
- assertEquals(5, result)
771
- }
772
-
773
- @Test
774
- fun `division by zero should throw exception`() {
775
- assertThrows<ArithmeticException> {
776
- calculator.divide(10, 0)
777
- }
778
- }
779
- }
780
-
781
- // 协程测试
782
- @ExperimentalCoroutinesApi
783
- class UserViewModelTest {
784
- @get:Rule
785
- val mainDispatcherRule = MainDispatcherRule()
786
-
787
- private lateinit var viewModel: UserViewModel
788
- private lateinit var repository: FakeUserRepository
789
-
790
- @Before
791
- fun setup() {
792
- repository = FakeUserRepository()
793
- viewModel = UserViewModel(repository)
794
- }
795
-
796
- @Test
797
- fun `loadUsers should update state to success`() = runTest {
798
- val users = listOf(User("1", "John", "john@example.com"))
799
- repository.setUsers(users)
800
-
801
- viewModel.loadUsers()
802
-
803
- val state = viewModel.uiState.value
804
- assertTrue(state is UiState.Success)
805
- assertEquals(users, (state as UiState.Success).users)
806
- }
807
- }
808
- ```
809
-
810
- ## 最佳实践
811
-
812
- | 场景 | 推荐做法 |
813
- |------|----------|
814
- | 异步操作 | 使用 Coroutines + Flow |
815
- | 状态管理 | StateFlow + ViewModel |
816
- | 依赖注入 | Hilt |
817
- | 网络请求 | Retrofit + OkHttp |
818
- | 本地存储 | Room + DataStore |
819
- | UI 开发 | Jetpack Compose |
820
-
821
- ## 工具清单
822
-
823
- | 工具 | 用途 |
824
- |------|------|
825
- | Android Studio | 官方 IDE |
826
- | Gradle | 构建工具 |
827
- | Kotlin Coroutines | 异步编程 |
828
- | Jetpack Compose | 声明式 UI |
829
- | Hilt | 依赖注入 |
830
- | Retrofit | 网络请求 |
831
- | Room | 数据库 |
832
- | Coil | 图片加载 |
833
-
834
- ---