Daily Digest

每日新聞摘要

2026年04月05日

今日總結

內容會跟隨語言切換。

📝 今日焦點 Gemma4 使本機 LLM 可上線並達成實用推理,適合私有助理與文件問答,並留意 APK 與記憶大小 🔧 效能最佳化 Gemma 在 6GB+ 裝置達到可用速度,但須處理熱降頻與 APK 大小,NPU 長時段更穩定,並實作超時 🏗 架構設計 為提升 BLE 穩定性應用前景服務並申請新版權限,維持單一 BluetoothGatt 實例、明確狀態機並確實 close() 與監控 ✅ 測試與品質 強調協程可讀性、一致邊界與觀測能力,改用 Channel 處理一次性事件,驗證 Room 遷移與註解 use‑site 並在多平台充分測試

01

Gemma 4 on Android: Complete Developer Guide to On-Device AI

Dev.to Kotlin 作者:rahul patwa 發布:04-05 08:33

Gemma 4 在 Android 上:完整開發者指南(離線 AI)

Gemma 4 E2B(2.58 GB)在 S26 Ultra GPU 上解碼速率為 52.1 tok/s、主動記憶體低於 1.5 GB,能在 6 GB+ 設備上實現真正的本地推論且資料不出裝置。

重點摘要

E2B 適合大多數 6 GB+ 裝置,E4B 提升推理但需 8 GB+
五步 Kotlin 整合:Gradle 依賴、下載到 filesDir、初始化 Engine、建立 Conversation、以 Flow 串流回應
後端取捨關鍵:GPU 快但熱降頻,NPU 長時穩定,SamplerConfig 用 Double,sendMessageAsync 回傳累積文字

本文說明如何在 Android 上以 Gemma 4 + LiteRT‑LM 建置可上線的本地 LLM。作者指出 E2B 變體檔案 2.58 GB、主動記憶體 <1.5 GB,可在 6 GB 以上裝置運行並在 S26 Ultra GPU 達到 52.1 tok/s;E4B 檔案更大(3.65 GB)、記憶體約 2 GB、但推理品質顯著提升。整合流程可在數小時內完成:加入 litertlm-android 依賴、於首次啟動下載模型到 filesDir、在背景執行初始化(為阻塞呼叫)、以 Conversation + SamplerConfig(參數為 Double)建立會話,並以 sendMessageAsync 透過 Flow 串流回應(每次發射為累積文字)。實務注意事項包括記憶體初始化順序、APK 大小限制、熱降頻處理與長對話電量增長,適合隱私敏感且需低延遲的應用場景。

02

Robust BLE: Preventing Disconnections and Implementing Auto-Reconnect on Android 12+ with Foreground Services

Dev.to Kotlin 作者:Ble Advertiser 發布:04-05 10:21

強固的 BLE:在 Android 12+ 使用前景服務防止斷線並實作自動重連

以 connectedDevice 類型的 Foreground Service 加上主動重連機制,可在 Android 12+ 背景下維持穩定的 BLE 連線。

重點摘要

前景服務(connectedDevice)與固定通知可降低系統終止背景程序的機率。
使用 connectGatt(autoConnect=false) 並在關閉舊 GATT 後以重試/退避策略主動重連。
在 Manifest 與執行時處理 BLUETOOTH_SCAN、BLUETOOTH_CONNECT 權限並監控藍牙狀態變更。

Android 12 之後對背景執行限制更嚴格,App 背景進程可能被系統終止,導致 BLE 連線無預警斷開。文章建議以 Foreground Service(foregroundServiceType=connectedDevice)並顯示固定通知,讓系統不易殺掉服務;同時在 Kotlin 範例中示範如何管理 BluetoothGatt、關閉舊 GATT、在 onConnectionStateChange 檢查 status、用 connectGatt(autoConnect=false) 實作主動重連與線性退避與最大重試次數。並說明 AndroidManifest 與執行時 BLUETOOTH_SCAN/BLUETOOTH_CONNECT 權限、使用 ContextCompat.startForegroundService 與 START_REDELIVER_INTENT,以及建議透過 LiveData/SharedFlow 回報連線狀態以利 UI 顯示與生產環境可靠性。

03

Mastering Android 12+ BLE Permissions in Kotlin: The `BLUETOOTH_SCAN`/`CONNECT` Dance and Common Gotchas

