---
# System prepended metadata

title: '[Video Note] [Jetpack Compose] Brandan Jones - Building a Broadcast Receiver'
tags: [Jetpack Compose, Video Note]

---

### BroadcastReceiver 介紹與用途

* BroadcastReceiver 用於接收系統或應用程式的廣播訊息（Intent）並觸發邏輯。
* 可依據 `IntentFilter` 過濾特定 Action，例如模擬藍牙連線以簡化 UI。
* 因模擬器無法模擬藍牙，範例以電源連接與拔除作為觸發事件。

### 建立 BroadcastReceiver 類別

* 建立一個類別繼承 `BroadcastReceiver`，實作 `onReceive()` 函式。
* 在 `onReceive()` 中使用 `when` 判斷 `intent.action` 為電源連接或拔除。
* 根據不同 Action 更新 UI 對應狀態（例如簡化與否）。

### 使用 ViewModel 傳遞狀態
![image](https://hackmd.io/_uploads/BkMYjuvUxe.png)

* Receiver 接收廣播後需通知 UI，透過 ViewModel 傳遞資料。
* ViewModel 中定義 `MutableLiveData<Boolean>` 表示是否簡化 UI。
* Receiver 透過建構子取得 ViewModel 實例，變更 `simplify.value`。

### 註冊 Receiver 與設置 IntentFilter

* 在 Activity 的 `onCreate()` 註冊 Receiver。
* 建立 `IntentFilter` 並加入 `ACTION_POWER_CONNECTED` 與 `ACTION_POWER_DISCONNECTED`。
* 呼叫 `registerReceiver(receiver, filter)` 完成註冊。

### Compose 中觀察 LiveData 狀態

* 在 Composable 中使用 `observeAsState()` 觀察 `LiveData`。
* 使用 Elvis 操作符處理可能的 null 值，預設為 `false`。
* 根據簡化模式與否決定 UI 呈現內容。

### 條件渲染 UI 元件

* 使用 `if` 判斷是否顯示某個按鈕（例如拍照按鈕）。
* 當 `simplify` 為 `true`（簡化模式）時隱藏該按鈕；為 `false` 時顯示。
* 利用 Compose 可組合式設計，靈活切換 UI 呈現。

### 模擬系統事件觸發

* 使用 Android Emulator 的 Extended Controls 模擬充電與斷電。
* 在模擬充電時觸發 `ACTION_POWER_CONNECTED`，觸發 UI 簡化。
* 模擬拔除電源時觸發 `ACTION_POWER_DISCONNECTED`，回復完整 UI。

### 除錯與觀察值變化

* 設定中斷點觀察 `onReceive()` 是否被正確觸發。
* 檢查 `simplify.value` 是否被正確設定並觸發重組。
* 驗證 UI 是否根據簡化狀態做出正確渲染邏輯。

---
# 選擇題

1. BroadcastReceiver 的主要用途是？
   A. 渲染 Jetpack Compose 畫面
   B. 處理網路請求
   C. 回應來自系統或其他應用的廣播 Intent
   D. 儲存資料到本地資料庫
   **答案：C**

2. 若要讓 BroadcastReceiver 回應電源插入事件，應監聽哪個 Action？
   A. ACTION\_BOOT\_COMPLETED
   B. ACTION\_POWER\_CONNECTED
   C. ACTION\_SCREEN\_ON
   D. ACTION\_USB\_MOUNTED
   **答案：B**

3. BroadcastReceiver 必須實作哪個函式？
   A. onCreate
   B. onReceive
   C. onHandleIntent
   D. onBroadcast
   **答案：B**

4. 在 Kotlin 中，BroadcastReceiver 中的 switch-case 應改用什麼？
   A. if-else
   B. select
   C. choose
   D. when
   **答案：D**

5. 若要判斷目前是哪個廣播事件觸發 receiver，可使用哪個方法？
   A. intent.extras
   B. context.getIntentType()
   C. intent.action
   D. receiver.name
   **答案：C**

6. 若要讓 BroadcastReceiver 能更改 Jetpack Compose 的 UI，應更新？
   A. ViewModel 中的 LiveData
   B. Compose 中的 Column
   C. BroadcastReceiver 的 context
   D. Activity 的 Intent
   **答案：A**

7. Jetpack Compose 的 UI 如何對 LiveData 的變化做出反應？
   A. observeAsState
   B. onLiveDataChange
   C. observeForever
   D. rememberState
   **答案：A**

8. 若 BroadcastReceiver 需要使用 ViewModel，建議怎麼實作？
   A. 使用 GlobalScope
   B. 使用 ViewModelProvider.get()
   C. 傳入 ViewModel 至 Receiver 的建構函式
   D. 在 Receiver 內建立新的 ViewModel
   **答案：C**

9. ViewModel 中使用的 LiveData 應為？
   A. val simplify = MutableState`<Boolean>`()
   B. val simplify = rememberSaveable { mutableStateOf(false) }
   C. val simplify = MutableLiveData`<Boolean>`()
   D. val simplify = remember { mutableStateOf(false) }
   **答案：C**

10. 若想讓 LiveData 變化時自動更新 UI，Jetpack Compose 中應該怎麼做？
    A. 使用 delay() 等待值
    B. 使用 LaunchedEffect
    C. 使用 observeAsState() 並與 Composable 綁定
    D. 使用 rememberCoroutineScope
    **答案：C**

11. LiveData 為何不能直接用 boolean 存值？
    A. LiveData 不支援基本型別
    B. Jetpack Compose observeAsState 只能觀察 LiveData
    C. 直接使用 Boolean 無法被觀察與自動更新
    D. Boolean 不能序列化
    **答案：C**

12. 若使用者插上電源，BroadcastReceiver 預期會設定 simplify 值為？
    A. null
    B. false
    C. true
    D. 0
    **答案：C**

13. 若想在沒有電源連接時顯示某個按鈕，應如何撰寫邏輯？
    A. if (simple) ShowButton()
    B. if (!simple) ShowButton()
    C. if (simple == null) ShowButton()
    D. ShowButton() always
    **答案：B**

14. BroadcastReceiver 是如何註冊的？
    A. 在 AndroidManifest.xml 中註冊
    B. 使用 registerReceiver() 搭配 IntentFilter
    C. 透過 onCreate() 傳入 context
    D. 使用 lifecycleScope.launch
    **答案：B**

15. 如果在 Activity 的 onCreate() 中註冊 Receiver，適合放在哪個位置？
    A. setContent() 之前
    B. setContent() 裡面
    C. setContent() 之後
    D. onResume() 中
    **答案：C**

16. IntentFilter 的作用為何？
    A. 將廣播轉為 ViewModel
    B. 決定 BroadcastReceiver 要接收哪些 Intent
    C. 決定 UI 顯示順序
    D. 註冊 Activity 的主題樣式
    **答案：B**

17. Jetpack Compose 可根據 LiveData 自動更新 UI 的核心機制是？
    A. rememberCoroutineScope
    B. ViewModelScope
    C. Compose Recomposition
    D. RecyclerView DiffUtil
    **答案：C**

18. 在 observeAsState() 回傳的值為 nullable 時，通常使用哪種寫法處理預設值？
    A. ?.orElse(false)
    B. ?: false
    C. !null
    D. if-else
    **答案：B**

19. 如果 LiveData 變更但 UI 無反應，可能是？
    A. 使用了 mutableState 而非 LiveData
    B. LiveData 未使用 observeAsState
    C. BroadcastReceiver 未設定正確
    D. 以上皆有可能
    **答案：D**

20. 若要透過 emulator 測試 power connected/disconnected 廣播，應在哪設定？
    A. Emulator 中的 Location 設定
    B. Emulator 的 Battery → Charger
    C. 模擬飛航模式
    D. 電量百分比設定
    **答案：B**

21. Kotlin 中 Elvis 運算子 `?:` 用於？
    A. 比較大小
    B. 判斷是否為 null 並提供預設值
    C. 組合多個狀態
    D. 建立新的 intent
    **答案：B**

22. 在 Jetpack Compose 中，為了隱藏 UI 元件常使用？
    A. setVisibility(false)
    B. Conditional Composable 包裹
    C. XML layout 判斷
    D. setEnabled(false)
    **答案：B**

23. 如果 BroadcastReceiver 沒有註冊 intent filter 會怎樣？
    A. 所有 intent 都觸發
    B. 無法觸發
    C. 預設接受所有廣播
    D. 會 crash
    **答案：B**

24. BroadcastReceiver 中接收到的 `context` 通常用途是？
    A. 建立 Composable
    B. 存取資源、啟動服務等
    C. 轉為 ViewModel
    D. 傳送通知到 Firebase
    **答案：B**

25. 對於多個觸發來源，使用 `when` 處理 broadcast 的好處為？
    A. 更快的效能
    B. Kotlin 強制要求
    C. 條件分支更清晰
    D. 支援非同步操作
    **答案：C**

26. 將簡化 UI 的判斷邏輯集中在 ViewModel 的好處為？
    A. 可以用 SharedPreferences 快取
    B. 易於測試、分離邏輯與 UI
    C. 減少 context 使用
    D. 增加 activity 體積
    **答案：B**

27. 在 Composable 中使用 `if (!simple)` 包裹按鈕的目的為何？
    A. 僅在非簡化模式下顯示按鈕
    B. 當簡化為 true 時重複顯示按鈕
    C. 減少記憶體使用
    D. 動畫更順暢
    **答案：A**

28. 使用 observeAsState 監聽 LiveData 時的返回類型是？
    A. 非同步任務
    B. State\`<T?>`
    C. Boolean
    D. Flow
    **答案：B**

29. 如果希望按鈕在簡化模式為 true 時隱藏，應該？
    A. 使用 if (simple) { Button() }
    B. 使用 if (!simple) { Button() }
    C. 使用 visibility = View\.GONE
    D. 使用 mutableListOf()
    **答案：B**

30. Jetpack Compose 中最適合處理外部廣播邏輯並同步到 UI 的架構組合是？
    A. Activity + ViewModel + BroadcastReceiver + LiveData
    B. Activity + Coroutine + Room
    C. Service + RecyclerView
    D. Fragment + DataBinding
    **答案：A**


# Terminology
* Broadcast Receiver：用於接收 Android 系統廣播訊息的組件，可響應如電源連接、網路變化等事件
* Intent：Android 中用於描述動作或事件的物件，可啟動組件或傳遞廣播
* Intent Filter：篩選可觸發 BroadcastReceiver 的特定 Intent 動作類型
* onReceive()：BroadcastReceiver 中必須實作的方法，用於處理接收到的廣播事件
* ACTION\_POWER\_CONNECTED：系統廣播動作，表示電源已連接
* ACTION\_POWER\_DISCONNECTED：系統廣播動作，表示電源已斷開
* ViewModel：儲存 UI 狀態與邏輯的組件，支援生命週期感知與資料共享
* LiveData：具備生命週期感知能力的可觀察資料容器，適合與 ViewModel 搭配使用
* MutableLiveData：LiveData 的可變版本，可由 ViewModel 更新值並觸發觀察者
* observeAsState()：將 LiveData 轉換為 Compose 中可觀察的 State，以響應資料變更
* Jetpack Compose：Android 的宣告式 UI 框架，讓 UI 可組合且狀態導向
* State：Compose 的可觀察狀態資料結構，變更後觸發重組
* Boolean?：可為 null 的布林型別，常用於表示三態（true/false/null）
* Elvis Operator（`?:`）：Kotlin 語法糖，用於指定 null 時的預設值
* `!`（否定運算子）：邏輯反轉布林值，在 Compose 中可用來條件渲染
* Button Composable：Compose 的按鈕 UI 元件，可附加 onClick 事件
* if 條件包裹：可將條件邏輯直接套用於組合式 UI 元件，用以動態控制渲染
* RegisterReceiver()：將 BroadcastReceiver 註冊至系統，使其能接收廣播
* UnregisterReceiver()：從系統取消註冊 BroadcastReceiver，避免記憶體洩漏
* Emulator Extended Controls：Android 模擬器的工具面板，可模擬硬體狀態（如充電）
* AC Charger 模擬：在模擬器中模擬電源連接事件以觸發廣播
* None 模擬：模擬未連接電源狀態，觸發 ACTION\_POWER\_DISCONNECTED
* Breakpoint（中斷點）：開發者工具功能，用於除錯時暫停程式並檢視狀態
* Debugger：偵錯工具，用於追蹤程式執行流程與變數狀態
* State hoisting：將狀態提升至外部以實現組件之間的單向資料流
* Constructor Dependency Injection：將依賴物件（如 ViewModel）透過建構子傳入
* sealed class IntentAction：封裝 Intent 動作的類別，提供類型安全
* MutableStateFlow：另一種可觀察狀態容器，可用於 Compose + Flow 架構
* rememberUpdatedState：保持對最新值或 Lambda 的穩定引用，避免因 recomposition 而過時
* CompositionLocal：提供跨組合層級的依賴注入機制，例如本地主題或資源存取
* LaunchedEffect：在組合期間啟動協程執行副作用任務（例如 LiveData 觀察）
* DisposableEffect：註冊生命週期相關資源並清理，例如登記 BroadcastReceiver
* ViewModelProvider：從 ViewModelStore 中取得 ViewModel 實例的工具
* Koin（DI 框架）：一個輕量級 Kotlin 相依注入框架，可提供 ViewModel 建立與注入
* setContent：Compose UI 初始化入口，設置組合式 UI 的內容區塊
* .value 屬性：LiveData 中包裹的實際值欄位，用於取得或設值
* nullable handling：處理可能為 null 的變數，常見於 Kotlin 中的 LiveData
* observer pattern：設計模式之一，允許物件對資料變更做出反應
* UI State Synchronization：透過觀察資料來源自動同步使用者介面狀態
* Event-driven programming：以事件為驅動的程式設計模式，廣播接收器即屬此類
* Switch-case（when 語法）：條件分支的語法結構，用於根據不同情況執行不同邏輯
* Simplified UI Mode：依環境狀態變更 UI 複雜度，例如駕駛模式下隱藏互動元件
* Runtime Permission：廣播接收器可能需授權（如藍牙變化），視情況加入檢查
* BluetoothAdapter.ACTION\_CONNECTION\_STATE\_CHANGED：接收藍牙連線狀態變化的廣播
* Manifest-declared Receiver：於 AndroidManifest.xml 中宣告的靜態廣播接收器（Android 8+ 受限制）
* Context.registerReceiver()：在執行期註冊接收器，需搭配 IntentFilter 使用
* IntentFilter.addAction()：加入欲接收的動作類型至意圖過濾器
* Intent.ACTION\_BATTERY\_CHANGED：另一個常見的系統廣播，用於偵測電池狀態變化
* MVVM 架構：Model-View-ViewModel，將 UI、業務邏輯與資料模型分離的架構
* ViewModelScope：ViewModel 專屬協程範圍，處理背景任務避免記憶體洩漏
* Configuration Change：如旋轉螢幕，LiveData 和 ViewModel 可維持狀態以應對此類變化
* recomposition：Compose UI 自動重繪過程，依狀態變更決定需更新的部分
* logcat：Android Studio 中即時查看系統與應用程式訊息的工具視窗
* power status simulation：透過模擬器模擬電源插拔，測試 BroadcastReceiver 行為
* Observer lambda：將 LiveData 與 UI 綁定的函式體，負責在值變更時重新渲染
* Null Safety：Kotlin 提供的語言特性，用於處理可為 null 的變數
* Elvis 表達式：提供簡潔的語法來設定變數預設值（`x ?: y`）
* MutableState<Boolean>：Compose 中可觀察的布林型別，用於控制條件渲染
* if 條件組合：可用邏輯判斷包裝 UI 元件，以動態決定是否顯示
* 可組合式條件渲染：Compose 支援條件內嵌組合式 UI，實現動態顯示/隱藏
* UI 減化策略：當偵測為駕駛模式等狀態時自動調整 UI 降低互動需求
* logDebug()：用於記錄除錯資訊以便開發時觀察事件觸發與狀態變更
* @Composable 註解：標示函式為可組合的 Compose UI 函式
* MainActivity.onCreate()：Android Activity 初始化 UI 與狀態的入口函式
* Android Emulator：提供虛擬裝置環境模擬硬體行為與狀態測試應用邏輯
* Device Event Simulation：模擬如藍牙、WiFi、電池狀態等硬體事件以觸發廣播
* observeForever()：LiveData 觀察者註冊方式之一，無視生命週期（需小心記憶體洩漏）
* observer removal：適時移除 LiveData 觀察者以避免資源佔用或重複通知
* Compose Preview：在 Android Studio 中預覽可組合函式 UI，不執行實體應用程式
* Navigation Component：管理應用頁面導覽與狀態堆疊的架構工具，可結合 ViewModel
* ViewModelFactory：客製化 ViewModel 建立邏輯，支援依賴注入與初始化資料
* LiveData.observe()：觀察 LiveData 變更並觸發對應 UI 更新邏輯
* Simulator Hot Swap：模擬器支援即時切換硬體狀態，無需重啟應用即觀察效果
* Backing Field（.value）：LiveData 內部存取值的方式，提供變更與觀察接口
* 層級感知元件：如 ViewModel、LiveData 等可隨組件生命週期自動管理狀態與資源
* UI 渲染順序控制：Compose 透過條件、狀態與重組機制控制元件何時繪製與更新
* 電源狀態感知 UI：透過 BroadcastReceiver 動態調整使用者介面對應充電狀態
* MVVM + Compose 整合：使用 ViewModel 管理狀態，Compose 負責視覺呈現與互動邏輯

