# Ô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. Thread-safe nghĩa là gì? synchronized hoạt động thế nào?

**Thread-safe** nghĩa là code/class hoạt động **đúng đắn** khi nhiều thread truy cập đồng thời, không cần caller phải tự xử lý đồng bộ bên ngoài. Ngược lại, code không thread-safe có thể gây race condition (kết quả phụ thuộc thứ tự chạy), data corruption, hoặc crash khi dùng từ nhiều thread.

Ví dụ **không** thread-safe: 2 thread cùng `counter++` → cả hai đọc counter = 0, cả hai ghi counter = 1, mất 1 lần tăng. Đây là race condition kinh điển vì `counter++` không phải atomic — nó gồm 3 bước: đọc → tăng → ghi.

**synchronized** là cơ chế **khoá** (lock/monitor) của JVM: chỉ 1 thread được vào block synchronized tại 1 thời điểm, các thread khác phải **chờ** (block) cho đến khi thread kia ra khỏi block.

```kotlin
// synchronized block — khoá trên object cụ thể
val lock = Any()
fun increment() {
    synchronized(lock) {
        counter++  // chỉ 1 thread chạy đoạn này tại 1 thời điểm
    }
}

// @Synchronized annotation — khoá trên this (tương đương synchronized(this))
class Counter {
    private var count = 0

    @Synchronized
    fun increment() { count++ }

    @Synchronized
    fun get(): Int = count
}
```

**@Volatile**: đảm bảo mọi thread luôn đọc giá trị **mới nhất** từ main memory, không đọc bản cache cũ trong CPU register/cache line. Tuy nhiên `@Volatile` chỉ đủ cho **đọc/ghi đơn lẻ** (set flag, publish reference), **KHÔNG đủ** cho compound operation (read-modify-write như `counter++`).

```kotlin
@Volatile
var stopped = false  // thread A ghi true → thread B thấy ngay, không đọc bản cũ
```

**Khi nào dùng gì?**

| Tình huống                                     | Dùng                                        |
| ---------------------------------------------- | ------------------------------------------- |
| Cờ on/off, publish reference giữa các thread   | `@Volatile`                                 |
| Compound operation (counter++, check-then-act) | `synchronized` hoặc `Atomic`                |
| Primitive counter cần atomic                   | `AtomicInteger`, `AtomicLong`               |
| Trong coroutine / suspend function             | `Mutex` (KHÔNG dùng synchronized)           |
| Toàn bộ collection cần thread-safe             | `ConcurrentHashMap`, `CopyOnWriteArrayList` |

**Tại sao KHÔNG dùng synchronized trong coroutine?**

`synchronized` **block thread** — thread bị giữ cứng, không thể giải phóng cho coroutine khác. Tệ hơn, coroutine có thể suspend giữa chừng rồi resume trên **thread khác** → vi phạm quy tắc "cùng thread phải giữ và nhả lock". Kết quả: deadlock hoặc mutual exclusion bị phá vỡ.

→ Trong coroutine, dùng `Mutex` — suspend (giải phóng thread) thay vì block (giữ thread), tương thích hoàn toàn với structured concurrency.

### Q23. 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

### Q24. 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.

### Q25. 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.

### Q26. 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.

### Q27. 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`.

### Q28. 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.

### Q29. 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ỷ.

### Q30. 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`.

### Q31. 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.

### Q32. 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.

### Q33. 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).

### Q34. 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)
    }
```

### Q35. 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) }`.

### Q36. 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

### Q37. 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.

### Q38. 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.

### Q39. 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`.

### Q40. 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.

### Q41. 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.

### Q42. 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)

### Q43. 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.

### Q44. 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.

### Q45. 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ụ.

### Q46. 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.

### Q47. Hilt vs Koin — cơ chế, ưu nhược, khi nào chọn cái nào?

**Dependency Injection (DI) là gì?** Thay vì class tự tạo dependency bên trong (`val repo = UserRepositoryImpl()`), dependency được **truyền từ bên ngoài vào** (qua constructor, setter, hoặc DI framework). Lợi ích: loose coupling, dễ test (inject mock), dễ thay đổi implementation. DI framework tự động hoá quá trình này — developer khai báo "cần gì" và "cung cấp gì", framework lo việc kết nối.

**Bản chất khác nhau hoàn toàn**:
- **Hilt**: dựa trên **Dagger** (Google), dùng **annotation processing** → KAPT/KSP đọc annotation lúc compile, **sinh Java/Kotlin code** thật sự chứa logic inject. Runtime chỉ gọi code đã sinh sẵn → overhead gần như 0.
- **Koin**: **pure Kotlin DSL**, không annotation processing, không code-gen. Khai báo dependency bằng lambda trong `module { }` block. Runtime dùng **HashMap lookup** để resolve dependency khi được yêu cầu → có chút overhead.

|                        | Hilt                                    | Koin                                |
| ---------------------- | --------------------------------------- | ----------------------------------- |
| Cơ chế                 | Annotation → code-gen compile-time      | Kotlin DSL → runtime resolution     |
| Nền tảng               | Dagger 2 (Google)                       | Pure Kotlin (Insert-Koin)           |
| Khi sai cấu hình       | **Compile error** (phát hiện sớm)       | **Runtime crash** (phát hiện muộn)  |
| Build time             | Chậm hơn (KAPT/KSP code-gen)           | Nhanh hơn (không code-gen)          |
| Runtime performance    | Gần 0 overhead (code sinh sẵn)          | Nhỏ (HashMap lookup mỗi lần inject) |
| Tích hợp Android       | Sâu (`@HiltViewModel`, `@AndroidEntryPoint`, WorkManager) | Cần thêm `koin-android`, `koin-compose` |
| Scoping                | `@Singleton`, `@ViewModelScoped`, `@ActivityScoped` | `single { }`, `viewModel { }`, `scope { }` |
| KMP                    | **Không**                               | **Có** — share DI module cross-platform |
| Learning curve         | Cao (annotation, component, module, scope) | Thấp (Kotlin DSL quen thuộc)      |
| Testing                | `@HiltAndroidTest`, `@UninstallModules` | `checkModules()`, swap trực tiếp    |
| Google recommend?      | **Có** — official Jetpack               | Không — third-party                 |