Dev.to Kotlin 作者:Ble Advertiser 發布:04-05 10:23

精通 Android 12+ 的 BLE 權限(Kotlin):BLUETOOTH_SCAN/CONNECT 的變化與常見陷阱

Android 12 將 BLE 操作從位置權限中拆分出來:在 Manifest 與執行時使用 BLUETOOTH_SCAN/CONNECT/ADVERTISE,並對 BLUETOOTH_SCAN 加上 neverForLocation。

重點摘要

Android 12+ 用 BLUETOOTH_SCAN/CONNECT/ADVERTISE 取代多數 BLE 的 ACCESS_FINE_LOCATION
在 Manifest 宣告新權限並於執行時請求;BLUETOOTH_SCAN 建議加上 neverForLocation
務必將 targetSdk 設為 31+、檢查藍牙與裝置位置服務,並僅請求必要權限

本文說明 Android 12(API 31)後 BLE 權限模型的改變與 Kotlin 實作要點。過去多數 BLE 操作倚賴 ACCESS_FINE_LOCATION,但 Android 12 引入更細緻的 BLUETOOTH_SCAN(掃描)、BLUETOOTH_CONNECT(連線)與 BLUETOOTH_ADVERTISE(廣播)等執行時權限,能避免不必要的地理位置授權。在 Manifest 中需兼顧舊版與新版權限,對 BLUETOOTH_SCAN 使用 android:usesPermissionFlags="neverForLocation" 以明確宣示不取位址。此外必須把 targetSdk 提升到 31+ 才會套用新模型;執行時依系統版本請求權限、檢查藍牙狀態與裝置位置服務,並採取最小授權、提供適當說明與前往設定的引導以改善使用者體驗。

04

Mastering Disconnection Handling in Android BLE: Robust Strategies for Reconnection and Error Recovery

Dev.to Kotlin 作者:Ble Advertiser 發布:04-05 10:27

掌握 Android BLE 斷線處理:重連與錯誤復原的穩健策略

學會偵測斷線類型、實作智慧重連策略、妥善管理 GATT 資源,並從常見 BLE 錯誤中優雅復原。

重點摘要

在 onConnectionStateChange 中解析 status 代碼以判斷斷線原因。
斷線時務必呼叫 BluetoothGatt.close() 並將參考設為 null 避免資源外洩與快取問題。
以明確狀態機避免競態,並用指數回退與最大嘗試次數實作重連。

Android 上的 BLE 連線易受範圍、裝置斷電、系統終止或藍牙堆疊錯誤影響而中斷。本文說明如何在 BluetoothGattCallback.onConnectionStateChange 中判讀 status 代碼(例如 0、19 為乾淨斷線;133 表示異常)以決定復原策略;強調在斷線時呼叫 BluetoothGatt.close() 並將參考設為 null 以避免資源外洩與快取混亂;建議以明確的狀態機避免競態,並用指數回退(exponential backoff)與最大嘗試次數實作重連。還涵蓋不同 Android 版本的權限需求與常見陷阱,幫助把 BLE 應用打造得更穩健、實務可用。

05

Mastering `BluetoothGatt` Lifecycle: Preventing Memory Leaks and Stale Connections in Android Kotlin BLE

Dev.to Kotlin 作者:Ble Advertiser 發布:04-05 10:29

精通 BluetoothGatt 生命週期:在 Android Kotlin BLE 中預防記憶體洩漏與過期連線

disconnect() 處理邏輯斷線,close() 則必須呼叫以釋放 BluetoothGatt 的原生資源,否則會造成洩漏與過期回呼。

重點摘要

對每個 BluetoothGatt 實例務必呼叫 close() 並清除參考
在每個回呼中驗證 gatt 實例以忽略過期或非管理中的回呼
主要執行緒呼叫 GATT 操作,且對 user-initiated 連線建議使用 autoConnect=false

