閒談軟體設計:Android App Architecture

更新於 2023/07/15閱讀時間約 10 分鐘

前言

這是 2014 年的舊文,稍加重新整理一下,會想重新整理這一篇是因為從前同事那裡聽到一些外包的趣事,所以把當初設計的思維和想法分享出來。當初開發的 App 已經上線了,不方便透漏太多設計的細節,文中介紹的是屬於泛用的的架構,適合大多數的 App,實作的細節則是隨專案的特性變化。

Model/View 分離

2013 年以 Android 工程師的身份進入水果公司,但就像是在遊戲中轉職一樣,沒多久就變成 iOS 工程師,在這之前,都在設計相同 App 的 Android 版的軟體架構,需要考慮的事情其實蠻多的,只是後來開始開發 iOS 版本後 (Android 版本暫緩),這些設計和想法就暫時放在腦中也沒機會整理下來。
設計時的考量主要有:(1) App 是 Internet App,在考量 UI 體驗和網路頻寬的消耗,多數資料需以某種形式 (例如:SQLite 或檔案) 儲存部分資料在行動裝置上;(2) 因此,會需要同步伺服器端和行動裝置端之間資料狀態;(3) 但行動裝置網路的穩定性不如一般網路可靠 (4G 也沒有比較好),要有足夠的自動化測試驗證正常的流程與異常的流程。
2014 年中,偶然的機會招募到數位 Android 工程師,於是 Android 版本準備開始復工,趁一些瑣碎的空檔時間,把當初想的結構給整理一下,個人對 Model 與 View 分離這件事十分堅持,讓自動化測試能夠極大化,所以基本的架構概念圖大概就如 Figure 1 所示。
Figure 1 — Android App Conceptual Architecture

Platform-independent Model

架構圖主要分成幾個區塊,綠色是 Model 的部分,所有的商業邏輯都集中在此,這個部分原則上盡可能是 platform-independent 的設計,因此以 Java SE 和 Android SDK 交集的 API 完成,在自動化測試時,完全不需要 Android 模擬器,用一般 JUnit 即可,希望用最少時間達到最大的測試涵蓋率,過去曾經統計過,核心功能部分的程式碼約是二萬八千多行,由三萬二千多行的測試程式,約一千五百個測試案例驗證功能的正確性,全部測時能在 40 秒內完成,測試涵蓋率在 95% 以上。
這邊要特別強調的是 Model,對於習慣只把資料結構當成 Model 的人來說,商業邏輯可能四散在 View、Controller (這裡的 Controller 指的是 MVC 中的 Controller) 或 Activity 中,但資料結構只是 Model 的一部分,也就是圖中 Domain Data Model 那一塊,維護資料結構物件關係的邏輯,如何回應 Use case 或 user story 所定義的系統事件 (system event),這些處理系統事件的 main controller 都屬於 Model,也就是圖中 Business Logic Managers,責任是透過 Web Service Interfaces 和伺服器溝通,然後維護資料結構物件間的關係,最後透過 DAO Interfaces 將狀態保存到行動裝置中。

Platform-dependent adapters

橘色是 platform-dependent 的實作,例如用 Android 的 SQLite API 來實作DAO,然後以 Setter Injection 或 Interface Injection 的方式,將 platform-dependent 的實作注入,為了做到這點,綠色區塊不能直接依賴 platform-dependent 的實作,因此 Web Service Interfaces 和 DAO Interfaces 兩個區塊只定義介面沒有實作,由橘色區塊中的 Web Service Implementation 和 DAO Implementation 分別實作這兩個介面,藉此將依賴關係反轉 (Dependency inversion),因此也容易把 Web Service Interfaces 和 DAO Interfaces 的 mock object 給注入,方便測試。
橘色區塊還有一個責任是扮演 Android Service 的角色,Android 能讓 App 以 Service 的方式在背景執行,以需要頻繁更新狀態的 App 類型來說,能有在背景執行的 Service 非常有用。當 Activity 被喚起,只需 bind service 就能取得所有最新的狀態。不過,設計不好,Service 是非常耗電的,若能以 GCM 喚起 App 進行單次的網路存取,也是一種用來更新狀態的一種方式,不一定得用 Service。此外,Service 與 Activity 共用同一個 thread,不能讓 Service 的初始化佔用太多時間,否則 App 啟動時,UI 會卡住無法動彈,以額外的 thread 進行初始化需要注意 multi-thread 引起的問題。
DAO Implementation 是 platform-dependent 的,所以用橘色標示沒什麼大問題,但 Web Service Implementation 卻是被標示為紫色,主要是因為以 JSON 格式傳遞資料的 Restful Web Services,是有機會只用以 Java SE 和 Android SDK 交集的 API (像是HttpURLConnection),或是是用 OkHttp,搭配 Android 上也能使用的 JSON Library (Gson 或 Jackson)來完成,也就是 Web Service Implementation 不一定是 platform-dependent 的實作,有機會能用 JUnit 來測試。