---

**Hilt — setup + code ví dụ**:

```kotlin
// 1. Application — khởi tạo Hilt
@HiltAndroidApp
class MyApp : Application()

// 2. Module — khai báo "cung cấp gì"
@Module
@InstallIn(SingletonComponent::class)  // scope: toàn app
object NetworkModule {
    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit =
        Retrofit.Builder()
            .baseUrl("https://api.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()

    @Provides
    @Singleton
    fun provideUserApi(retrofit: Retrofit): UserApi =
        retrofit.create(UserApi::class.java)
}

// 3. Repository — inject qua constructor
@Singleton
class UserRepositoryImpl @Inject constructor(
    private val api: UserApi,
    private val db: UserDao
) : UserRepository {
    override suspend fun getUsers() = api.getUsers().map { it.toEntity() }
}

// 4. Bind interface → implementation
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
    @Binds
    abstract fun bindUserRepo(impl: UserRepositoryImpl): UserRepository
}

// 5. ViewModel — Hilt tự inject
@HiltViewModel
class UserViewModel @Inject constructor(
    private val repo: UserRepository
) : ViewModel() {
    val users = repo.observeUsers().stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
}

// 6. Activity/Fragment — nhận inject
@AndroidEntryPoint
class UserActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()  // Hilt tự tạo + inject
}
```

**Hilt — ưu điểm chi tiết**:
- **Compile-time safety**: quên provide dependency → build fail ngay, không đợi đến runtime mới biết. Với dự án lớn (50+ module, 200+ dependency), đây là lợi thế cực lớn.
- **Scoping rõ ràng**: `@Singleton` (app lifetime), `@ActivityScoped`, `@ViewModelScoped`, `@FragmentScoped` — Hilt tự quản lý lifecycle, tự cleanup khi scope kết thúc.
- **`@HiltViewModel`**: tích hợp seamless với `by viewModels()` — không cần custom `ViewModelProvider.Factory`.
- **`@AndroidEntryPoint`**: inject vào Activity, Fragment, Service, BroadcastReceiver, View — cover hết Android component.
- **WorkManager integration**: `@HiltWorker` — inject dependency vào Worker dễ dàng.
- **Testing**: `@HiltAndroidTest` + `@UninstallModules` → thay module production bằng module test trong integration test.

**Hilt — nhược điểm**:
- **Build time**: KAPT chậm (deprecated dần), KSP nhanh hơn nhưng vẫn tốn thời gian code-gen. Project nhỏ cảm nhận rõ.
- **Learning curve cao**: phải hiểu Dagger concepts (Component, Module, Scope, Qualifier, Binds vs Provides). Error message từ Dagger đôi khi khó hiểu.
- **Boilerplate**: mỗi dependency cần annotation, module class, install-in annotation. Simple project cảm thấy nặng nề.
- **Không hỗ trợ KMP** — chỉ Android/JVM.

---

**Koin — setup + code ví dụ**:

```kotlin
// 1. Khai báo module — Kotlin DSL thuần
val networkModule = module {
    single {
        Retrofit.Builder()
            .baseUrl("https://api.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
    single { get<Retrofit>().create(UserApi::class.java) }
}

val repositoryModule = module {
    single<UserRepository> { UserRepositoryImpl(api = get(), db = get()) }
}

val viewModelModule = module {
    viewModel { UserViewModel(repo = get()) }
}

// 2. Khởi tạo trong Application
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@MyApp)
            modules(networkModule, repositoryModule, viewModelModule)
        }
    }
}

// 3. Repository — constructor bình thường, không cần annotation
class UserRepositoryImpl(
    private val api: UserApi,
    private val db: UserDao
) : UserRepository {
    override suspend fun getUsers() = api.getUsers().map { it.toEntity() }
}

// 4. ViewModel — constructor bình thường
class UserViewModel(
    private val repo: UserRepository
) : ViewModel() {
    val users = repo.observeUsers().stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
}

// 5. Activity/Fragment — inject bằng delegate
class UserActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModel()  // Koin delegate
}

// 6. Compose — inject trực tiếp
@Composable
fun UserScreen(viewModel: UserViewModel = koinViewModel()) {
    val users by viewModel.users.collectAsStateWithLifecycle()
}
```

**Koin — ưu điểm chi tiết**:
- **Setup nhanh**: không KAPT/KSP, không annotation, không code-gen → build nhanh hơn, đặc biệt với project vừa/nhỏ.
- **Kotlin DSL tự nhiên**: `single { }`, `factory { }`, `viewModel { }` — đọc hiểu ngay, không cần học Dagger concepts.
- **KMP native**: cùng module khai báo dùng chung cho Android + iOS + Desktop. Đây là lý do chính chọn Koin khi làm KMP.
- **Flexible**: không bị ràng buộc bởi annotation → dễ tạo dynamic module, conditional provide, module override trong test.
- **`checkModules()`**: verify DI graph trong unit test — phần nào bù đắp việc thiếu compile-time check.
- **Compose integration**: `koinViewModel()` seamless trong Composable function.

**Koin — nhược điểm**:
- **Runtime crash**: quên khai báo dependency → crash khi inject lần đầu (thường là khi user mở màn hình), không phải lúc build. Với dự án lớn, rủi ro miss dependency rất cao.
- **Runtime overhead**: mỗi lần inject = HashMap lookup + lambda invocation. Với `factory { }` (tạo mới mỗi lần), overhead tích luỹ. Thực tế: overhead rất nhỏ, hiếm khi là bottleneck.
- **Không có scope lifecycle tự động** như Hilt: phải tự quản lý scope creation/destruction bằng `createScope()`/`closeScope()`. Dễ quên cleanup → leak.
- **Google không recommend** — documentation/tutorial chính thống đều dùng Hilt.

---

**So sánh code cùng 1 use case**:

```kotlin
// === HILT ===
// Cần: @Module, @InstallIn, @Provides/@Binds, @Inject, @HiltViewModel, @AndroidEntryPoint
// 6 annotation + module class → compile-time safe

// === KOIN ===
// Cần: module { }, single/factory/viewModel { }, startKoin { }, by viewModel()
// 0 annotation, pure Kotlin DSL → runtime resolution
```