BluetoothGatt 在 Android 中代表 GATT 客戶端並持有原生系統資源,若未正確管理會導致記憶體洩漏、幽靈回呼及重連失敗。本文說明 disconnect()(邏輯斷線)與 close()(釋放資源)間的關鍵差別,並提供 Kotlin 的 BleConnectionManager 範例:維持單一 BluetoothGatt 實例、在回呼內驗證 gatt、於斷線或錯誤時呼叫 close() 並將引用設為 null,同時強調主線程或專用 Handler 執行 GATT 操作、謹慎使用 autoConnect 與處理執行時權限。採用這些做法可避免資源無法回收與不穩定的 BLE 行為,提升 IoT 應用的可靠性。

06

Why Your Android BLE Advertisements Silently Fail in the Background on Android 12+ and How to Fix It

Dev.to Kotlin 作者:Ble Advertiser 發布:04-05 10:31

為何你的 Android BLE 廣播在 Android 12+ 背景下會悄然失效,以及如何修正

即便獲得 BLUETOOTH_ADVERTISE 權限也不會免除 Android 12+ 的背景限制;必須搭配前景服務與正確權限與設定,才能穩定在背景廣播。

重點摘要

Android 12+ 強化背景執行限制,系統可能在背景終止廣播
BLUETOOTH_ADVERTISE 是必要的執行時權限,但不等於可持續背景廣播
用前景服務、低功耗廣播參數與完整的權限與生命週期管理來提高穩定性

在 Android 12(API 31)以後,系統為了省電與隱私收緊背景執行,導致 BluetoothLeAdvertiser 在應用進入背景時可能被系統靜默停止。新引入的 BLUETOOTH_ADVERTISE、BLUETOOTH_CONNECT、BLUETOOTH_SCAN 等執行時權限須申請,但無法取代前景服務或 OEM 等級的例外權限。對於一般開發者,正確作法是設定 targetSdkVersion 至 31 以上、在 AndroidManifest 宣告所有必要權限(並於 Android 14+ 指定 foregroundServiceType)、在前景服務中管理廣播、以 ADVERTISE_MODE_LOW_POWER/低功率輸出在背景廣播,並加強 AdvertiseCallback、權限檢查與藍牙狀態監控與重啟策略,以避免廣播悄然中止。

07

Solving the Android BLE GATT Race Condition: Reliable Sequential Operations with Kotlin Coroutines

Dev.to Kotlin 作者:Ble Advertiser 發布:04-05 10:33

以 Kotlin Coroutines 解決 Android BLE GATT 競態條件

透過協程驅動的序列化操作隊列,避免 Android BluetoothGatt 非同步呼叫導致的間歇性 BLE 失敗。

重點摘要

BluetoothGatt 的讀寫與設定皆為非阻塞,並可能引發競態條件
以 Channel 與 suspendCancellableCoroutine 將 GATT 操作排成單一序列處理
加入逾時、資源正確釋放與操作唯一識別以確保可靠性

Android 的 BluetoothGatt API 為非同步、非阻塞設計,若在前一個操作尚未完成時又發出新的讀寫或設定命令,容易造成操作被丟棄、內部狀態錯亂或連線斷開。文章建議以 GattOperation 封裝每個操作,使用單一 Channel 作為隊列,並在協程中逐一執行,透過 suspendCancellableCoroutine 與 CompletableDeferred 將 callback 轉為可掛起的協程呼叫。另需實作每次操作逾時、防止資源漏失的 disconnect/close 邏輯、錯誤傳遞機制,以及為操作加上唯一 ID 以正確對應回撥。將非同步 BLE 流程序列化後,可顯著提升穩定性並簡化程式邏輯。

08

Mastering Reliable BLE Characteristic Notifications on Android (Kotlin): Avoiding Missed Updates and Connection Drops

Dev.to Kotlin 作者:Ble Advertiser 發布:04-05 10:35

在 Android (Kotlin) 上穩定掌握 BLE 特徵通知

在 Android 上可靠接收通知需要兩個步驟:在本地呼叫 setCharacteristicNotification,並向遠端特徵的 CCCD 寫入啟用值,遺漏任一者會造成通知不穩定。

重點摘要

本地啟用通知並寫入 CCCD 才能真正讓外設發送更新
連線後立即協商較大 MTU 以減少分段並提升效率
維持訂閱清單並在重連後重新啟用通知,妥善管理 GATT 生命週期

