Daily Digest

每日新聞摘要

2026年03月05日

今日總結

內容會跟隨語言切換。

📝 今日焦點 本日重點包括:說明在 Android 10+ 上打包並執行二進位(例如 QEMU)會遭遇 SELinux W^X 限制的根因與可行解法 — 將執行檔改名為 .so 並放入 jniLibs/<abi>/,使用以 Bionic(Android linker)為 ELF 直譯器的編譯產物(如 Termux build),並在 AGP 3.6+ 設定 packagingOptions.jniLibs.useLegacyPackaging true 強制解壓,這組做法可在非 root APK 中允許 execve() 成功,對於需要在 App 內啟動 VM 或原生工具的專案(如 Pockr)具有直接可用性與部署影響。 📝 今日焦點 EveryCal:一款以場景卡(Smart Cards)為核心的 Android 計算器,聚焦減少日常情境中的上下文切換(烹飪、購物等),提供即時輸入結果、語音喚出、懸浮計算器與 19 種情境卡片(稀釋/濃度、瓦數換算、單位/匯率/價格比較等);技術採用 Kotlin + Jetpack Compose、Room、Retrofit、Coroutines/Flow,強調 UX 連續性與可客製化工作區,對提升使用情境效率有實務價值。 🏗 架構設計 Sub Tracker(以 AI agents 主導開發):作者以需求定義與 agent orchestration 為開發流程核心,前端採 Kotlin + Jetpack Compose(MVVM + Clean Architecture),後端為 Java Spring Boot + MongoDB,雙方透過 REST API 串接;使用 Gemini/Claude/Antigravity 等工具。意義在於示範 AI 可加速產出與原型驗證,但開發者角色轉向系統設計師與 AI 協調者,需加強審查、測試與架構治理以確保品質與可維護性。 🏗 架構設計 UI 狀態建模建議:文章主張以 Kotlin sealed class 明確建模 UI 狀態(例如 Loading、Success(data)、Error),取代零散布林旗標。好處是讓狀態成為編譯期可檢查的閉集合,當使用 when 做模式匹配時能獲得分支完整性檢查,提升可讀性、可測試性與維護性,降低因旗標競合或遺漏情況導致的錯誤風險。 ✅ 測試與品質 Pockr 的重啟迴圈案例揭示 race condition:問題源於 Kotlin 層以布林 isRunning 作為狀態來源,導致在 stopVm() 與新 QEMU 啟動間的短暫窗格回報為 stopped,使 Flutter 端重新允許啟動並觸發重複啟動。修正做法是在 getStatus() 優先檢查 vmProcess 並確認其 exitValue(),僅在進程確實終止時回報 stopped,並在 Dart 層加入啟動護欄以忽略重複呼叫;經 Firebase Test Lab 驗證後問題消失。此案強調以「實際資源(process handle)」為真實來源、加強日誌與在自動化 UI 測試下預期會曝露 race 的重要性。

01

Pockr | Part 2 — Executing Binaries on Android

Dev.to Kotlin 作者:Ai2th 發布:03-05 13:24

Pockr | 第 2 部分 — 在 Android 上執行二進位檔

將 QEMU 解壓到 app 私有資料夾會在 Android 10+ 因 SELinux 的 W^X 政策被禁止執行(app_data_file)。

重點摘要

SELinux 的 W^X 政策使 getFilesDir() 下的檔案(app_data_file)無法執行。
把 QEMU 改名為 .so,放入 jniLibs/<abi>/,安裝時會被解到 nativeLibraryDir(exec_type)。
使用 Termux 編譯的 QEMU(Bionic /system/bin/linker64)並啟用 useLegacyPackaging 強制解壓。

本篇說明在 Android 上執行打包二進位(如 QEMU)會出現 Permission denied 的原因與解法。Android 10 起,SELinux 以 W^X 政策標記 app 資料為 app_data_file,導致 execve() 被拒;唯一例外是 nativeLibraryDir(標籤為 exec_type),因此可將 QEMU 二進位重命名為 .so,放在 android/app/src/main/jniLibs/<abi>/,安裝時會被提取到 /data/.../lib/...,得以執行。此外,PackageManager 只接受以 Android linker 為 ELF 直譯器的檔案,故需使用 Termux 編譯版本(對 Bionic 連結);最後 AGP 3.6+ 預設會壓縮 native libs,需在 build.gradle 設定 packagingOptions.jniLibs.useLegacyPackaging true 以強制解壓到磁碟。

02

Pockr | Part 5 — Debugging the VM Restart Loop

Dev.to Kotlin 作者:Ai2th 發布:03-05 13:26

Pockr|第 5 篇 — 偵錯 VM 重啟循環

在重啟期間,程式先用布林旗標判斷狀態而非檢查實際的 VM 程序,導致 health check 在約 35 秒時誤判為「stopped」,觸發連續重啟。

重點摘要

isRunning 與 vmProcess 之間的競態導致 VM 不斷重啟。
修正:先檢查實際的 vmProcess,再依進程狀態決定是否為 stopped。
在 Dart 層加上狀態護欄,避免重複啟動請求在轉換期間發生。