**Khi nào chọn cái nào?**

| Tình huống                                    | Chọn      |
| --------------------------------------------- | --------- |
| Dự án lớn, team đông, nhiều module            | **Hilt** (compile-time safety cực quan trọng) |
| Dự án nhỏ/vừa, prototype nhanh               | **Koin** (setup nhanh, ít boilerplate) |
| KMP — share code Android + iOS               | **Koin** (Hilt không hỗ trợ KMP) |
| Team mới với DI, chưa biết Dagger            | **Koin** (learning curve thấp hơn nhiều) |
| Cần tích hợp sâu Jetpack (WorkManager, Nav)  | **Hilt** (first-class support từ Google) |
| Ưu tiên build time, CI/CD nhanh              | **Koin** (không code-gen) |
| Cần đảm bảo không crash runtime do DI        | **Hilt** (compile error > runtime crash) |
| Dự án đang dùng Dagger 2, muốn migrate       | **Hilt** (Hilt = Dagger + Android simplification) |

> **Tóm gọn cho phỏng vấn**: Hilt = Dagger-based, compile-time code-gen, type-safe, Google official, chỉ Android. Koin = pure Kotlin DSL, runtime resolution, nhanh setup, hỗ trợ KMP. Chọn Hilt khi dự án lớn cần compile-time safety + Jetpack ecosystem. Chọn Koin khi cần KMP hoặc dự án nhỏ muốn setup nhanh. Cả hai đều giải quyết cùng bài toán DI — chỉ khác approach.

---

## 7. Modularization

### Q48. 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.

### Q49. 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.

### Q50. 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

> **Nền tảng**: Design pattern là giải pháp đã được chứng minh cho các bài toán thiết kế lặp đi lặp lại. GoF (Gang of Four) chia thành 3 nhóm: **Creational** (tạo object), **Structural** (tổ chức class/object), **Behavioral** (giao tiếp giữa object). Trong phỏng vấn Android, không cần nhớ hết 23 pattern — chỉ cần nắm vững những pattern thường gặp trong codebase Android thực tế.

---

### Creational Patterns — Tạo object

### Q51. Singleton — đảm bảo chỉ 1 instance duy nhất

**Ý tưởng**: Đảm bảo 1 class chỉ có **đúng 1 instance** trong toàn bộ ứng dụng, và cung cấp điểm truy cập toàn cục đến instance đó.

**Khi nào dùng**: Quản lý tài nguyên dùng chung (database connection, network client, cache, logging), đảm bảo consistency (config, preference manager).

**Cấu trúc**:
- Constructor phải `private` → bên ngoài không `new` được.
- Class tự quản lý instance duy nhất.
- Cung cấp static method/property để lấy instance.

**Trong Kotlin**: `object` declaration — compiler đảm bảo thread-safe, lazy-init (init lần đầu access), chỉ 1 instance:

```kotlin
// Kotlin object = thread-safe singleton, compiler sinh code đảm bảo
object Analytics {
    fun track(event: String) { /* ... */ }
}
Analytics.track("click_buy")  // truy cập trực tiếp, không cần getInstance()
```

**Trong Android thực tế**: dùng DI với `@Singleton` scope là **preferred** hơn `object` vì:
- **Testable**: inject mock/fake dễ dàng, `object` thì không thay thế được.
- **Lifecycle rõ ràng**: DI container quản lý lifecycle, `object` sống mãi đến khi process chết.
- **Tránh global mutable state**: `object` dễ bị truy cập từ mọi nơi, khó trace data flow.

```kotlin
// Preferred: DI quản lý singleton
@Singleton
class AnalyticsTracker @Inject constructor(
    private val api: AnalyticsApi
) {
    fun track(event: String) { /* ... */ }
}
```

**Bẫy**: Singleton giữ reference đến Activity/Context → memory leak. Luôn dùng `applicationContext` nếu Singleton cần Context.

### Q52. Factory Method — uỷ quyền việc tạo object cho subclass

**Ý tưởng**: Định nghĩa **interface** cho việc tạo object, nhưng để **subclass quyết định** tạo class cụ thể nào. Client code gọi factory method mà không biết class thật sự được tạo — chỉ biết interface/abstract type.

**Khi nào dùng**: Khi class không biết trước chính xác object nào sẽ được tạo; khi muốn subclass mở rộng bộ sản phẩm mà không sửa code cũ.

**Cấu trúc**:
- **Creator** (abstract class/interface): khai báo factory method trả về `Product`.
- **ConcreteCreator**: override factory method, trả về `ConcreteProduct` cụ thể.
- **Product** (interface): type chung mà client code sử dụng.
- **ConcreteProduct**: implementation cụ thể.

```kotlin
// Product interface
interface Notification {
    fun send(message: String)
}

// Concrete products
class PushNotification : Notification {
    override fun send(message: String) { /* FCM push */ }
}
class EmailNotification : Notification {
    override fun send(message: String) { /* SMTP email */ }
}

// Factory method — client không cần biết class cụ thể
fun createNotification(type: String): Notification = when (type) {
    "push" -> PushNotification()
    "email" -> EmailNotification()
    else -> throw IllegalArgumentException("Unknown type: $type")
}
```

**Trong Android**:
- `ViewModelProvider.Factory` — tạo ViewModel với custom constructor. `ViewModelProvider` gọi `create()` mà không biết ViewModel cụ thể nào.
- `WorkerFactory` — tạo `ListenableWorker` với custom dependency injection.
- `Fragment.instantiate()` / `FragmentFactory` — tạo Fragment với custom constructor.

**Biến thể — Abstract Factory**: factory tạo ra **họ object liên quan** (ví dụ: `LightThemeFactory` tạo `LightButton` + `LightCard`, `DarkThemeFactory` tạo `DarkButton` + `DarkCard`). Ít gặp trực tiếp trong Android vì Compose theme system xử lý việc này.

### Q53. Builder — xây dựng object phức tạp từng bước