本文聚焦於在 Android(Kotlin)中建立可靠的 BLE 特徵通知,指出常見錯誤為只呼叫 setCharacteristicNotification 而忘記寫入 CCCD(Client Characteristic Configuration Descriptor)。文章說明 GATT 與 BluetoothGattCallback 的非同步流程,建議在連線後盡早 requestMtu 以降低封包分段,並示範一個 BleNotificationManager 範例:儲存想要訂閱的特徵 UUID,於 onServicesDiscovered 時重啟通知流程。實務要點還包括實作健全的連線狀態機(重試與資源釋放)、處理 descriptor write 的錯誤與重試邏輯,以及對高頻通知做去抖或限速,以避免 UI 卡頓與資料遺失,從而提升 IoT 應用的穩定性與使用者體驗。

09

Why I Killed My DSL Parser for Raw DEX Compilation

Dev.to Kotlin 作者:Elimihele God's favour 發布:04-05 14:38

為何我放棄 DSL 解析器改用原始 DEX 編譯

我沒有再用脆弱的 DSL,而是編譯實際的 Kotlin 程式,將原始 DEX 串流到裝置並即時載入。

重點摘要

最初用 DSL 把 Kotlin 樣式語法轉成 JSON 渲染 Compose,但太侷限。
新流程用 kotlinc 與 Compose 插件編譯 .kt 成 .class,再用 d8 產生 classes.dex。
將 base64 DEX 經 WebSocket 推送至裝置,由自訂 ClassLoader 即時載入並觸發重組,無須重建或重裝。

作者為 JetStart 放棄了以 DSL 為核心的實作,因為該方法在處理自定義 state、複雜動畫或第三方 Compose 函式庫時太脆弱且受限。新的架構直接編譯開發者的 Kotlin 程式:使用 kotlinc 與 Compose 編譯器插件將 .kt 編成 .class,透過 Android 的 d8 工具產出 classes.dex,再將其 base64 編碼後以自訂 WebSocket 協定推送至連線的 Android 裝置。裝置上的自訂 ClassLoader 即時載入新類別,UI 隨之重組,達到真實的 @Composable 支援與近乎即時(亞百毫秒)的熱重載體驗,開發者寫的是純 Android 程式而非專有 DSL。

10

Android Emulator vs Physical Device: Picking Your Development Target

Dev.to Kotlin 作者:Elimihele God's favour 發布:04-05 15:05

Android 模擬器 vs 真機:選擇你的開發目標

JetStart 讓你能以偏好方式對 Kotlin Compose UI 進行熱重載:直接到真機或使用高效能的 Android 模擬器。

重點摘要

真機熱重載採用自訂注入架構,編譯並透過 WebSocket 將 DEX 推到執行中應用的 ClassLoader。
CLI 整合 AVD 管理,可建立 JetStart 優化的模擬器並自動安裝客戶端與建立熱重載連線。
實驗性 Web 模擬器會將 Compose 轉譯為 JavaScript,於瀏覽器快速呈現,適合輕量 UI 骨架開發。

JetStart 提供三種熱重載路徑:真機、Android 模擬器與實驗性 Web 模擬器。為了在真機上達到即時且保留狀態的更新,作者建立了自訂注入流程,監測檔案變更後在背景執行 kotlinc 和 d8,並透過 WebSocket 將原始 DEX bytecode 推送到正在執行的應用內 ClassLoader,保留觸控回饋與顏色等真實體驗。若沒有真機,JetStart CLI 可管理 AVD,建立預設為快速優化的模擬器並自動偵測與連線。Web 模擬器作為戰術性備援,將 Compose 轉為 ES modules 在瀏覽器呈現,雖然快速但無法完全取代原生 Android 環境。建議:最終測試用真機、日常開發用模擬器、外出或省資源時用 Web 模擬器。

11

Kotlin Weekly #505

Kotlin Weekly 作者: 發布:04-05 07:49

Kotlin Weekly #505

第505期(2026-04-05)重點包括 Amper 0.10(自動 JDK 配置與 Maven 轉換器)、新的 OpenTelemetry Kotlin SDK,以及多篇技術文章與教學影片。

重點摘要

Amper 0.10:自動 JDK 配置、Maven-to-Amper 轉換器、支援自訂編譯器外掛
OpenTelemetry Kotlin SDK:跨平台、針對行動友善的追蹤與日誌 API
重點文章:Koog 建構 AI 代理、以單元測試掌握 Koin 編譯器、為多模組 Android 設定 BoM