Views

淺灰色區塊即 View,主要是圖中 Android Activities & UI Flow Controls 區塊,這部分也能自動化測試,但需要 Android 模擬器或是使用實機,測試時間也較長,但 App 好不好用,這塊佔了很大的因素。此外,綠色區塊和橘色區塊的 API 會以 synchronous 的方式設計,為了不卡住 UI,Android SDK 本身有一些 Asynchronous 的輔助類別(例如AsyncTask),但我覺得還不夠好用,所以Asynchronous Supports & UI Components 會根據 App 的體驗需求客製化一些輔助類別,另外提供一些特殊的 UI 元件。
此外,Android 不允許非在 UI Thread 執行中的程式可以變更畫面,所以 Asynchronous Supports & UI Components 這區塊還要負責將不同 Thread 回來的更新通知轉到 UI Thread中。這個區塊以藍色標示,主要是因為這些元件應該設計成能跨專案共用的,當成是公司的資產,在開發新專案時就能夠使用才是長久之計。
大原則是這樣沒錯,不過準備開發的 App 會整合一個第三方的遊戲引擎,到時架構可能會有些許調整,例如在 Domain Model 或 Android Service 中多一些 Interface 與遊戲引擎的部分溝通,盡可能保護 Domain Model。最後,為了不讓不同層級的物件相互汙染,應該會用 Maven (或 Gradle) 的 module 機制將不同層級的物件歸屬到不同的 module 中,再利用 dependency 的方式限制 module 之間的可見度。希望 Android 版在需求相對明確 (已有iOS版) 的情況下,開發能夠順利些。

優缺點

最小交集

離開這專案後再回來想這個架構的優缺點,首先,為了讓自動化測試能不依賴 Android 模擬器,以 Java SE 和 Android SDK 交集的 API 完成有個缺點,要等到 Android SDK 正式支援 Java 8,才能在 Model 中開始使用 Java 8 的新特性 (像是 Lambda),不過,從 Android N 開始,已經開始支援大多數 Java 8 的特性,這缺點就沒這麼嚴重了。

跨平台共用

當初腦中確實有想過讓這 App 的核心能跨足到 PC 版,但跨平台不是最主要的考量,讓團隊成員能習慣從 domain 開始寫程式,才是這架構中 Model 與 UI 分開最主要的原因,不過後來真的開發 PC 版了,核心數萬行的程式不需重寫,想像一下,核心就像下圖中綠色的拼圖,無法單獨使用,需要其他的拼圖才能組成一個完整的 App,開發 PC 版時,則是提供藍色的拼圖整合核心的部分成為另外一個可以在 Mac 與 Windows 上執行的程式。

相依性管理

在跨平台時有一點要注意,這也是當初沒有做好的部分,當初為了開發方便,Android 相關的程式與核心的部分是同一個專案的二個模組,不像是引入第三方套件的方式引入核心到 Android 專案中,PC 版也是以 link 的方式直接將原始碼引入,但一旦核心被二個專案以原始碼形式引用時,任何一邊對核心的介面做了修改,都會導致另一邊編譯失敗,較理想的方式,應該是將核心的部分獨立出去,任何修改後建置成一個 JAR 檔並加上版本布署到內部的 repository 中,Android 與 PC 版的專案以第三方套件的方式引入,這樣便能夠以管理第三方套件版本的方式,讓雙方隨時有相容的介面可以編譯。

快速測試