**Ý tưởng**: Tách quá trình **xây dựng** object phức tạp ra khỏi **biểu diễn** của nó. Cho phép cùng 1 quy trình xây dựng tạo ra các biểu diễn khác nhau. Client config từng bước, gọi `build()` khi xong.

**Khi nào dùng**: Object có nhiều parameter (đặc biệt nhiều optional), cần validation trước khi tạo, hoặc quá trình tạo gồm nhiều bước.

**Cấu trúc**:
- **Builder**: class chứa các setter/method config, mỗi method return `this` (fluent API).
- **Product**: object phức tạp cần tạo.
- **Director** (optional): định nghĩa thứ tự gọi builder methods.

```kotlin
// Builder trong Android — OkHttpClient
val client = OkHttpClient.Builder()
    .connectTimeout(30, TimeUnit.SECONDS)
    .addInterceptor(loggingInterceptor)     // bước 1
    .addInterceptor(authInterceptor)        // bước 2
    .cache(Cache(cacheDir, 10 * 1024 * 1024)) // bước 3
    .build()                                  // validate + tạo object
```

**Trong Android**: `Notification.Builder`, `AlertDialog.Builder`, `OkHttpClient.Builder`, `Retrofit.Builder`, `Room.databaseBuilder()`.

**Kotlin có cần Builder không?** Kotlin có **named arguments + default parameters** → thay thế Builder cho đa số case đơn giản:

```kotlin
// Không cần Builder — named args đủ rõ ràng
data class ServerConfig(
    val host: String,
    val port: Int = 8080,
    val ssl: Boolean = true,
    val timeout: Long = 30_000
)
val config = ServerConfig(host = "api.com", ssl = false)
```

Builder vẫn cần khi: Java interop (Java không có named argument), validation từng bước phức tạp, hoặc API cho library user.

---

### Structural Patterns — Tổ chức class/object

### Q54. Adapter — chuyển đổi interface không tương thích

**Ý tưởng**: Chuyển đổi interface của 1 class thành interface khác mà client mong đợi. Adapter cho phép 2 class **không tương thích interface** làm việc cùng nhau — đóng vai trò "phiên dịch" giữa 2 bên.

**Khi nào dùng**: Muốn dùng class có sẵn nhưng interface không khớp với code hiện tại; tích hợp library bên thứ 3; wrap legacy code.

**Cấu trúc**:
- **Target**: interface mà client mong đợi.
- **Adaptee**: class có sẵn với interface không tương thích.
- **Adapter**: implement Target, bên trong giữ reference đến Adaptee, chuyển đổi lời gọi.

```kotlin
// Adaptee — API trả về DTO format
data class UserDto(val user_name: String, val user_email: String)

// Target — UI cần domain model
data class User(val name: String, val email: String)

// Adapter — chuyển đổi DTO → Domain
fun UserDto.toUser() = User(name = user_name, email = user_email)
```

**Trong Android**:
- **RecyclerView.Adapter**: đây chính là Adapter pattern kinh điển nhất trong Android. Data (List) không tương thích trực tiếp với RecyclerView → Adapter chuyển đổi data thành ViewHolder mà RecyclerView hiểu.
- **ListAdapter / PagingDataAdapter**: biến thể của Adapter kết hợp DiffUtil.
- **DTO → Domain mapping** trong Data layer: `UserResponse.toEntity()`, `ApiError.toDomainError()`.

```kotlin
// RecyclerView.Adapter — chuyển List<User> thành các ViewHolder
class UserAdapter : ListAdapter<User, UserViewHolder>(UserDiffCallback()) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
        // tạo ViewHolder — "khung" hiển thị
    }
    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        holder.bind(getItem(position))  // "dịch" User thành UI
    }
}
```

### Q55. Decorator — thêm behavior mà không sửa class gốc

**Ý tưởng**: Gắn thêm **trách nhiệm mới** vào object **động** (runtime), bằng cách wrap object gốc trong object decorator. Decorator implement cùng interface với object gốc → client không phân biệt được.

**Khác Inheritance ở chỗ**: Inheritance thêm behavior **tĩnh** (compile-time), cho tất cả instance. Decorator thêm behavior **động** (runtime), cho từng instance riêng, có thể xếp chồng nhiều decorator.

**Khi nào dùng**: Khi cần thêm behavior mà không muốn tạo subclass; khi muốn tổ hợp nhiều behavior linh hoạt; khi class gốc không cho phép kế thừa (final).

**Cấu trúc**:
- **Component** (interface): contract chung.
- **ConcreteComponent**: object gốc.
- **Decorator** (abstract): implement Component, giữ reference đến Component bên trong.
- **ConcreteDecorator**: thêm behavior trước/sau khi delegate cho Component bên trong.

```kotlin
// Component interface
interface DataSource {
    fun readData(): String
}

// ConcreteComponent
class FileDataSource(private val filename: String) : DataSource {
    override fun readData(): String = File(filename).readText()
}

// Decorator — thêm logging mà không sửa FileDataSource
class LoggingDataSource(private val wrapped: DataSource) : DataSource {
    override fun readData(): String {
        Log.d("DataSource", "Reading data...")
        val data = wrapped.readData()  // delegate cho object gốc
        Log.d("DataSource", "Read ${data.length} chars")
        return data
    }
}

// Xếp chồng decorator
val source = LoggingDataSource(EncryptionDataSource(FileDataSource("data.txt")))
```

**Trong Android**:
- **OkHttp Interceptor Chain**: mỗi interceptor wrap request/response, thêm behavior (logging, auth header, retry, caching) rồi delegate cho interceptor tiếp theo. Đây là Decorator + Chain of Responsibility.
- **InputStream wrapping**: `BufferedInputStream(FileInputStream(file))` — thêm buffering mà không sửa FileInputStream.
- **Context wrapper**: `ContextThemeWrapper` wrap `Context` gốc, override theme.

### Q56. Facade — giao diện đơn giản cho hệ thống phức tạp

**Ý tưởng**: Cung cấp **1 interface đơn giản** che giấu sự phức tạp của hệ thống con bên dưới. Client chỉ giao tiếp với Facade thay vì phải biết và phối hợp nhiều class phức tạp.