Kotlin Weekly 第505期(2026年4月5日)彙整近期 Kotlin 生態的重要更新。新聞部分介紹 Amper 0.10,新增自動 JDK 配置、Maven 轉 Amper 的轉換器、對自訂 Kotlin 編譯器外掛的支援,以及針對 Ktor 與效能分析的 IDE 整合改進;並宣布新的 OpenTelemetry Kotlin SDK,提供跨 Android、iOS、JVM 與 JS 的統一追蹤與日誌介面。專欄文章涵蓋使用 Koog 建立可靠 AI 代理、透過單元測試熟悉 Koin 編譯器註解,以及為多模組 Android 凝聚 BoM 的設定說明。其他內容還有 Android CI 的 GC 實驗、教學影片與多個新興函式庫,以及贊助與投稿資訊。

12

There’s a Better Way to CTA with TooltipBox (Material 3) — Stop Using Wonky Toasts!

Medium Kotlin 作者:RAFA 發布:04-05 08:41

用 TooltipBox(Material 3)更好地處理 CTA — 別再用怪怪的 Toasts!

與其用底部 Toast 解釋被點擊的 CTA,不如用 Material 3 的 TooltipBox 提供貼齊且清楚的回饋。

重點摘要

底部 Toast 與觸發元件容易脫節,顯得不明確。
TooltipBox 可貼齊 CTA,提供上下文相關的提示或回饋。
使用 TooltipBox 可提升清晰度、可掃描性與互動一致性。

RAFA 在文章中指出,使用底部 Toast 來回應 CTA 操作常常不是理想做法:訊息與觸發元件脫節、顯示位置不直觀,容易讓使用者困惑。作者建議改用 Material 3 的 TooltipBox,因為它能貼齊按鈕或控制項,提供上下文相關的回饋或標示,同時維持視覺與互動一致性。與短暫出現的底部 Toast 相比,TooltipBox 更容易被掃描與發現,干擾性也較低,並能減少哪個控制項觸發訊息的模糊性。文章鼓勵開發者考慮採用 TooltipBox,並在 Stackademic 提供連結以便閱讀完整示範與說明。

13

I Spent Hours Debugging a One-Line Fix: A Kotlin Multiplatform War Story

Medium Kotlin 作者:Sijan Neupane 發布:04-05 10:27

我花了好幾小時除錯,結果是一行修正:一則 Kotlin Multiplatform 的戰事

當你的跨平台應用在還沒載入就當掉,而修正竟然只有一行且令人尷尬地簡單。

重點摘要

Kotlin Multiplatform 的跨平台應用在啟動前當掉。
花了數小時排查,最後竟只需一行程式碼即可修復。
故事凸顯多平台開發中小細節可能造成大問題。

作者 Sijan Neupane 分享一則 Kotlin Multiplatform 的實戰經驗:一個跨平台應用在啟動前就當掉,花了好幾小時除錯後,問題竟然只需一行程式碼就能修好,令人尷尬。文章以這段「工程師戰事」為例,凸顯跨平台開發中小細節或設定可能造成大問題,以及長時間排查卻被簡單解法收場的無奈與幽默。這個故事提醒開發者在多平台共用程式碼時務必檢查微小差異與相容性,並從經驗中學會謙虛與耐心。

14

When Coroutines Become Unreadable: Designing Systems You Can Still Understand Six Months Later

Medium Kotlin 作者:Oliver Bozhinovski 發布:04-05 14:45

當協程變得難以閱讀:設計能在六個月後仍看得懂的系統

如果你問大多數 Android 開發者什麼造就良好的協程架構,答案通常會是類似的回應:

重點摘要

協程導向的程式碼可能隨時間變得難以閱讀
以明確邊界與一致慣例來設計以利長期理解
透過文件、測試與可觀測性減輕未來維護負擔

Oliver Bozhinovski 的文章探討為何在實務中以協程為基礎的架構會逐漸變得難以理解,以及如何在設計時優先考量以確保系統在數月後仍易於閱讀。文章指出,雖然開發者對「良好協程架構」有既定想法,但實際程式碼常因隱含的控制流程、分散的 scope 與過度抽象而失去可讀性。作者建議以清晰的邊界、一致的約定與有限且有說明的抽象來設計系統,並搭配充足的測試與可觀測性工具。整體論點是將可讀性與可維護性視為設計首要目標,重視文件與工具帶來的長期收益。