從剛剛提到的統計資料可以看出來,測試程式碼與功能程式碼之間的比例約是 1.14 : 1,這同時也代表寫測試程式所花的時間幾乎和寫功能是一樣的,甚至更多,但這投資是值得的,若要以人力的方式測試二萬八千多行的功能,絕對是不可能在 40 秒內完成的,但一千五百多個測試案例卻是在每次有人簽入程式時都執行過一遍,只要 40 秒就能知道核心功能有沒有被改壞,是否安心許多呢?
最後,這設計可能的缺點之一是對於 Model 與 UI 分離這概念不熟,或是對 OO 設計不熟的人來說,有點難以上手...
為什麼會看到廣告
avatar-img
53會員
104內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Spirit的沙龍 的其他內容
程式開發有趣的地方,同樣的目標,不同的團隊會因不同的因素做出不同的設計抉擇。而這往往也是為什麼一個資深的工程師在開發速度上不一定比較快的原因之一,一個越是資深的工程師,思考的因素會更多,不過,不是考慮得越多就結果就一定越好,有時還會變成 over design 較糟的結果。
唸研究所開始當助教,偶而會有學弟妹問:怎樣寫好程式?老實說,這是個大哉問,連我學開發軟體這麼久,我也只能回答他們:多培養自己釐清問題、拆解問題、解決問題與抽象化的能力。但他們通常只會一臉狐疑看著我,感覺我說的話好抽象。
在履歷中常常看到導入 MVVM,然後問為什麼要導入 MVVM 時,最常聽到的答案是這樣不會有很肥大的 view controller,但如果再問 view controller 是 MVC 的那一個部分,很多人卻回答不出個所以然,所以想聊聊這個很多種說法的 MVC pattern。
為什麼是煮拉麵呢?主題是來自前同事在問我為什麼有人的程式好像常常會歪掉,或是變得難維護,後續的討論中,他用的例子就是拉麵,所以... 今天就用程式來煮拉麵吧!
程式開發有趣的地方,同樣的目標,不同的團隊會因不同的因素做出不同的設計抉擇。而這往往也是為什麼一個資深的工程師在開發速度上不一定比較快的原因之一,一個越是資深的工程師,思考的因素會更多,不過,不是考慮得越多就結果就一定越好,有時還會變成 over design 較糟的結果。
唸研究所開始當助教,偶而會有學弟妹問:怎樣寫好程式?老實說,這是個大哉問,連我學開發軟體這麼久,我也只能回答他們:多培養自己釐清問題、拆解問題、解決問題與抽象化的能力。但他們通常只會一臉狐疑看著我,感覺我說的話好抽象。
在履歷中常常看到導入 MVVM,然後問為什麼要導入 MVVM 時,最常聽到的答案是這樣不會有很肥大的 view controller,但如果再問 view controller 是 MVC 的那一個部分,很多人卻回答不出個所以然,所以想聊聊這個很多種說法的 MVC pattern。
為什麼是煮拉麵呢?主題是來自前同事在問我為什麼有人的程式好像常常會歪掉,或是變得難維護,後續的討論中,他用的例子就是拉麵,所以... 今天就用程式來煮拉麵吧!
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
我為什麼喜歡【怪醫豪斯】?很大一部分是因為這個主角應該是史上最迷人的角色之一。以現在角色來說,豪斯醫生就是個爛人,但是他的天才又讓人對他又愛又恨。
Thumbnail
一、從小觀察 從上電腦課學會用 Excel 這個文書處理軟體時開始注意到爸爸有用 Excel 記帳的習慣(簡單記錄,有時候會忘記金額所以不一定確實),每次爸爸發現我在觀察他記帳的時候一定會對我說:「固定支出越少每個月才能存到更多錢。」以及「不要小看雜支,常常花最多花得最莫名其妙的就是雜支。」 高
Thumbnail
通常,看大家聊職場話大多比較嚴肅,所以今天想聊些比較輕鬆的話題,是關於我職場上的LGBTQ族群間會出現的家常幹話
Thumbnail
看到有人因為這次的事情在說這是因為沒有校園沒有髮禁、禁止體罰、延後到校時間導致學生逐漸沒有紀律,覺得自己什麼都能做......我第一個想到的是我爸說他高中時畢業典禮教官只要走得慢一點一定會被憤怒的學生蓋布袋拖去打。發生這樣的事情當然得檢討,但希望不是以一種意氣用事、情緒主導的心態......
Thumbnail
「一群人所決定的,就是對的嗎?」 這是民主的盲點也是缺點
Thumbnail
最近身旁有幾位正在懷孕、或剛生產完的朋友,讓我想起自己在懷孕期間印象最深刻的三件「怪事」,其中又以第三件事最誇張。
Thumbnail
不知道大家在買房之前是不是都會參考親朋好友的意見,或是上網看一些買房注意事項,有時候考慮了這塊就忘了那塊,考慮的那塊又忘了這塊.......
Thumbnail
塔西佗陷阱(Tacitus Trap),一個得名於古羅馬歷史學家塔西佗的政治學理論,意指倘若公權力失去其公信力,無論如何發言或是處事,社會均將給予其負面評價。 當然信著恆信、不信者恆不信,這就是真實的人生。
Thumbnail
關於片名   台灣片名《花漾女子》,原文片名《Promising Young Woman》,台灣譯名將時間定格在悲劇發生前,而原文片名則進一步帶我們看見另一個可能性結果
Thumbnail
前幾年因為身體的關係,當了幾年的律師逃兵,當時開了之前的事務所以後,一時間也沒有特別想要做甚麼事情,所以就邊讀一點書、早晚運動一下,剛好聽到當年同梯朋友進去金融業工作,因此也抱著嘗(ㄊㄠˊ)試(ㄅㄧˋ)的心態,找了份銀行法令遵循的工作
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
我為什麼喜歡【怪醫豪斯】?很大一部分是因為這個主角應該是史上最迷人的角色之一。以現在角色來說,豪斯醫生就是個爛人,但是他的天才又讓人對他又愛又恨。
Thumbnail
一、從小觀察 從上電腦課學會用 Excel 這個文書處理軟體時開始注意到爸爸有用 Excel 記帳的習慣(簡單記錄,有時候會忘記金額所以不一定確實),每次爸爸發現我在觀察他記帳的時候一定會對我說:「固定支出越少每個月才能存到更多錢。」以及「不要小看雜支,常常花最多花得最莫名其妙的就是雜支。」 高
Thumbnail
通常,看大家聊職場話大多比較嚴肅,所以今天想聊些比較輕鬆的話題,是關於我職場上的LGBTQ族群間會出現的家常幹話
Thumbnail
看到有人因為這次的事情在說這是因為沒有校園沒有髮禁、禁止體罰、延後到校時間導致學生逐漸沒有紀律,覺得自己什麼都能做......我第一個想到的是我爸說他高中時畢業典禮教官只要走得慢一點一定會被憤怒的學生蓋布袋拖去打。發生這樣的事情當然得檢討,但希望不是以一種意氣用事、情緒主導的心態......
Thumbnail
「一群人所決定的,就是對的嗎?」 這是民主的盲點也是缺點
Thumbnail
最近身旁有幾位正在懷孕、或剛生產完的朋友,讓我想起自己在懷孕期間印象最深刻的三件「怪事」,其中又以第三件事最誇張。
Thumbnail
不知道大家在買房之前是不是都會參考親朋好友的意見,或是上網看一些買房注意事項,有時候考慮了這塊就忘了那塊,考慮的那塊又忘了這塊.......
Thumbnail
塔西佗陷阱(Tacitus Trap),一個得名於古羅馬歷史學家塔西佗的政治學理論,意指倘若公權力失去其公信力,無論如何發言或是處事,社會均將給予其負面評價。 當然信著恆信、不信者恆不信,這就是真實的人生。
Thumbnail
關於片名   台灣片名《花漾女子》,原文片名《Promising Young Woman》,台灣譯名將時間定格在悲劇發生前,而原文片名則進一步帶我們看見另一個可能性結果
Thumbnail
前幾年因為身體的關係,當了幾年的律師逃兵,當時開了之前的事務所以後,一時間也沒有特別想要做甚麼事情,所以就邊讀一點書、早晚運動一下,剛好聽到當年同梯朋友進去金融業工作,因此也抱著嘗(ㄊㄠˊ)試(ㄅㄧˋ)的心態,找了份銀行法令遵循的工作