**Khi nào dùng**: Khi hệ thống có nhiều class phụ thuộc lẫn nhau, client chỉ cần subset tính năng; khi muốn giảm coupling giữa client và hệ thống con.

**Cấu trúc**:
- **Facade**: class đơn giản, expose các method high-level.
- **Subsystem classes**: các class phức tạp bên trong, Facade phối hợp chúng.

```kotlin
// Subsystem classes — phức tạp, nhiều bước
class RetrofitClient { fun fetchUser(id: Int): UserDto = /* ... */ }
class RoomDatabase { fun cacheUser(user: UserEntity) { /* ... */ } }
class DtoMapper { fun toEntity(dto: UserDto): UserEntity = /* ... */ }

// Facade — client chỉ cần gọi 1 method
class UserRepository(
    private val api: RetrofitClient,
    private val db: RoomDatabase,
    private val mapper: DtoMapper
) {
    suspend fun getUser(id: Int): UserEntity {
        val cached = db.getUser(id)
        if (cached != null) return cached
        val dto = api.fetchUser(id)
        val entity = mapper.toEntity(dto)
        db.cacheUser(entity)
        return entity
    }
}
```

**Trong Android**:
- **Repository pattern** chính là Facade — giấu chi tiết data source (API, DB, cache) sau 1 interface đơn giản.
- **Retrofit**: `Retrofit.create(ApiService::class.java)` giấu toàn bộ HTTP plumbing (OkHttp, converter, call adapter).
- **MediaPlayer**: Facade cho audio decoder, output device, buffer management.
- **Glide/Coil**: `Glide.with(context).load(url).into(imageView)` — giấu networking, caching, decoding, transformation.

### Q57. Proxy — kiểm soát truy cập đến object khác

**Ý tưởng**: Cung cấp object **đại diện** (proxy) cho object khác để kiểm soát truy cập: lazy init, cache, access control, logging, remote call. Proxy implement cùng interface với object thật → client không phân biệt.

**Khi nào dùng**: Object tốn tài nguyên để tạo (lazy loading), cần kiểm soát quyền truy cập, cần cache kết quả, hoặc object ở remote.

**Các loại**:
- **Virtual Proxy**: lazy init — chỉ tạo object thật khi cần dùng lần đầu.
- **Protection Proxy**: kiểm tra quyền trước khi delegate.
- **Caching Proxy**: cache kết quả, trả cache nếu còn hạn.

```kotlin
interface ImageLoader {
    fun load(url: String): Bitmap
}

// Real object — tốn tài nguyên
class NetworkImageLoader : ImageLoader {
    override fun load(url: String): Bitmap = /* download từ network */
}

// Caching Proxy — cache kết quả, tránh download lại
class CachedImageLoader(private val real: NetworkImageLoader) : ImageLoader {
    private val cache = LruCache<String, Bitmap>(50)
    override fun load(url: String): Bitmap {
        return cache.get(url) ?: real.load(url).also { cache.put(url, it) }
    }
}
```

**Trong Android**:
- **`by lazy`**: virtual proxy — Kotlin delegate tạo object lần đầu access.
- **Retrofit**: interface method → proxy object tạo bởi `Proxy.newProxyInstance()` → mỗi lời gọi method được chuyển thành HTTP request.
- **Glide cache**: memory cache → disk cache → network (chuỗi proxy).

---

### Behavioral Patterns — Giao tiếp giữa object

### Q58. Observer — thông báo khi state thay đổi

**Ý tưởng**: Định nghĩa quan hệ **1-nhiều** giữa các object: khi 1 object (Subject) thay đổi state, tất cả object phụ thuộc (Observer) được **tự động thông báo** và cập nhật.

**Khi nào dùng**: Khi thay đổi ở 1 object cần phản ánh ở nhiều object khác; khi không biết trước có bao nhiêu observer; khi muốn loose coupling giữa publisher và subscriber.

**Cấu trúc**:
- **Subject** (Observable): giữ danh sách observer, notify khi state đổi.
- **Observer**: interface nhận thông báo, cập nhật theo state mới.

```kotlin
// Tự implement Observer (để hiểu cơ chế)
interface Observer<T> {
    fun onChanged(value: T)
}

class Observable<T> {
    private val observers = mutableListOf<Observer<T>>()
    private var value: T? = null

    fun observe(observer: Observer<T>) { observers.add(observer) }
    fun removeObserver(observer: Observer<T>) { observers.remove(observer) }

    fun setValue(newValue: T) {
        value = newValue
        observers.forEach { it.onChanged(newValue) }  // notify tất cả
    }
}
```

**Trong Android** — không cần tự implement, framework cung cấp sẵn:

| Implementation             | Subject          | Observer        | Lifecycle-aware |
| -------------------------- | ---------------- | --------------- | --------------- |
| `LiveData`                 | `MutableLiveData`| `Observer { }` | Có              |
| `StateFlow` / `SharedFlow` | `MutableStateFlow`| `collect { }` | Cần `repeatOnLifecycle` |
| `RxJava Observable`        | `Observable`     | `subscribe { }` | Không (dùng `CompositeDisposable`) |
| `BroadcastReceiver`        | System/App       | `onReceive()`   | Theo register/unregister |

**Flow là Observer hiện đại**:
- `emit()` = `notifyObservers()`
- `collect { }` = `onChanged()`
- Scope cancel → tự unregister → không memory leak.
- Kết hợp operators (`map`, `filter`, `combine`) → reactive programming.

### Q59. Strategy — đổi thuật toán runtime

**Ý tưởng**: Định nghĩa **họ thuật toán**, đóng gói mỗi thuật toán thành class riêng, và cho phép **thay đổi thuật toán runtime** mà không sửa class sử dụng. Client giữ reference đến interface Strategy, swap implementation tuỳ ý.

**Khi nào dùng**: Khi có nhiều cách thực hiện cùng 1 việc và muốn chọn cách nào tuỳ theo điều kiện runtime; khi muốn loại bỏ chuỗi `if-else`/`when` dài chọn behavior.

**Cấu trúc**:
- **Strategy** (interface): khai báo method chung cho thuật toán.
- **ConcreteStrategy**: implement thuật toán cụ thể.
- **Context**: giữ reference đến Strategy, delegate việc thực thi.