作者描述在 Pockr 中運行 Alpine Linux 時遭遇的重啟迴圈:VM 大約每 35 秒重啟一次,因 health check 超時。追查後發現 Kotlin 的 getStatus() 先檢查布林值 isRunning,若為 false 就回傳 "stopped",但在 stopVm() 與新 QEMU 啟動之間存在短暫窗格,該旗標會暫時為 false 而實際進程尚未結束。這使得 Flutter 端重新啟用啟動按鈕,進而觸發第二次啟動請求而中斷首次初始化。解法是在 getStatus() 中先檢查 vmProcess 是否存在並判斷其 exitValue(),只有進程確實結束時才回報 stopped;並在 Dart 層加入啟動保護以忽略重複呼叫。修正後在 Firebase Test Lab 驗證成功,Alpine 完成首次啟動不再迴圈重啟。

03

I Built an Android App Without Writing a Single Line of Code (Using AI Agents)

Dev.to Kotlin 作者:Gökhan AYTEKİN 發布:03-05 13:31

我在不寫一行程式碼的情況下建立了一個 Android 應用(使用 AI agents)

透過協調 AI agents 產生與迭代實作,我完成了一個具備 Spring Boot 後端的可運作 Android 應用程式。

重點摘要

開發 Sub Tracker,管理定期訂閱與付款
以 Gemini、Claude、Antigravity、Google Stitch 等 AI 工具產生並修正程式與 UI
前端使用 Kotlin/Jetpack Compose(MVVM、Clean Architecture),後端為 Spring Boot + MongoDB,現正進行 Google Play Closed Testing

作者以 AI agents 為主導開發 Sub Tracker,一款用於管理與追蹤定期訂閱和經常性付款的 Android 應用。開發流程由作者定義需求、引導 AI agents、審查並反覆修正生成的程式碼與介面,前端採用 Kotlin 與 Jetpack Compose,遵循 MVVM 與 Clean Architecture,後端使用 Java Spring Boot 與 MongoDB,雙方透過 REST API 溝通。專案使用的工具包含 Gemini、Claude、Antigravity(agent orchestration)與 Google Stitch(UI 設計),具備訂閱追蹤、付款提醒、定期支出管理、內購與廣告變現等功能。程式碼已公開於 GitHub,應用正在 Google Play Closed Testing,測試重點為穩定性、變現流程、後端效能與使用者體驗。作者認為 AI 並非取代開發者,而是將角色轉為系統設計師與 AI 協調者。

04

I hate cooking math. So I built a smart Android calculator.

Dev.to Kotlin 作者:kuroappworks 發布:03-05 14:28

我討厭料理時的數學,所以我做了個智慧 Android 計算機

我開發了 EveryCal,一款能自動處理廚房數學、微波瓦數換算、購物比較等功能的 Android 計算器,讓你不用在做菜時停下來算數。

重點摘要

即時顯示結果、語音輸入、懸浮計算器與備忘清單,方便多工操作。
Smart Cards 提供 19 種自動解算卡片,涵蓋稀釋濃度、微波瓦數換算、單位與匯率轉換等。
採用 Kotlin、Jetpack Compose、Room、Retrofit、Coroutines & Flow,並用 exp4j 處理運算解析。

作者開發的 EveryCal 是一款簡潔但功能豐富的 Android 計算器,目的是避免在做菜或日常情境中為了計算而打斷流程。除了基本運算外,EveryCal 提供即時輸入結果、語音喚出功能、可釘選的懸浮計算器與購物備忘清單;核心是以情境分組的 Smart Cards(目前已實作 19 種),自動處理稀釋/濃度換算、微波瓦數轉換、單位與匯率轉換、價格比較與折扣計算等。進階用戶可自訂工作區,只保留常用卡片。技術棧包含 Kotlin、Jetpack Compose、Room、Retrofit、Coroutines & Flow、手動注入與 exp4j,並提供手冊和 Google Play 下載連結。

05

Sealed Classes: The Cleanest Way to Model UI State in Kotlin

Medium Kotlin 作者:Rituraj Sambherao 發布:03-05 10:31

密封類別:在 Kotlin 中建模 UI 狀態的最乾淨方式

許多 Android 應用的 UI 狀態一開始只是 loading 標誌、可能的資料與錯誤訊息。

重點摘要

UI 狀態常從 loading 標誌、資料與錯誤訊息開始
分散的布林旗標隨複雜度增加會變得脆弱且難以維護
使用 sealed class 可明確建模每種狀態並由編譯器保證處理齊全

許多 Android 應用一開始以簡單的 UI 狀態表達:一個 loading 標誌、可能的資料,以及發生錯誤時的錯誤訊息。這樣的設計在初期可行,但隨著流程與例外情況增加,零散的布林旗標會變得混亂且容易出錯。文章主張在 Kotlin 中採用 sealed class 作為更乾淨的替代:定義封閉的一組狀態類別(例如 Loading、Success 並攜帶資料、Error),將每種狀態明確建模;透過 when 的模式匹配可確保每個分支都被處理,並由編譯器捕捉未處理的情況。以 sealed class 取代臨時旗標能提升可讀性、可測試性與維護性,使 UI 狀態轉換更清晰且更安全。