15

One-Time Events in Jetpack Compose: Why I Switched from SharedFlow to Channel

Medium Kotlin 作者:chanzmao 發布:04-05 05:19

Jetpack Compose 的一次性事件:我為何從 SharedFlow 改用 Channel

在 MVI 與現代 Android 架構中,處理像導覽或暫時性 UI 回饋等「一次性事件」需要不同的設計模式。

重點摘要

一次性事件(如導覽、暫時性提示)在 MVI 中需特殊處理。
作者從 SharedFlow 轉向 Channel 以處理這類事件。
文章比較兩者差異並說明對 Compose 的影響。

本文探討在 MVI 架構與 Jetpack Compose 中如何處理「一次性事件」。作者以導覽和暫時性 UI 回饋為例,說明這類事件應僅被消費一次的需求,並解釋為何決定從 SharedFlow 改用 Channel。文章著重於兩種工具在事件行為上的不同、促使改變的考量,以及這些差異對 Compose UI 事件傳遞的實務影響,並提供選擇更符合單次消費語意的方法建議。

16

BUKU SAKU LENGKAP: PEMROGRAMAN KOTLIN

Medium Kotlin 作者:Ahmad Fakih 發布:04-05 06:01

完整口袋書:Kotlin 程式設計

Kotlin 是由 JetBrains 開發並獲 Google 正式支援的現代程式語言,用於應用程式開發。

重點摘要

現代程式語言
由 JetBrains 開發
獲 Google 正式支援,用於應用程式開發

Kotlin 是由 JetBrains 開發、並獲 Google 正式支援的現代程式語言。此 Medium 文章〈BUKU SAKU LENGKAP: PEMROGRAMAN KOTLIN〉由 Ahmad Fakih 撰寫,標示為「完整口袋書」類型,片段強調 Kotlin 的現代性與官方背書,指出其在應用程式開發上的相關性。摘錄內容簡短,作為導讀並引導讀者前往 Medium 閱讀完整內容;片段未提供技術細節或實作範例,但明確表明文章屬於概覽性、參考性質的簡要介紹,讀者可藉此找到更全面的 Kotlin 資訊來源。

17

Room Database: A Practical Guide for Android Development

Medium Kotlin 作者:Sivabharath K 發布:04-05 06:05

Room 資料庫:Android 開發實務指南

一篇有架構的指南,從核心元件講到資料庫遷移與交易處理,協助理解 Room 的運作。

重點摘要

說明 Room 的核心概念與元件
涵蓋資料庫遷移與交易管理
以 Android 開發者實務為導向

本文為一篇有條理的 Room 指南,說明 Android 常用的持久化機制 Room 的主要概念與組成,並介紹與遷移(migrations)及交易(transactions)相關的處理重點。文章以實務角度整理要點,幫助開發者了解各個元件如何協同運作,以及在維護與擴展應用資料庫時應注意的面向,適合需要掌握 Room 基礎與實作考量的 Android 開發者閱讀。

18

The Sticker Dilemma: Why Kotlin Annotations Need Use-Site Targets

Medium Kotlin 作者:Akshay Surana 發布:04-05 06:09

貼紙困境:為何 Kotlin 註解需要指定使用位置

註解是我們程式碼庫中無聲的架構師,驅動依賴注入並強制資源類型。

重點摘要

註解在 Android 開發中決定行為與生成結果
它們支援依賴注入並檢查資源類型
Kotlin 要求 use-site targets 以消除註解應用的歧義

在 Android 開發中,註解雖不顯眼,卻影響大量行為:驅動生成程式碼、校驗資源並與依賴注入等框架整合。文章指出,Kotlin 的一個宣告可能對應到欄位、getter、setter 或參數等多個實體,若不明確指定使用位置(use-site target),註解就可能被套用到錯誤的目標,導致預期之外的行為或錯誤。透過 use-site targets 可以明確註解的作用位置,降低隱性錯誤、改善與函式庫和 DI 的互通性,並讓後續維護者更易理解原始意圖。結論強調在 Kotlin Android 專案中精確標註使用位置的重要性。