```kotlin
// Strategy interface
interface SortStrategy {
    fun <T : Comparable<T>> sort(list: MutableList<T>)
}

// Concrete strategies
class QuickSort : SortStrategy {
    override fun <T : Comparable<T>> sort(list: MutableList<T>) { /* quick sort */ }
}
class MergeSort : SortStrategy {
    override fun <T : Comparable<T>> sort(list: MutableList<T>) { /* merge sort */ }
}

// Context — swap strategy runtime
class Sorter(private var strategy: SortStrategy) {
    fun setStrategy(newStrategy: SortStrategy) { strategy = newStrategy }
    fun <T : Comparable<T>> sort(list: MutableList<T>) = strategy.sort(list)
}

// Dùng
val sorter = Sorter(QuickSort())
sorter.sort(data)             // dùng QuickSort
sorter.setStrategy(MergeSort())
sorter.sort(data)             // đổi sang MergeSort, không sửa Sorter
```

**Trong Android**:
- **OkHttp Interceptor**: mỗi interceptor là 1 strategy xử lý request/response (logging, auth, retry, caching). Thêm/bỏ interceptor = thay đổi pipeline xử lý.
- **DiffUtil.ItemCallback**: strategy so sánh item cho `ListAdapter` — mỗi adapter có callback riêng tuỳ loại item.
- **RecyclerView.LayoutManager**: `LinearLayoutManager` vs `GridLayoutManager` vs `StaggeredGridLayoutManager` — swap layout strategy mà không sửa RecyclerView.
- **RecyclerView.ItemAnimator**: swap animator để đổi animation style.
- **Comparator**: `list.sortedWith(compareBy { it.name })` — strategy so sánh.

**Kotlin idiom**: thay vì tạo interface + class, dùng **function type** (lambda) — gọn hơn nhiều:

```kotlin
class Sorter(private var strategy: (MutableList<Int>) -> Unit) {
    fun sort(list: MutableList<Int>) = strategy(list)
}
val sorter = Sorter { list -> list.sort() }  // lambda thay thế class
```

### Q60. Template Method — định nghĩa khung, subclass điền chi tiết

**Ý tưởng**: Định nghĩa **bộ khung** (skeleton) của thuật toán trong method cha, để **subclass override** các bước cụ thể mà không thay đổi cấu trúc tổng thể. Cha quyết định "thứ tự làm gì", con quyết định "làm thế nào".

**Khi nào dùng**: Khi nhiều class có thuật toán giống nhau về cấu trúc, chỉ khác ở vài bước; khi muốn kiểm soát thứ tự thực thi từ class cha.

**Cấu trúc**:
- **AbstractClass**: chứa template method (final — không cho override) gọi các step method.
- **ConcreteClass**: override các step method cụ thể.

```kotlin
abstract class DataParser {
    // Template method — khung cố định, subclass không override được
    fun parse(source: String): List<Item> {
        val raw = readData(source)      // bước 1: đọc
        val parsed = parseData(raw)     // bước 2: parse
        val validated = validate(parsed) // bước 3: validate
        return validated
    }

    abstract fun readData(source: String): String  // subclass implement
    abstract fun parseData(raw: String): List<Item> // subclass implement
    open fun validate(items: List<Item>): List<Item> = items // hook — có default
}

class JsonParser : DataParser() {
    override fun readData(source: String) = File(source).readText()
    override fun parseData(raw: String) = Gson().fromJson(raw, /* ... */)
}
class CsvParser : DataParser() {
    override fun readData(source: String) = File(source).readText()
    override fun parseData(raw: String) = raw.lines().map { /* parse CSV */ }
}
```

**Trong Android** — đây là pattern **phổ biến nhất** trong Android framework:
- **Activity lifecycle**: `onCreate()` → `onStart()` → `onResume()` — framework quyết định thứ tự gọi, developer override để điền logic cụ thể. `Activity` là abstract class, lifecycle là template method.
- **Fragment lifecycle**: tương tự — `onCreateView()`, `onViewCreated()` là các "step" mà developer override.
- **RecyclerView.Adapter**: `onCreateViewHolder()` → `onBindViewHolder()` — framework gọi theo thứ tự, developer implement từng bước.
- **AsyncTask** (deprecated): `doInBackground()` → `onPostExecute()`.
- **View.draw()**: framework gọi `onDraw()` — developer override để vẽ custom view.

### Q61. Chain of Responsibility — truyền request qua chuỗi xử lý

**Ý tưởng**: Cho phép nhiều object có cơ hội xử lý request bằng cách truyền request qua **chuỗi handler**. Mỗi handler quyết định: xử lý request hoặc chuyển tiếp cho handler tiếp theo.

**Khi nào dùng**: Khi có nhiều object có khả năng xử lý request và muốn quyết định handler **runtime**; khi muốn decouple sender và receiver.

**Cấu trúc**:
- **Handler** (interface): khai báo method xử lý + reference đến handler tiếp theo.
- **ConcreteHandler**: xử lý nếu có thể, nếu không → chuyển tiếp cho next.

```kotlin
abstract class ApprovalHandler(private val next: ApprovalHandler? = null) {
    fun handle(amount: Int): String {
        return if (canHandle(amount)) approve(amount)
        else next?.handle(amount) ?: "Rejected — no handler"
    }
    abstract fun canHandle(amount: Int): Boolean
    abstract fun approve(amount: Int): String
}

class Manager : ApprovalHandler(Director()) {
    override fun canHandle(amount: Int) = amount <= 1000
    override fun approve(amount: Int) = "Manager approved $$amount"
}
class Director : ApprovalHandler(VP()) {
    override fun canHandle(amount: Int) = amount <= 5000
    override fun approve(amount: Int) = "Director approved $$amount"
}
class VP : ApprovalHandler() {
    override fun canHandle(amount: Int) = amount <= 20000
    override fun approve(amount: Int) = "VP approved $$amount"
}

Manager().handle(3000)  // "Director approved $3000"
```

