# Ôn phỏng vấn iKame — Android Middle

Tập trung đúng JD iKame: Kotlin, Android Internals, Compose, Coroutines/Flow, MVVM/MVI, Clean Architecture, Modularization, Design Patterns + SOLID. Mỗi câu trả lời gọn, đủ dùng cho phỏng vấn 2-3 năm kinh nghiệm.

---

## Mục lục

1. [Kotlin cốt lõi](#1-kotlin-cốt-lõi)
2. [OOP & SOLID](#2-oop--solid)
3. [Android Internals](#3-android-internals)
4. [Coroutines & Flow](#4-coroutines--flow)
5. [Jetpack Compose](#5-jetpack-compose)
6. [Architecture (MVVM / MVI / Clean)](#6-architecture-mvvm--mvi--clean)
7. [Modularization](#7-modularization)
8. [Design Patterns trong Android](#8-design-patterns-trong-android)
9. [Điểm cộng: KMP, DSA cơ bản](#9-điểm-cộng-kmp-dsa-cơ-bản)

---

## 1. Kotlin cốt lõi

### Q1. val vs var vs const val?

- **val**: tham chiếu bất biến — không gán lại được, nhưng nếu object bên trong là mutable thì vẫn thay đổi nội dung được (ví dụ `val list = mutableListOf()` vẫn `.add()` bình thường).
- **var**: tham chiếu khả biến — gán lại thoải mái bất cứ lúc nào.
- **const val**: hằng số tại thời điểm compile. Chỉ dùng với primitive hoặc String, phải đặt ở top-level hoặc trong `companion object`. Giá trị được inline trực tiếp vào bytecode nên không tạo field thật sự.

```kotlin
val list = mutableListOf(1, 2)
list.add(3)               // OK — nội dung object thay đổi được
// list = mutableListOf() // LỖI — không gán lại reference được

const val API_URL = "https://api.com"  // inline vào bytecode, không tạo field
```

### Q2. Null safety — ?. / ?: / !! / let?

Kotlin thiết kế hệ thống kiểu phân biệt rõ nullable (`String?`) và non-null (`String`) ngay từ compile-time, giúp loại bỏ phần lớn NullPointerException.

| Toán tử | Ý nghĩa                                | Ví dụ                     |
| ------- | -------------------------------------- | ------------------------- |
| `?.`    | Safe call — trả null nếu receiver null | `user?.name`              |
| `?:`    | Elvis — giá trị mặc định khi null      | `name ?: "Unknown"`       |
| `!!`    | Force unwrap — ném NPE nếu null        | `name!!.length`           |
| `let`   | Chạy block khi non-null                | `name?.let { print(it) }` |

> Bẫy: `!!` gần như không nên dùng trong production code. Nếu thấy `!!` xuất hiện nhiều, đó là dấu hiệu thiết kế sai — nên dùng `?.`, `?:`, hoặc redesign cho non-null.

### Q3. data class khác class thường thế nào?

`data class` yêu cầu ít nhất 1 property trong primary constructor, và compiler sẽ tự sinh:
- `equals()` / `hashCode()` — so sánh theo giá trị tất cả property trong primary constructor.
- `toString()` — in dạng `User(id=1, name="An")`.
- `componentN()` — cho destructuring: `val (id, name) = user`.
- `copy()` — tạo bản sao, có thể đổi 1 vài field: `user.copy(name = "Bình")`.

**Bẫy quan trọng**: chỉ property trong **primary constructor** mới được tính vào `equals`/`hashCode`. Property khai báo trong body bị bỏ qua hoàn toàn:

```kotlin
data class User(val id: Int) {
    var name: String = ""   // KHÔNG tham gia equals/hashCode
}
User(1).apply { name = "A" } == User(1).apply { name = "B" }  // true!
```

→ Mọi property dùng để **định danh** object phải đặt trong primary constructor.

### Q4. sealed class / sealed interface — khi nào dùng?

**sealed** giới hạn tập subtype được phép tồn tại — tất cả subtype phải nằm cùng package. Compiler biết chính xác có bao nhiêu subtype → `when` exhaustive (không cần `else`). Nếu sau này thêm subtype mới, mọi `when` trong project sẽ compile fail → buộc phải cập nhật, an toàn hơn `else -> {}` (âm thầm bỏ qua case mới).

```kotlin
sealed class Result
class Success(val data: List<User>) : Result()
class Error(val message: String) : Result()
object Loading : Result()

when (result) {
    is Success -> showData(result.data)
    is Error   -> showError(result.message)
    is Loading -> showLoading()
    // không cần else — compiler biết đủ 3 case
}
```

**sealed class vs sealed interface**:
- `sealed class`: subtype chỉ kế thừa được 1 class (Kotlin không cho đa kế thừa class). Dùng khi cần chia sẻ state hoặc constructor chung giữa các subtype.
- `sealed interface`: subtype có thể implement nhiều sealed interface cùng lúc → linh hoạt hơn. Đây là lựa chọn mặc định.

```kotlin
sealed interface Animal
sealed interface Pet
class Dog : Animal, Pet   // OK — 1 type thuộc nhiều sealed hierarchy cùng lúc
```

**Khác enum**: enum yêu cầu mọi value cùng khuôn (cùng property), sealed cho phép mỗi subtype có schema riêng biệt — `Success` mang `data`, `Error` mang `message`, `Loading` không cần gì.

**Khác abstract class**: abstract class mở — ai cũng extend được, kể cả module khác → `when` không exhaustive, vẫn phải có `else`.

**Use case thực tế**: UI state (Loading/Success/Error), Result wrapper, Navigation event, MVI Intent/Action.

### Q5. Scope functions: let, run, with, apply, also

Kotlin cung cấp 5 scope function, phân biệt theo 2 chiều:
1. **Truy cập object thế nào**: `it` (phải viết rõ `it.name`) hay `this` (gọi trực tiếp `name` như đang ở trong class).
2. **Trả về gì**: kết quả dòng cuối (block result) hay chính object gốc (object itself).

| Function | Truy cập object | Trả về        | Use case                          |
| -------- | --------------- | ------------- | --------------------------------- |
| `let`    | `it`            | kết quả block | Null check + transform            |
| `run`    | `this`          | kết quả block | Cấu hình + tính giá trị           |
| `with`   | `this`          | kết quả block | Gọi nhiều method trên cùng object |
| `apply`  | `this`          | chính object  | Builder-style config              |
| `also`   | `it`            | chính object  | Side effect (log, validate)       |

**Khi nào chọn `it`?** Khi cần đổi tên cho dễ đọc (`user?.let { u -> ... }`), hoặc khi nhiều scope lồng nhau — `this` ngoài bị che bởi `this` trong, dùng `it` tránh nhầm lẫn.

**Khi nào chọn `this`?** Khi gọi nhiều method/property trên cùng 1 object → bớt lặp `it.xxx`.

**Trả về object itself** dùng để chain tiếp (config xong gán vào biến). **Trả về block result** dùng để transform sang kiểu khác.

| Tình huống                          | Chọn            |
| ----------------------------------- | --------------- |
| Null check rồi transform            | `?.let { }`     |
| Config object lúc khởi tạo          | `apply { }`     |
| Log/validate giữa chain             | `also { }`      |
| Tính ra value mới từ object         | `run { }`       |
| Gọi nhiều method trên object có sẵn | `with(obj) { }` |

### Q6. inline function — tại sao cần?

Bình thường mỗi lambda compile thành 1 anonymous class + 1 object instance → tốn allocation trên heap, đặc biệt trong vòng lặp hoặc hàm gọi thường xuyên.

`inline` bảo compiler copy toàn bộ body của function + body của lambda thẳng vào call site → không tạo class/object nào cả. Tương đương viết code trực tiếp tại chỗ gọi.

**Bắt buộc** để dùng **reified generics** — giữ type info ở runtime (bình thường generic bị type erasure, không biết `T` là gì lúc runtime):

```kotlin
inline fun <reified T> Gson.fromJson(json: String): T =
    fromJson(json, T::class.java)  // T::class.java chỉ dùng được khi reified
```

**Modifier bổ sung**: `noinline` (1 lambda cụ thể không inline — vì cần truyền nó như object), `crossinline` (lambda không được phép non-local return — dùng khi lambda chạy trong context khác như coroutine).

> Khi nào KHÔNG inline? Function body lớn, gọi ở nhiều nơi → mỗi call site copy cả body → code bloat, tăng kích thước APK. inline chỉ đáng cho function nhận lambda + ngắn gọn.

### Q7. lateinit vs lazy?

|             | `lateinit`     | `by lazy`                  |
| ----------- | -------------- | -------------------------- |
| Kiểu        | `var`          | `val`                      |
| Primitive   | Không hỗ trợ   | Có                         |
| Thời điểm   | Bất kỳ lúc nào | Lần đầu access             |
| Thread-safe | Không          | Mặc định có (SYNCHRONIZED) |

- **lateinit**: dùng khi biết chắc sẽ init trước khi dùng, nhưng không thể init ngay lúc khai báo. Ví dụ: DI inject, `binding` trong `onCreate`. Truy cập trước khi init → crash `UninitializedPropertyAccessException`. Có thể check bằng `::property.isInitialized`.
- **lazy**: dùng khi compute đắt và có thể không bao giờ dùng đến. Chỉ tính 1 lần, cache kết quả. Có 3 mode: `SYNCHRONIZED` (mặc định, thread-safe), `PUBLICATION` (nhiều thread tính, chỉ dùng kết quả đầu), `NONE` (không sync, nhanh nhất — chỉ dùng trong single-thread).

### Q8. Generics — in, out, *?

Variance quyết định quan hệ kế thừa giữa generic types:

- **out T** (covariance): chỉ **sản xuất** T (return), không tiêu thụ (không nhận T làm param). `List<Cat>` là subtype của `List<Animal>` → an toàn vì chỉ đọc ra `Animal`.
- **in T** (contravariance): chỉ **tiêu thụ** T (nhận param), không sản xuất. `Comparator<Animal>` dùng được cho `Comparator<Cat>` → vì so sánh Animal thì so sánh Cat cũng OK.
- **\*** (star projection): không biết/không quan tâm type cụ thể. Chỉ đọc được dạng `Any?`, không ghi được.

> Mẹo nhớ: **PECS** — Producer Extends (`out`), Consumer Super (`in`). Hoặc: **out = output = đọc ra**, **in = input = ghi vào**.

### Q9. == vs ===?

- `==`: **structural equality** → gọi `equals()` bên dưới. So sánh giá trị/nội dung.
- `===`: **referential equality** → so sánh 2 biến có trỏ đến cùng 1 object trong bộ nhớ hay không.

> Tương đương Java: `==` Kotlin = `.equals()` Java, `===` Kotlin = `==` Java. Kotlin đảo ngược ý nghĩa so với Java để cú pháp thông dụng nhất (`==`) làm điều hữu ích nhất (so sánh giá trị).

### Q10. Extension function — bytecode thế nào?

Compile thành **static method** bình thường, receiver trở thành parameter đầu tiên. → Không có virtual dispatch, compiler resolve tĩnh theo **declared type** (kiểu khai báo), không phải runtime type.

```kotlin
open class Base
class Derived : Base()
fun Base.hello() = "Base"
fun Derived.hello() = "Derived"

val b: Base = Derived()
b.hello()  // "Base" — vì declared type là Base, dù runtime type là Derived
```

Hệ quả: extension function không thể override như member function. Nếu cần polymorphism → dùng member function hoặc interface.

---

## 2. OOP & SOLID

### Q11. 4 tính chất OOP — ví dụ trong Android?

**1. Encapsulation (Đóng gói)**: Giấu chi tiết bên trong, chỉ expose ra ngoài những gì cần thiết. State chỉ được sửa qua method/property có kiểm soát — không cho code ngoài tự ý ghi vào.

Ví dụ kinh điển trong Android: `MutableStateFlow` private + `StateFlow` public trong ViewModel. Fragment/Compose chỉ collect được `StateFlow` (read-only), không thể `.value = ...`. Muốn đổi state → phải gọi method trên ViewModel (như `loadUsers()`), ViewModel kiểm soát mọi thay đổi.

**2. Inheritance (Kế thừa)**: Class con dùng lại code (property + method) của class cha, có thể override để đổi behavior ở phần khác biệt.

Ví dụ: `HomeViewModel : ViewModel()` — tự động có `viewModelScope`, `onCleared()`, lifecycle binding mà không phải tự viết lại. Chỉ cần override phần riêng. Android SDK xây dựng trên inheritance: `AppCompatActivity`, `Fragment`, `Service`...

Bẫy: Inheritance tạo coupling chặt cha-con. Sửa cha → con có thể vỡ. Kotlin mặc định `final` để giảm kế thừa bừa bãi.

**3. Polymorphism (Đa hình)**: Cùng 1 interface/method, nhiều cách thực thi khác nhau. Code gọi method không cần biết object thật sự thuộc class nào.

- **Override (đa hình động)**: class con override method cha, JVM quyết định gọi version nào **lúc runtime** dựa trên type thật của object (dynamic dispatch).
- **Overload (đa hình tĩnh)**: cùng tên function nhưng khác param (số lượng/kiểu), compiler chọn version **lúc compile**.

Ví dụ: `UserRepository` interface → `UserRepositoryImpl` (production, gọi API thật) và `FakeUserRepository` (test, trả data cứng). ViewModel nhận `UserRepository` qua constructor, không biết đang dùng impl nào → dễ test, dễ thay đổi.

> Kotlin thường thay overloading bằng **default argument**: `fun greet(name: String, greeting: String = "Hi")` — 1 hàm thay 2 overload.

**4. Abstraction (Trừu tượng hoá)**: Chỉ định nghĩa "làm gì" (contract/interface), giấu "làm thế nào" (implementation). Người dùng class chỉ cần biết signature, không cần hiểu nội bộ.

Khác Encapsulation ở chỗ: Encapsulation giấu **state** (data — private field), Abstraction giấu **logic** (implementation — interface vs impl). Encapsulation là "cách giấu", Abstraction là "giấu cái gì".

Ví dụ: `repo.getUser(id)` — ViewModel không biết bên trong có cache, có gọi API, có map DTO hay không. Mai mốt đổi REST → GraphQL, Room → SQLDelight — ViewModel không động.

### Q12. Vì sao Kotlin class mặc định final?

Triết lý "Design for inheritance or prohibit it" (Effective Java, Item 19). Java mặc định `open` → class dễ bị extend bừa, phá vỡ invariant mà tác giả không lường trước. Kotlin đảo lại: muốn cho kế thừa phải khai báo `open` tường minh, kể cả từng method.

`data class`, `enum class`, `object` đều không thể `open` — vì semantics của chúng không phù hợp với kế thừa.

### Q13. abstract class vs interface?

|                        | abstract class | interface |
| ---------------------- | -------------- | --------- |
| State có backing field | Có             | Không     |
| Constructor            | Có             | Không     |
| Đa kế thừa             | Không          | Có        |

Cả hai đều có thể chứa default method implementation. Sự khác biệt cốt lõi: abstract class **có state** (backing field, constructor), interface thì không.

Dùng **abstract class** khi cần chia sẻ state hoặc constructor logic giữa các subclass. Dùng **interface** cho contract/capability thuần tuý hoặc khi cần 1 class implement nhiều "khả năng" cùng lúc (multiple inheritance).

### Q14. SOLID — 5 nguyên tắc

**S — Single Responsibility**: Mỗi class chỉ có **1 lý do để thay đổi**. Ôm đồm nhiều việc → khó test, khó tái sử dụng, sửa chỗ này vỡ chỗ kia.

Dấu hiệu vi phạm: tên class có "Manager/Helper/Utils", file >300 dòng, method đụng nhiều domain (UI + network + analytics). Ví dụ: `UserViewModel` vừa gọi API, vừa format tên user, vừa gửi analytics → 3 lý do thay đổi. Tách: `UserRepository` (load) + `UserFormatter` (format) + `Analytics` (track).

**O — Open/Closed**: Mở cho **mở rộng** (thêm tính năng mới), đóng cho **sửa đổi** (không động code cũ đang chạy ổn). Cách đạt: dùng abstraction + polymorphism — code gốc gọi qua interface, thêm feature = thêm class mới implement interface đó.

Ví dụ: màn thanh toán có Card, MoMo, ZaloPay. Viết `when(type)` → thêm Apple Pay phải sửa hàm cũ. Theo OCP: `interface PaymentMethod`, mỗi phương thức là 1 class. Thêm Apple Pay = thêm `ApplePayMethod`, code cũ không động.

**L — Liskov Substitution**: Mọi chỗ dùng class cha phải **thay thế hoàn toàn** bằng class con mà code vẫn đúng — bao gồm cả behavior contract, không chỉ signature.

Dấu hiệu vi phạm: class con override method rồi throw `UnsupportedOperationException`; class con thắt input hoặc nới output; code phải `is` check để xử lý riêng cho từng loại con.

Ví dụ kinh điển: `Penguin` kế thừa `Bird` nhưng `fly()` throw exception → code nhận `Bird` gọi `fly()` sẽ vỡ. Fix: tách `Flyable` ra interface riêng, `Penguin` không implement `Flyable`.

**I — Interface Segregation**: Thà có nhiều interface nhỏ **chuyên biệt** còn hơn 1 "fat interface". Class không nên bị ép implement method nó không dùng.

Dấu hiệu vi phạm: class implement interface nhưng nhiều method để trống/throw, interface có >5-7 method không liên quan chặt. Ví dụ: interface `Worker` chứa cả `work()` và `eat()` → `Robot` chỉ cần `work()` nhưng bị buộc implement `eat()`. Fix: tách `Workable` và `Eatable`.

**D — Dependency Inversion**: Module cấp cao (logic nghiệp vụ) không phụ thuộc module cấp thấp (chi tiết kỹ thuật). Cả hai cùng phụ thuộc **abstraction** (interface). Đây là cốt lõi của Clean Architecture.

Lợi ích: test dễ (inject fake/mock), thay đổi linh hoạt (đổi Retrofit → Ktor chỉ đổi impl, logic không động), modularization rõ ràng (Domain định nghĩa interface, Data implement).

> Đừng nhầm **DIP** với **DI**: DIP = nguyên tắc thiết kế (phụ thuộc abstraction). DI = kỹ thuật cài đặt (truyền dependency qua constructor/setter thay vì `new` bên trong class). DI là cách phổ biến nhất để đạt DIP.

### Q15. Composition over Inheritance — tại sao?

Inheritance tạo coupling chặt cha-con: sửa class cha → class con có thể vỡ, class con bị gắn cứng với 1 hierarchy. Composition (giữ reference đến object khác) linh hoạt hơn: dễ thay đổi behavior runtime, dễ tổ hợp nhiều behavior, dễ test từng phần riêng.

Ví dụ: thay vì `class Penguin : Bird()` (Penguin kế thừa `fly()` vô nghĩa), dùng `class Bird(private val mover: Mover)` — inject `FlyingMover` cho chim bay, `WalkingMover` cho chim đi bộ. Thay đổi behavior = thay object, không cần sửa class.

---

## 3. Android Internals

### Q16. Activity lifecycle — 6 callback chính?

`onCreate` → `onStart` → `onResume` → **(running)** → `onPause` → `onStop` → `onDestroy`

- **onCreate**: khởi tạo view, `setContentView()`, nhận `savedInstanceState` để restore state. Chỉ gọi 1 lần trong đời Activity (trừ khi recreate).
- **onStart**: Activity visible nhưng chưa ở foreground — user chưa tương tác được.
- **onResume**: Activity ở foreground, focused, nhận input từ user. Đây là trạng thái "đang chạy".
- **onPause**: mất focus (dialog overlay, multi-window, Activity khác lên partial). Vẫn visible 1 phần. Nên pause animation, release camera.
- **onStop**: không visible nữa. Giải phóng tài nguyên nặng. Lưu data cần thiết (DB, SharedPreferences).
- **onDestroy**: cleanup cuối cùng. Sau callback này, Activity bị GC thu hồi. Lưu ý: process có thể bị kill bất kỳ lúc nào **sau onStop** mà không qua `onDestroy`.

**Config change** (xoay màn hình, đổi locale, đổi theme): Activity destroy → recreate hoàn toàn. Dùng `ViewModel` giữ data qua recreation, `rememberSaveable`/`SavedStateHandle` cho UI state nhỏ.

**onRestart**: gọi khi Activity quay lại từ trạng thái stopped (ví dụ user bấm back từ Activity khác) → tiếp tục `onStart`.

### Q17. Fragment lifecycle — khác Activity?

Thêm các callback riêng: `onAttach` → `onCreate` → `onCreateView` → `onViewCreated` → `onStart` → `onResume` → `onPause` → `onStop` → `onDestroyView` → `onDestroy` → `onDetach`.

**Bẫy quan trọng nhất**: Fragment lifecycle ≠ View lifecycle. Fragment có thể sống lâu hơn View — khi Fragment bị đẩy vào backstack, `onDestroyView` được gọi (View bị huỷ) nhưng Fragment vẫn alive (không gọi `onDestroy`). Khi pop lại, `onCreateView` → `onViewCreated` chạy lại với View mới.

Hệ quả: khi observe Flow/LiveData từ Fragment, **phải dùng `viewLifecycleOwner`**, KHÔNG phải `this`. Dùng `this` → observer sống theo Fragment lifecycle → khi View bị huỷ mà Fragment còn sống, observer vẫn nhận data và cập nhật View đã huỷ → crash hoặc leak.

```kotlin
viewLifecycleOwner.lifecycleScope.launch {
    viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.uiState.collect { render(it) }
    }
}
```

### Q18. ViewModel sống qua config change nhờ gì? SavedStateHandle để làm gì?

**Config change (rotate)**: `ViewModelStore` được giữ qua `NonConfigurationInstances` của `ComponentActivity`. Khi Activity destroy do config change, hệ thống lưu `ViewModelStore` sang bên, Activity mới recreate xong nhận lại → ViewModel instance cũ được reuse nguyên vẹn.

ViewModel **chết** khi: Activity `finish()` thật sự (user bấm back, gọi `finish()`), Fragment bị pop khỏi backstack hẳn, hoặc process bị kill.

**Process death** hoàn toàn khác config change — toàn bộ process bị kill, mọi object trên heap mất (bao gồm ViewModel). Khi user quay lại app, Android restore Activity stack nhưng ViewModel khởi tạo lại từ đầu, dữ liệu trong RAM mất sạch.

→ Cần **SavedStateHandle**: lưu UI state nhỏ vào `Bundle` (survive cả process death):

- UI state nhỏ (query text, selected tab, scroll position) → `SavedStateHandle`.
- Data từ API → cache vào DB/disk, ViewModel reload khi khởi tạo lại.

> Lưu ý: Bundle có giới hạn ~500KB cho toàn bộ transaction. Vượt quá → `TransactionTooLargeException`. Đừng nhét object lớn (list, bitmap) vào `SavedStateHandle`.

### Q19. Process lifecycle — biết app foreground/background?

`ProcessLifecycleOwner` cung cấp observer cho lifecycle cấp **toàn app** (không phải từng Activity):

- `onStart` → app vào foreground (có ít nhất 1 Activity visible).
- `onStop` → app vào background (không Activity nào visible).

Hữu ích cho: pause/resume analytics tracking, stop/start polling, lock app khi inactive, hiển thị overlay "session expired".

### Q20. Memory leak — pattern phổ biến và cách tránh?

Memory leak xảy ra khi object không còn cần nhưng vẫn bị giữ reference → GC không thu hồi được → RAM tăng dần → OOM crash.

| Pattern                   | Nguyên nhân                           | Cách fix                               |
| ------------------------- | ------------------------------------- | -------------------------------------- |
| Static giữ Context        | `static var context = activity`       | Dùng `applicationContext`              |
| Inner class non-static    | Handler/AsyncTask giữ outer Activity  | static class + WeakReference           |
| Listener không unregister | Register onCreate, quên remove        | Cleanup trong `onDestroy`              |
| Coroutine không cancel    | `GlobalScope.launch` giữ Activity ref | Dùng `viewModelScope`/`lifecycleScope` |
| Singleton giữ Activity    | DI inject sai scope                   | Inject Application thay vì Activity    |

Phát hiện: **LeakCanary** — tự dump heap khi nghi leak, hiển thị reference chain chi tiết.

### Q21. Threading model — Looper / Handler / MessageQueue?

Mỗi thread có thể gắn 1 bộ Looper + MessageQueue + Handler:

- **MessageQueue**: hàng đợi FIFO chứa các `Message` chờ xử lý.
- **Looper**: vòng lặp vô hạn, liên tục lấy message từ queue ra xử lý. Mỗi lần lấy 1 message, xử lý xong mới lấy tiếp.
- **Handler**: công cụ để post message vào queue (từ bất kỳ thread nào) + xử lý message khi đến lượt.

Main thread (UI thread) có Looper được khởi tạo sẵn trong `ActivityThread.main()`. **Tất cả** UI update, lifecycle callback, touch event đều chạy qua message queue này — tuần tự, không song song.

→ Block main thread = freeze UI. Mọi message phía sau đều phải đợi → nếu quá lâu → ANR.

### Q22. ANR là gì? Ngưỡng bao nhiêu?

**Application Not Responding** — hệ thống phát hiện main thread không xử lý kịp sự kiện trong thời gian quy định:

- Input event (touch, key): **5 giây**
- BroadcastReceiver `onReceive()`: **10 giây** (foreground), **60 giây** (background)
- Service lifecycle: **20 giây** (foreground), **200 giây** (background)

Tránh ANR bằng cách đẩy việc nặng ra background:
- I/O (network, file, DB) → `Dispatchers.IO`
- CPU nặng (parse JSON lớn, image processing) → `Dispatchers.Default`
- Tuyệt đối không `Thread.sleep()`, blocking `.get()`, hay synchronous network call trên main thread.

Debug: dùng `StrictMode` trong debug build để phát hiện disk/network operation trên main thread sớm.

---

## 4. Coroutines & Flow

### Q23. Coroutine vs Thread?

|            | Thread       | Coroutine                                    |
| ---------- | ------------ | -------------------------------------------- |
| OS thread  | 1-1          | N coroutines : M threads                     |
| Chi phí    | ~1MB stack   | ~vài KB                                      |
| Chuyển đổi | Kernel-level | User-level (rẻ)                              |
| Tạm dừng   | Block thread | Không block, giải phóng thread cho việc khác |

`suspend` function compile thành **state machine** + `Continuation` (CPS — Continuation-Passing Style). Mỗi suspend point trở thành 1 state trong state machine. Khi suspend "tạm dừng", thread được giải phóng để chạy coroutine khác — không có "magic thread" hay green thread, chỉ là tái sử dụng thread pool thông minh.

Có thể chạy hàng ngàn coroutine trên vài thread mà không bị OOM — khác với thread, mỗi thread tốn ~1MB stack.

### Q24. Dispatchers?

- **Main**: UI thread. Chỉ dùng cho update view, observe Flow. Không bao giờ làm việc nặng ở đây.
- **IO**: Pool cho I/O blocking (network, file, DB). Mặc định 64 thread, có thể mở rộng. Tối ưu cho blocking I/O (thread nằm chờ I/O response).
- **Default**: Pool cho CPU-intensive (parse, sort, tính toán). Số thread = số CPU cores. Tối ưu cho compute (thread chạy liên tục, dùng hết CPU).
- **Unconfined**: không confine vào thread cụ thể — chạy ở thread nào resume thì tiếp ở đó. Chủ yếu dùng cho testing hoặc case đặc biệt.

### Q25. Structured Concurrency là gì?

Mọi coroutine phải chạy trong 1 `CoroutineScope`. Scope quản lý lifecycle: khi scope huỷ → tất cả coroutine con huỷ theo, tránh leak.

3 quy tắc cốt lõi:
1. **Parent đợi child**: parent coroutine chỉ complete khi mọi child hoàn thành.
2. **Failure propagation**: child fail → parent fail → cancel mọi sibling (trừ `SupervisorJob`).
3. **Cancellation propagation**: cancel parent → cancel mọi child recursively.

Ví dụ: `viewModelScope.launch { async { apiA() }; async { apiB() } }` — nếu ViewModel cleared, cả 2 API call bị cancel. Không có coroutine "mồ côi" chạy ngầm tốn resource.

### Q26. launch vs async vs withContext?

- **launch**: "bắn-và-quên" — chạy coroutine nhưng không quan tâm kết quả. Trả về `Job` (có thể cancel/join). Dùng cho side effect (log, analytics, update UI).
- **async**: chạy coroutine và trả về `Deferred<T>` — gọi `.await()` để lấy kết quả. Dùng khi cần chạy **song song** nhiều task rồi gộp kết quả.
- **withContext**: switch dispatcher + trả kết quả, suspend đến khi xong. Dùng khi cần chạy **tuần tự** trên dispatcher khác.

```kotlin
// Tuần tự — tổng thời gian = a + b
val a = withContext(IO) { apiA() }
val b = withContext(IO) { apiB() }

// Song song — tổng thời gian = max(a, b)
coroutineScope {
    val a = async { apiA() }
    val b = async { apiB() }
    a.await() + b.await()
}
```

**Exception handling khác nhau**:
- `launch`: throw ngay khi xảy ra → bắt bằng try-catch trong block, hoặc `CoroutineExceptionHandler` ở root.
- `async`: exception được **lưu** trong `Deferred` → chỉ throw lúc gọi `.await()` → phải try-catch quanh `.await()`. `CoroutineExceptionHandler` **KHÔNG** bắt được exception từ `async`.

### Q27. SupervisorJob vs Job?

- **Job** (mặc định): child fail → cancel siblings + cancel parent → cả scope sập. Phù hợp khi các task phụ thuộc nhau — 1 cái fail thì cả nhóm vô nghĩa.
- **SupervisorJob**: child fail **không ảnh hưởng** siblings. Mỗi child tự quản lý failure riêng. Phù hợp cho UI scope — 1 request fail không nên kill toàn bộ scope.

`viewModelScope` và `lifecycleScope` đều dùng `SupervisorJob` internally → 1 coroutine crash không ảnh hưởng coroutine khác trong cùng scope.

### Q28. Cancellation — cooperative là gì?

Coroutine **không bị kill cưỡng bức** — system chỉ set flag `isActive = false`. Coroutine phải tự kiểm tra flag này hoặc gọi suspend function (mọi standard suspend function đều tự check cancellation) để dừng.

```kotlin
// SAI — vòng lặp không check cancellation, chạy mãi
while (true) { heavyCompute() }

// ĐÚNG — check isActive mỗi vòng
while (isActive) { heavyCompute() }

// ĐÚNG — yield() là suspend point, check cancellation
while (true) { yield(); heavyCompute() }
```

Nếu code CPU-intensive không gọi suspend function nào, nó sẽ **không bao giờ bị cancel** dù scope đã huỷ.

### Q29. Xử lý exception?

Hai cách bắt, tuỳ vị trí và nhu cầu:

- **try-catch**: bắt exception trong block, xử lý **local** — biến lỗi thành UI state (Error), hiện snackbar, v.v.
- **CoroutineExceptionHandler**: bắt **uncaught exception** ở root coroutine — dùng cho logging, crash reporting. Chỉ hoạt động với `launch`, **không** với `async`.

> Quan trọng: **Đừng bao giờ nuốt `CancellationException`**. Đây là cơ chế structured concurrency dùng để huỷ coroutine. Nuốt nó = coroutine không biết đã bị cancel, tiếp tục chạy → phá vỡ lifecycle management. Nếu catch `Exception` chung, phải re-throw `CancellationException`.

### Q30. Race condition — làm sao tránh?

Nhiều coroutine cùng đọc/ghi shared mutable state → kết quả không deterministic. Ví dụ: `counter++` từ 1000 coroutine → kết quả < 1000 vì read-modify-write không atomic.

| Cách                     | Mô tả                                              |
| ------------------------ | -------------------------------------------------- |
| `Mutex`                  | Khoá đoạn critical: `mutex.withLock { counter++ }` |
| `Atomic`                 | Cho primitive: `AtomicInteger`                     |
| Single-thread dispatcher | `Dispatchers.Default.limitedParallelism(1)`        |

> **Đừng dùng `synchronized`** trong suspend function — `synchronized` block thread (không phải suspend), giữ lock khi thread bị suspend → deadlock tiềm ẩn + phá structured concurrency.

### Q31. StateFlow vs SharedFlow vs LiveData?

|                  | StateFlow                     | SharedFlow         | LiveData |
| ---------------- | ----------------------------- | ------------------ | -------- |
| Giá trị khởi tạo | Bắt buộc                      | Không              | Không    |
| Conflate         | Có (chỉ giữ latest)           | Tuỳ cấu hình       | Có       |
| Lifecycle-aware  | Không (cần repeatOnLifecycle) | Không              | Có       |
| Use case         | UI state                      | Sự kiện (one-shot) | Legacy   |

- **StateFlow**: luôn có giá trị hiện tại (`.value`), conflate tự động (emit giá trị trùng bị bỏ qua). Dùng cho **UI state** — Loading, Success, Error.
- **SharedFlow**: configurable replay/buffer, không conflate mặc định, có thể emit cùng giá trị nhiều lần. Dùng cho **events** — snackbar, navigation, toast (sự kiện xảy ra 1 lần, không phải state).
- **LiveData**: lifecycle-aware sẵn nhưng API hạn chế hơn Flow. Nên chuyển sang StateFlow cho dự án mới.

### Q32. Cold vs Hot Flow?

- **Cold** (`flow { }`, `flowOf`): **không chạy** cho đến khi có collector. Mỗi collector trigger 1 lần thực thi riêng — 2 collector = 2 lần chạy code bên trong `flow { }`.
- **Hot** (`StateFlow`, `SharedFlow`): chạy **độc lập** với collector. Mọi collector chia sẻ cùng luồng emission. Không có collector vẫn có thể đang emit.

`stateIn` / `shareIn` chuyển cold → hot:

```kotlin
val users = repository.observeUsers()   // cold flow
    .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
```

`WhileSubscribed(5000)`: tắt upstream sau 5 giây khi không có subscriber → tiết kiệm tài nguyên nhưng vẫn handle config change (Activity recreate trong <5 giây thì không restart).

### Q33. Flow operators quan trọng?

| Operator               | Công dụng                                        |
| ---------------------- | ------------------------------------------------ |
| `map`, `filter`        | Transform/lọc giống collection                   |
| `combine(a, b)`        | Emit khi a HOẶC b thay đổi, cung cấp cả 2 latest |
| `zip(a, b)`            | Emit theo cặp — đợi cả hai có giá trị mới        |
| `flatMapLatest`        | Huỷ flow trước, switch sang flow mới             |
| `debounce(300)`        | Chờ 300ms im lặng mới emit — throttle input      |
| `distinctUntilChanged` | Bỏ emission trùng liên tiếp                      |

Kết hợp kinh điển cho search box:

```kotlin
searchQuery
    .debounce(300)              // chờ user ngừng gõ
    .distinctUntilChanged()     // bỏ qua nếu query không đổi
    .flatMapLatest { query ->   // cancel search cũ, chạy search mới
        repo.search(query)
    }
```

### Q34. Backpressure trong Flow?

Khi producer emit nhanh hơn consumer xử lý → buffer phình hoặc UI lag. Flow cung cấp 4 operator xử lý:

| Operator            | Hành vi                                                        |
| ------------------- | -------------------------------------------------------------- |
| `buffer(n)`         | Đệm n phần tử, suspend producer khi buffer đầy                 |
| `conflate()`        | Chỉ giữ value mới nhất, drop tất cả intermediate               |
| `collectLatest { }` | Huỷ block xử lý cũ khi có emission mới — chỉ xử lý cái cuối    |
| `sample(period)`    | Lấy mẫu theo định kỳ, bỏ qua emission giữa các khoảng sampling |

Ví dụ: sensor emit 60fps nhưng UI chỉ cần latest → `sensorFlow.conflate().collect { render(it) }`.

### Q35. repeatOnLifecycle — tại sao cần?

Collect Flow trong UI **phải dừng khi ở background** (tránh tốn resource + crash khi update UI từ background) và **restart khi foreground**. `repeatOnLifecycle` tự động quản lý: start collect khi lifecycle đạt state chỉ định (STARTED), cancel khi xuống dưới.

Nếu collect bằng `lifecycleScope.launch` thuần → collector chạy mãi kể cả khi app background → waste.

Compose tương đương: `collectAsStateWithLifecycle()` — tự xử lý lifecycle binding.

---

## 5. Jetpack Compose

### Q36. Compose vs XML?

|         | XML + View                      | Compose                                  |
| ------- | ------------------------------- | ---------------------------------------- |
| Mô hình | Imperative (ra lệnh từng bước)  | Declarative (mô tả trạng thái mong muốn) |
| State   | Cập nhật thủ công (`setText()`) | Tự động recompose khi state đổi          |
| Code    | XML layout + Kotlin logic riêng | 100% Kotlin, UI và logic cùng chỗ        |

Compose loại bỏ View hierarchy truyền thống, thay bằng Composition tree. Không cần `findViewById`, `ViewBinding`, hay `DataBinding`. Preview bằng `@Preview` annotation ngay trong IDE.

### Q37. Recomposition hoạt động thế nào? @Stable vs @Immutable?

Khi **state** được đọc bên trong composable thay đổi giá trị → Compose **re-execute** function đó (và children). Đây gọi là recomposition.

**Skipping**: Compose có thể **bỏ qua** recompose cho composable nếu 2 điều kiện thoả:
1. Mọi param là **stable type** (primitive, String, function type, hoặc đánh dấu `@Stable`/`@Immutable`).
2. Giá trị input **không đổi** (`equals` return true so với lần render trước).

- **@Immutable**: cam kết mạnh — object **không bao giờ đổi** sau khi tạo. Mọi property đều `val` + immutable type. Compose tin tưởng hoàn toàn → skip nhanh nhất.
- **@Stable**: cam kết yếu hơn — object **có thể đổi**, nhưng cam kết sẽ **báo Compose biết** khi đổi (thông qua `MutableState` bên trong). Compose vẫn skip được nếu `equals` trả true.

> Bẫy: `List`, `Map` → **unstable** mặc định (vì là interface — impl có thể là `MutableList`). Compose không biết chắc chúng có thay đổi không → không skip. Fix: dùng `ImmutableList` (kotlinx.collections.immutable) hoặc wrap trong `@Immutable` data class.

### Q38. remember vs rememberSaveable?

- **remember**: lưu giá trị qua recomposition (composable bị gọi lại vẫn giữ value). **Mất** khi config change (rotate, locale change) vì Composition bị huỷ hoàn toàn.
- **rememberSaveable**: lưu vào `Bundle` → **sống sót** cả config change và process death. Chỉ dùng cho `Parcelable`, `Serializable`, primitive, hoặc khai báo custom `Saver`.

Quy tắc: UI state tạm (animation progress, dropdown expanded) → `remember`. UI state user quan tâm (text input, scroll position, selected tab) → `rememberSaveable`.

### Q39. Side effects trong Compose?

Composable function phải **idempotent** và **không có side effect** (đọc state, trả UI, không làm gì khác). Khi cần side effect, dùng các Effect API:

| Effect                     | Công dụng                                              |
| -------------------------- | ------------------------------------------------------ |
| `LaunchedEffect(key)`      | Chạy suspend khi vào composition. Chạy lại khi key đổi |
| `DisposableEffect(key)`    | Setup + cleanup (register/unregister listener)         |
| `SideEffect`               | Chạy sau mỗi recomposition thành công                  |
| `rememberCoroutineScope()` | Lấy scope để launch từ callback (onClick, onScroll)    |

- `LaunchedEffect`: dùng khi cần gọi suspend function theo key (load data khi userId đổi, start animation). Cancel coroutine cũ khi key đổi hoặc composable rời composition.
- `DisposableEffect`: dùng khi cần cleanup (unregister listener, release resource). Block `onDispose` được gọi khi key đổi hoặc composable rời composition.
- `SideEffect`: không có coroutine, chạy synchronous sau mỗi successful recomposition. Dùng cho log analytics, sync state với non-Compose code.

### Q40. Compose performance — best practices?

- **State hoisting**: nâng state lên **thấp nhất cần thiết** (gần nơi đọc nhất), nhưng **cao nhất 1 nơi dùng chung** → giảm phạm vi recompose.
- **Stable types**: dùng `@Immutable` cho data class, `ImmutableList` cho list → enable skipping.
- **Lambda stability**: lambda capture biến không stable → mỗi recompose tạo lambda mới → child recompose theo. Fix bằng `remember`: `val onClick = remember(id) { { vm.click(id) } }`.
- **key trong LazyColumn**: cung cấp unique key cho mỗi item → Compose track item đúng khi reorder/remove, tránh recompose toàn list.
- **derivedStateOf**: cho computed state — chỉ trigger recompose khi **kết quả tính toán** thay đổi, không phải khi mỗi input thay đổi. Ví dụ: `val showButton = derivedStateOf { scrollState.firstVisibleItemIndex > 0 }` — chỉ recompose khi boolean đổi, không phải khi scroll mỗi pixel.
- **Defer reads**: truyền `() -> T` (lambda) thay vì `T` trực tiếp → composable đọc giá trị ở phase cuối (draw), tránh recompose ở composition phase.

### Q41. CompositionLocal — khi nào dùng?

Truyền data **ngầm** xuống deep tree mà không phải khai báo param qua từng cấp composable (tương tự React Context / Provider).

Built-in hay dùng: `LocalContext` (Android Context), `LocalDensity` (dp/px conversion), `LocalLifecycleOwner` (bind side effect vào lifecycle), `LocalConfiguration` (orientation, screenSize).

Tạo custom:
```kotlin
val LocalUser = compositionLocalOf<User?> { null }  // default value

CompositionLocalProvider(LocalUser provides currentUser) {
    // mọi composable bên trong đều đọc được LocalUser.current
}
```

**Đúng**: data toàn cục trong scope tree — theme, locale, current user, navigation controller.
**Sai**: data 1 màn hình cụ thể — pass param explicit dễ trace flow data, dễ debug, dễ test hơn nhiều.

Trade-off: `CompositionLocal` tạo coupling ngầm — khó biết composable phụ thuộc gì khi nhìn vào signature. Đừng abuse.

---

## 6. Architecture (MVVM / MVI / Clean)

### Q42. MVVM vs MVI?

|               | MVVM                      | MVI                                |
| ------------- | ------------------------- | ---------------------------------- |
| State         | Nhiều StateFlow riêng lẻ  | 1 immutable UiState duy nhất       |
| Sự kiện       | Gọi method trên ViewModel | Gửi Intent/Action vào ViewModel    |
| Luồng dữ liệu | 2 chiều (binding)         | 1 chiều (unidirectional data flow) |
| Debug         | State thay đổi rời rạc    | Mọi thay đổi qua reducer → log dễ  |

**MVVM**: mỗi piece of state là 1 `StateFlow`/`LiveData` riêng. View gọi method trực tiếp trên ViewModel. Đơn giản, phù hợp form đơn giản, ít state.

**MVI**: toàn bộ UI state gói trong 1 immutable data class. View gửi `Intent` (sealed class mô tả hành động), ViewModel xử lý qua `reducer` (pure function: oldState + intent → newState). Mọi thay đổi state đi qua 1 chỗ → dễ debug, dễ log, dễ time-travel. Phù hợp UI phức tạp, nhiều state phụ thuộc nhau.

### Q43. Clean Architecture — 3 layer + data flow?

```
┌─────────────────────────────────────┐
│ Presentation (UI + ViewModel)       │  ← Compose/Activity
├─────────────────────────────────────┤
│ Domain (UseCase + Entity)           │  ← Pure Kotlin, không Android
├─────────────────────────────────────┤
│ Data (Repository impl + DataSource) │  ← Room, Retrofit
└─────────────────────────────────────┘
```

**Dependency Rule**: chiều phụ thuộc **hướng vào trong**. Domain là lõi, không biết gì về Data hay Presentation. Data implement interface do Domain định nghĩa (Dependency Inversion). Presentation gọi UseCase của Domain.

Flow data thực tế (1 click → kết quả): User click "Refresh" → ViewModel gọi `GetUsersUseCase()` → UseCase gọi `UserRepository.getUsers()` (interface ở Domain) → `UserRepositoryImpl` (ở Data) gọi Retrofit API → JSON → map thành Entity → ViewModel cập nhật `_uiState` → Compose recompose → render danh sách.

Tại sao chia? Test Domain không cần Android framework. Đổi Room → SQLDelight, REST → GraphQL chỉ sửa Data layer, Domain và Presentation không động.

### Q44. Repository pattern — vai trò?

Repository **trừu tượng hoá data source** — UseCase/ViewModel gọi repository qua interface, không biết và không quan tâm data đến từ network, local cache, hay in-memory.

Repository quyết định **chiến lược data**: check cache trước → nếu hết hạn gọi API → lưu DB → trả về. Mai mốt thêm logic sync offline, thêm CDN fallback → chỉ sửa Repository impl, không động logic nghiệp vụ.

### Q45. UseCase — có cần không?

**Pro**: tái sử dụng business logic giữa nhiều ViewModel (ví dụ `ValidateEmailUseCase` dùng ở cả Register và Profile); tách Domain layer rõ ràng; dễ unit test (pure function).

**Con**: boilerplate nếu UseCase chỉ 1 dòng chuyển tiếp `return repo.getUsers()`.

**Quy tắc**: dùng UseCase khi có **logic thực sự** (combine nhiều repo, validation, transformation, business rule). Đừng tạo UseCase chỉ để wrap 1 lời gọi repo — đó là over-engineering.

### Q46. Hilt vs Koin?

|                  | Hilt                        | Koin             |
| ---------------- | --------------------------- | ---------------- |
| Cơ chế           | Code-gen compile-time       | Runtime DSL      |
| Khi sai cấu hình | Compile error               | Runtime crash    |
| Tích hợp Android | Sâu (HiltViewModel, Worker) | Cần plugin riêng |
| KMP              | Không                       | Có               |

**Hilt** (Google): dùng annotation (`@HiltAndroidApp`, `@Inject`, `@HiltViewModel`) → KAPT/KSP sinh code lúc compile. Sai cấu hình → compile fail (phát hiện sớm). Build chậm hơn do code-gen nhưng runtime overhead gần như 0. Backed by Google → mainstream cho Android thuần.

**Koin**: pure Kotlin DSL (`module { single { ... } }`), không annotation processing. Build nhanh hơn, runtime có chút overhead (lookup map). Sai cấu hình → crash lúc chạy (phát hiện muộn). Hỗ trợ **KMP** — dùng chung module cho Android + iOS.

Chọn Hilt cho dự án lớn, team đông, ưu tiên type-safe. Chọn Koin cho dự án vừa/nhỏ, KMP, hoặc không muốn KAPT/KSP.

---

## 7. Modularization

### Q47. Vì sao modular hoá?

- **Tốc độ build**: Gradle incremental build chỉ rebuild module thay đổi → build nhanh hơn nhiều so với monolith.
- **Tái sử dụng**: feature module có thể dùng lại ở app khác hoặc chia sẻ qua internal library.
- **Ranh giới rõ ràng**: `internal` visibility modifier giới hạn trong module → module này không thấy implementation detail của module khác → giảm coupling.
- **Làm việc song song**: team chia nhau theo module, ít conflict, review dễ.
- **Dynamic feature** (Play Feature Delivery): tải module on-demand, giảm kích thước APK ban đầu.

### Q48. Cách chia module phổ biến?

Chia theo **layer + feature**:

```
:app                — lắp ráp, DI, navigation graph
:core:ui            — design system, theme, common composable
:core:network       — Retrofit setup, interceptor, base response
:core:database      — Room setup, DAO base
:core:common        — utils, extensions, constants
:feature:home       — UI + ViewModel + UseCase riêng cho Home
:feature:profile    — UI + ViewModel + UseCase riêng cho Profile
:domain             — entity, repository interface (optional, tuỳ scale)
```

Quy tắc:
- `:feature` **không phụ thuộc** `:feature` khác → giao tiếp qua `:app` hoặc navigation. Nếu 2 feature cần share logic → extract ra `:core`.
- `:core` **không phụ thuộc** `:feature` → core là nền tảng, feature xây trên.
- `:app` lắp ráp tất cả — DI graph, navigation, entry point.

### Q49. api vs implementation trong Gradle?

- **implementation**: dependency chỉ visible trong module hiện tại. Module khác phụ thuộc module này **không thấy** transitively. Build nhanh hơn vì Gradle biết phạm vi ảnh hưởng nhỏ.
- **api**: dependency được **expose** ra ngoài → module phụ thuộc cũng thấy. Sửa dependency này → Gradle phải rebuild cả module phụ thuộc.

> Best practice: dùng `implementation` mặc định. Chỉ dùng `api` khi type từ dependency xuất hiện trong **public API** của module (return type, parameter type, inheritance).

---

## 8. Design Patterns trong Android

### Q50. Singleton — đúng cách trong Kotlin?

Kotlin cung cấp `object` declaration — thread-safe singleton được compiler đảm bảo. Tuy nhiên, dùng DI với `@Singleton` scope là **preferred** vì: test dễ thay thế (inject mock), lifecycle do DI container quản lý, tránh global mutable state khó debug.

### Q51. Factory pattern — khi nào dùng?

Khi việc tạo object **phức tạp** (nhiều bước setup, cần logic chọn implementation) hoặc cần **chọn impl runtime** dựa trên input.

Trong Android: `ViewModelProvider.Factory` (tạo ViewModel với custom constructor), `WorkerFactory` (tạo Worker với custom dependency), `Retrofit.Builder` (kết hợp Factory + Builder).

### Q52. Observer pattern — implement bằng Flow?

`StateFlow`/`SharedFlow` chính là Observer pattern hiện đại, tích hợp sẵn trong Kotlin:
- **Subject** = Flow (nguồn phát)
- **Observer** = collector (người nhận)
- `emit()` = `notifyObservers()`

Lợi thế so với tự viết `addListener`/`removeListener`: Flow tự cleanup khi scope cancel → không lo quên unregister → không memory leak. Kết hợp với `repeatOnLifecycle` → lifecycle-aware.

### Q53. Strategy pattern — ví dụ?

Đóng gói behavior vào object riêng, swap object để **thay đổi behavior runtime** mà không sửa class sử dụng.

Trong Android:
- `OkHttp Interceptor`: mỗi interceptor là 1 strategy xử lý request (logging, auth, retry). Thêm/bỏ interceptor = thay đổi pipeline.
- `RecyclerView.ItemAnimator`: swap animator khác nhau để đổi animation style.
- `DiffUtil.ItemCallback`: strategy so sánh item cho `ListAdapter`.

### Q54. Builder pattern — Kotlin có cần không?

Kotlin có **named arguments + default parameters** → thay thế Builder trong đa số trường hợp vì code gọn hơn nhiều, compiler kiểm tra type, không có vấn đề "quên gọi build()".

Builder vẫn dùng khi: Java interop (Java không có named argument), build phức tạp cần **validation từng bước** (ví dụ `OkHttpClient.Builder()` validate config trước khi tạo client), hoặc API cho library user (Builder pattern quen thuộc hơn với Java developer).

---

## 9. Điểm cộng: KMP, DSA cơ bản

### Q55. Kotlin Multiplatform — share gì, không share gì?

**Share (commonMain)** — code chạy được trên mọi platform:
- Domain models, Entity, UseCase, business logic, validation
- Repository interface + implementation (nếu dùng KMP library)
- Network: Ktor (thay Retrofit), Serialization: kotlinx.serialization
- Database: SQLDelight (thay Room)
- Utility, extension functions

**Platform-specific (androidMain / iosMain)** — code cần native API:
- UI: Compose (Android) vs SwiftUI / Compose Multiplatform (iOS)
- Platform API: notification, biometric, camera, file system
- DI container setup (Koin module riêng cho mỗi platform)

Dùng `expect`/`actual` để khai báo API chung ở common, implement riêng ở mỗi platform.

### Q56. Big-O — cheatsheet?

| Big-O      | Tên            | Ví dụ              |
| ---------- | -------------- | ------------------ |
| O(1)       | Hằng số        | HashMap get/put    |
| O(log n)   | Logarit        | Binary search      |
| O(n)       | Tuyến tính     | Duyệt array        |
| O(n log n) | Tuyến tính-log | Merge sort         |
| O(n^2)     | Bậc 2          | Vòng lặp lồng nhau |

Quy tắc phân tích: bỏ hằng số (`2n` → `n`), giữ số hạng cao nhất (`n^2 + n` → `n^2`), mặc định phân tích **worst-case** trừ khi đề bài nói rõ.

### Q57. Pattern DSA hay gặp?

- **Two pointers**: array đã sort, tìm cặp/triplet thoả điều kiện. Ví dụ: Two Sum II, 3Sum, Container With Most Water.
- **Sliding window**: tìm substring/subarray thoả điều kiện với độ dài cố định hoặc biến đổi. Ví dụ: Longest Substring Without Repeating, Maximum Sum Subarray of Size K.
- **HashMap counting**: đếm tần suất, kiểm tra anagram, Two Sum O(n). Dùng khi cần lookup nhanh.
- **BFS/DFS**: duyệt tree, graph, matrix. BFS cho shortest path (unweighted), DFS cho explore toàn bộ.
- **Binary search**: array sorted hoặc search-space monotonic. Biến thể: search insert position, find peak element.
- **Dynamic programming**: khi bài toán có **overlapping subproblems** và **optimal substructure**. Ví dụ: Climbing Stairs, Coin Change, Longest Common Subsequence.

---

## Cheatsheet cuối — câu trả lời 1 dòng

| Câu hỏi                        | Trả lời                                                         |
| ------------------------------ | --------------------------------------------------------------- |
| val vs var                     | Tham chiếu bất biến vs khả biến                                 |
| data class tự sinh gì?         | equals, hashCode, toString, copy, componentN (chỉ primary ctor) |
| Sealed dùng làm gì?            | Đóng kín hierarchy, when exhaustive, UI state                   |
| inline để làm gì?              | Bỏ lambda allocation + reified generics                         |
| SOLID — D nghĩa là?            | Phụ thuộc abstraction (interface), không phải impl              |
| Activity lifecycle?            | onCreate→onStart→onResume→onPause→onStop→onDestroy              |
| Fragment + View lifecycle?     | Khác nhau — observer phải dùng viewLifecycleOwner               |
| ViewModel sống qua rotate nhờ? | ViewModelStore qua NonConfigurationInstances                    |
| SavedStateHandle để làm gì?    | Sống sót process death (Bundle)                                 |
| Memory leak phổ biến?          | Static context, inner class, listener, GlobalScope              |
| Ngưỡng ANR?                    | Input 5s, Receiver 10s/60s, Service 20s/200s                    |
| Coroutine vs Thread?           | N:M, rẻ, suspend không block                                    |
| launch vs async?               | Bắn-và-quên vs Deferred (await)                                 |
| Structured concurrency?        | Scope huỷ → con huỷ                                             |
| StateFlow vs SharedFlow?       | State (1 latest) vs Events (replay tuỳ chỉnh)                   |
| Cold vs Hot Flow?              | Theo collector vs chia sẻ chung                                 |
| @Stable vs @Immutable?         | Stable: báo Compose khi đổi. Immutable: không bao giờ đổi       |
| MVI vs MVVM?                   | 1 immutable state vs nhiều state                                |
| Clean Arch dependency rule?    | Phụ thuộc hướng vào trong (Domain trong cùng)                   |
| Hilt vs Koin?                  | Hilt code-gen type-safe; Koin runtime DSL, hỗ trợ KMP           |
| api vs implementation?         | Transitive vs không transitive                                  |