**Trong Android**:
- **OkHttp Interceptor Chain**: request đi qua chuỗi interceptor (Application → Network), mỗi interceptor có thể modify request, short-circuit, hoặc chuyển tiếp. Đây là CoR + Decorator kết hợp.
- **Android touch event dispatch**: `Activity.dispatchTouchEvent()` → `ViewGroup.onInterceptTouchEvent()` → `View.onTouchEvent()` — event đi qua chuỗi View hierarchy, mỗi level quyết định xử lý hoặc chuyển tiếp.
- **Exception handling**: try-catch nested — exception đi qua các catch block từ trong ra ngoài.

### Q62. Command — đóng gói request thành object

**Ý tưởng**: Đóng gói **request** (hành động + tham số) thành 1 object riêng biệt. Cho phép: queue request, log request, undo/redo, schedule thực thi sau.

**Khi nào dùng**: Khi cần undo/redo; khi cần queue hoặc schedule thao tác; khi muốn decouple "ai ra lệnh" và "ai thực thi".

**Cấu trúc**:
- **Command** (interface): khai báo `execute()` (và optional `undo()`).
- **ConcreteCommand**: giữ receiver + params, implement `execute()`.
- **Invoker**: giữ command, gọi `execute()` khi cần.
- **Receiver**: object thực sự thực hiện công việc.

```kotlin
// Command interface
interface Command {
    fun execute()
    fun undo()
}

// ConcreteCommand
class AddTextCommand(
    private val editor: TextEditor,
    private val text: String
) : Command {
    override fun execute() { editor.insert(text) }
    override fun undo() { editor.delete(text.length) }
}

// Invoker — quản lý history cho undo
class CommandManager {
    private val history = ArrayDeque<Command>()
    fun execute(command: Command) {
        command.execute()
        history.addLast(command)
    }
    fun undo() { history.removeLastOrNull()?.undo() }
}
```

**Trong Android**:
- **MVI Intent/Action**: mỗi `Intent` (sealed class) là 1 Command — đóng gói user action thành object, ViewModel là Invoker xử lý.
- **WorkManager**: `OneTimeWorkRequest` / `PeriodicWorkRequest` — đóng gói task thành object, queue thực thi, retry, chain.
- **Navigation action**: `NavDirections` — đóng gói navigation destination + args thành object.
- **Undo/Redo** trong text editor, drawing app.

---

### Tổng hợp — Pattern nào gặp ở đâu trong Android?

| Pattern            | Nhóm        | Ví dụ Android thực tế                                         |
| ------------------ | ----------- | ------------------------------------------------------------- |
| Singleton          | Creational  | `object`, Hilt `@Singleton`, Application class                |
| Factory Method     | Creational  | `ViewModelProvider.Factory`, `WorkerFactory`, `FragmentFactory`|
| Builder            | Creational  | `Notification.Builder`, `OkHttpClient.Builder`, `Room.databaseBuilder` |
| Adapter            | Structural  | `RecyclerView.Adapter`, DTO → Domain mapping                  |
| Decorator          | Structural  | OkHttp Interceptor, InputStream wrapping, ContextWrapper       |
| Facade             | Structural  | Repository pattern, Retrofit, Glide/Coil                       |
| Proxy              | Structural  | `by lazy`, Retrofit dynamic proxy, image cache                 |
| Observer           | Behavioral  | Flow/LiveData/RxJava, BroadcastReceiver, lifecycle observer    |
| Strategy           | Behavioral  | LayoutManager, Interceptor, DiffUtil.ItemCallback, Comparator  |
| Template Method    | Behavioral  | Activity/Fragment lifecycle, RecyclerView.Adapter, custom View |
| Chain of Resp.     | Behavioral  | OkHttp chain, touch event dispatch                             |
| Command            | Behavioral  | MVI Intent, WorkManager, Navigation action                     |

> **Mẹo phỏng vấn**: Đừng nhớ pattern theo tên — nhớ theo **bài toán** nó giải quyết. Khi được hỏi "pattern nào dùng trong project?", liên hệ ngay với code thật: "Repository của em là Facade pattern, giấu API + DB sau 1 interface. Touch event dispatch là Chain of Responsibility. RecyclerView.Adapter chính là Adapter pattern + Template Method."

---

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

### Q63. 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.

### Q64. Room vs SQLDelight — khác nhau thế nào, khi nào dùng cái nào?

**Bản chất khác nhau hoàn toàn**:
- **Room**: abstraction layer **trên SQLite**, viết query bằng annotation trong Kotlin/Java → compile-time verify → sinh code tương tác SQLite. Tư duy **object-first** — định nghĩa Entity (data class) trước, Room sinh table.
- **SQLDelight**: viết **SQL thuần** trong file `.sq` → compile-time verify → sinh Kotlin data class + type-safe API. Tư duy **SQL-first** — định nghĩa schema/query bằng SQL trước, SQLDelight sinh code Kotlin.

|                        | Room                              | SQLDelight                          |
| ---------------------- | --------------------------------- | ----------------------------------- |
| Ngôn ngữ query         | Annotation + SQL trong `@Query`   | File `.sq` chứa SQL thuần          |
| Sinh code              | Entity → Table (object-first)     | SQL → Kotlin class (SQL-first)     |
| Multiplatform (KMP)    | **Không** — chỉ Android           | **Có** — Android, iOS, Desktop, JS |
| SQLite engine          | Android SQLite (hoặc bundled)     | Tuỳ platform: SQLite, native driver|
| Migration              | Auto migration hoặc manual SQL    | Versioned `.sqm` migration files   |
| Reactive (Flow/observe)| `Flow<List<T>>` return type trong DAO | `.asFlow().mapToList()` extension  |
| Compile-time verify    | Có (KAPT/KSP verify SQL syntax)  | Có (plugin verify SQL syntax)      |
| IDE support            | Android Studio tích hợp sâu       | Plugin riêng, ít mature hơn        |
| Backing database       | Chỉ SQLite                        | SQLite + có thể dùng driver khác   |
| Google maintain?       | **Có** — Jetpack official         | **Không** — CashApp/Square maintain|

**Room — ưu điểm và hạn chế**:

```kotlin
// Room: object-first — định nghĩa Entity, Room sinh table
@Entity(tableName = "users")
data class UserEntity(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "user_name") val name: String,
    val email: String
)

@Dao
interface UserDao {
    @Query("SELECT * FROM users WHERE id = :id")
    fun getUser(id: Int): Flow<UserEntity?>   // reactive tự nhiên

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(user: UserEntity)

    @Query("SELECT * FROM users ORDER BY user_name ASC")
    fun getAllSorted(): Flow<List<UserEntity>>
}

@Database(entities = [UserEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}
```

Ưu điểm:
- **Ecosystem Jetpack**: tích hợp sâu với Hilt (`@HiltViewModel`), Paging 3 (`PagingSource`), WorkManager. Google maintain → documentation tốt, community lớn.
- **Auto migration** (Room 2.4+): `@AutoMigration(from = 1, to = 2)` — Room tự phát hiện schema change đơn giản (thêm column, thêm table) và sinh migration code.
- **IDE support**: Android Studio hiểu Room annotation → autocomplete SQL, highlight lỗi, navigate Entity↔DAO.
- **Type converter** đơn giản: `@TypeConverter` cho custom type (Date, enum, JSON string).
- Quen thuộc với Android developer — hầu hết tutorial/course đều dạy Room.

Hạn chế:
- **Chỉ Android** — không dùng được cho KMP.
- Object-first → với query phức tạp (JOIN nhiều bảng, subquery, window function), phải tạo thêm data class riêng cho kết quả hoặc dùng `@RawQuery` — mất type-safety.
- SQL viết trong annotation string → khó format, khó đọc khi query dài.

**SQLDelight — ưu điểm và hạn chế**:

```sql
-- src/commonMain/sqldelight/com/example/User.sq

-- SQLDelight: SQL-first — viết SQL, tool sinh Kotlin class
CREATE TABLE user (
    id INTEGER NOT NULL PRIMARY KEY,
    user_name TEXT NOT NULL,
    email TEXT NOT NULL
);

getUser:
SELECT * FROM user WHERE id = ?;

getAllSorted:
SELECT * FROM user ORDER BY user_name ASC;

insert:
INSERT OR REPLACE INTO user (id, user_name, email) VALUES (?, ?, ?);
```

```kotlin
// Code Kotlin được sinh tự động — type-safe, không viết tay
val user: User? = database.userQueries.getUser(id = 1).executeAsOneOrNull()

// Reactive với Flow
database.userQueries.getAllSorted()
    .asFlow()
    .mapToList(Dispatchers.IO)
    .collect { users -> /* update UI */ }
```

Ưu điểm:
- **KMP native** — cùng schema + query dùng cho Android, iOS, Desktop, JS. Database layer share 100% cross-platform.
- **SQL-first** → tận dụng full power SQL: complex JOIN, subquery, window function, CTE — viết SQL thuần, không bị giới hạn bởi ORM abstraction.
- Query phức tạp **tự nhiên hơn** Room vì viết SQL trực tiếp trong file `.sq`, không nhồi vào annotation string.
- **Compile-time verify** mạnh: verify cả SQL syntax lẫn type mapping.
- Migration rõ ràng: file `.sqm` versioned, dễ review trong PR.

Hạn chế:
- **Phải biết SQL tốt** — không có abstraction layer giúp như Room `@Insert`, `@Update`, `@Delete`. Phải viết INSERT/UPDATE/DELETE thủ công.
- IDE plugin kém mature hơn Room — ít autocomplete, ít navigation support.
- Community nhỏ hơn Room nhiều → ít tutorial, ít StackOverflow answer.
- Tích hợp với Jetpack (Paging, Hilt) phải tự setup, không có sẵn như Room.
- Learning curve cao hơn nếu team quen Room.

**Khi nào chọn cái nào?**

| Tình huống                                  | Chọn          |
| ------------------------------------------- | ------------- |
| Android thuần, team quen Jetpack            | **Room**      |
| KMP — share database layer cho iOS          | **SQLDelight**|
| Query đơn giản (CRUD cơ bản)               | **Room** (ít boilerplate hơn) |
| Query phức tạp (JOIN, subquery, analytics)  | **SQLDelight** (SQL thuần mạnh hơn) |
| Cần tích hợp Paging 3, Hilt sâu            | **Room** (first-class support) |
| Team giỏi SQL, muốn kiểm soát hoàn toàn    | **SQLDelight**|
| Dự án mới, có kế hoạch KMP tương lai       | **SQLDelight** (tránh migrate sau) |
| Dự án đang dùng Room, không cần KMP        | **Giữ Room** (không đáng migrate) |

> **Tóm gọn cho phỏng vấn**: Room = object-first, Jetpack ecosystem, chỉ Android. SQLDelight = SQL-first, KMP cross-platform, full SQL power. Chọn Room cho Android thuần + team quen Jetpack. Chọn SQLDelight khi cần KMP hoặc query phức tạp. Không nên migrate Room → SQLDelight nếu không có lý do KMP rõ ràng.

### Q66. 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õ.

### Q67. 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 compile-time code-gen, type-safe. Koin runtime DSL, KMP    |
| api vs implementation?         | Transitive vs không transitive                                  |
| 3 nhóm Design Pattern?        | Creational (tạo), Structural (tổ chức), Behavioral (giao tiếp)  |
| Singleton trong Kotlin?        | `object` declaration, nhưng prefer DI `@Singleton` cho testable |
| Factory Method dùng khi?       | Uỷ quyền tạo object cho subclass, chọn impl runtime            |
| Adapter pattern trong Android? | RecyclerView.Adapter, DTO→Domain mapping                        |
| Decorator vs Inheritance?      | Decorator: thêm behavior động, xếp chồng. Inheritance: tĩnh    |
| Observer trong Android?        | Flow/LiveData = Observer hiện đại, tự cleanup khi scope cancel  |
| Template Method ở đâu?        | Activity/Fragment lifecycle, RecyclerView.Adapter callbacks     |
| Chain of Responsibility?       | OkHttp interceptor chain, touch event dispatch                  |
| Room vs SQLDelight?            | Room: object-first, Android only. SQLDelight: SQL-first, KMP   |
