李彥龍
(美的智慧生活(上海)科技有限公司,上海 201702)
智能手機已經(jīng)成為人們工作生活的一個不可缺少的輔助設備,記錄備忘功能的應用也成為了用戶日常使用率很高的一款應用程序,觀察國內(nèi)手機廠商的系統(tǒng),都會預裝記錄備忘類應用,比如華為手機“備忘錄”應用,聯(lián)想手機的“聯(lián)想記事本”,小米手機的“便簽”應用等。另外開放市場上的NearyMe 云筆記,有道云筆記,大姨媽App,寶寶樹孕育APP 等記事本備忘類型的應用都很受用戶的歡迎[1]。智能手機上的記事本相對于傳統(tǒng)的記錄方式可以做到記錄形式的多樣,更加的方便快捷。比如用戶可以通過錄音、視頻、圖片、文字等一種或者多種形式記錄。除此之外記錄的內(nèi)容還可以通過智能識別的方式,把包含時間點信息的記錄在日歷應用中自動生成提醒,更進一步提高便捷性。本文介紹基于谷歌在2018 年Goole I/O 大會上發(fā)布的JetPack組件庫設計開發(fā)聯(lián)想日歷的過程,為后續(xù)其它應用開發(fā)提供一套快捷高效的方案和思路。
記事本App 基于安卓平臺的應用,使用Kotlin 這一Google官方推薦的語言開發(fā)語言,并使用谷歌新推出的Jetpack 組件,實現(xiàn)高效快捷的開發(fā)性能穩(wěn)定的應用。下面介紹使用到的相關(guān)技術(shù)。
Kotlin 是一種新型的靜態(tài)類型編程語言,它有助于提高工作效率、開發(fā)者滿意度和代碼安全性,截止2021 年2 月11 日谷歌商店排名前1000 的應用中已經(jīng)有60%的采用了Kotlin 語言開發(fā)[2]。2019 年Google I/O 更是把Kotlin 定位推薦語言,而且后續(xù)的支持組件包都是以Kotlin 為開發(fā)語言??梢奊oogle 對Kotlin 語言的重視。Kotlin 語言有以下特點[2]:
兼容java,而且Android Studio 開發(fā)工具有一鍵轉(zhuǎn)換功能,可以將java 語言轉(zhuǎn)換成Kotlin 語言。
空指針安全。
支持Lamada。
支持擴展。
所以使用Kotlin 語言開發(fā)Android 應用可以做到:用更少的代碼更快速的開發(fā)出更少空指針異常的應用。
Jetpack 是一個由多個庫組成的套件,可幫助開發(fā)者遵循最佳做法,減少樣板代碼并編寫可在各種Android 版本和設備中一致運行的代碼,讓開發(fā)者精力集中編寫重要的代碼[3]。它是Google 公司在2018 年Google I/O 大會上推出的一套官方認證的開發(fā)系統(tǒng),包含架構(gòu)、UI、基礎庫、行為四個方面到目前一共有大約131 個庫[4],使用這些庫可以快速的搭建開發(fā)一個穩(wěn)定的應用,比如接下來介紹本文用到的幾個庫文件。
1.2.1 ViewModel
ViewModel 是JetPack 架構(gòu)組件中的一個類,它注重生命周期的方式存儲和管理界面相關(guān)的數(shù)據(jù)。相對于Activity 的7 個生命周期來說ViewModel 只有兩個生命周期,它把自己的生命周期和初始化時候傳入的context 綁定,只有在綁定的context生命周期結(jié)束時才會銷毀。所以它只有兩個生命周期的回調(diào)函數(shù):創(chuàng)建和初始化傳入context 生命周期結(jié)束時候的onCleared回調(diào)方法。
圖1 Activity 旋轉(zhuǎn)生命回調(diào)函數(shù)和綁定的ViewModel 生命周期回調(diào)函數(shù)的對比[5]
基于上述ViewModel 的特點可見,使用ViewModel 來作為容器管理UI 層依賴Model 層的數(shù)據(jù)是個便捷的做法,只需要初始化和使用,而不用管理界面的旋轉(zhuǎn)息屏等特殊場景數(shù)據(jù)的保存與恢復邏輯了,這樣可以把時間投入到業(yè)務邏輯的開發(fā)中。
1.2.2 LiveData
LiveData 是一種可觀察的數(shù)據(jù)存儲器類。與常規(guī)的可觀察類不同,LiveData 具有生命周期感知能力,意指它遵循其他應用組件(如Activity、Fragment 或Service)的生命周期。這種感知能力可確保LiveData 僅更新處于活躍生命周期狀態(tài)的應用組件觀察者[6]。使用時注冊觀察者Observer,如果LiveData 的實例被通過setValue(T),postValue(T)接口賦值更新數(shù)據(jù)時候,會回調(diào)Observer 的onChanged()接口,一次觸發(fā)更新UI。
1.2.3 Room
Room 在SQLite 上提供了一個抽象層,以便在充分利用SQLite 的強大功能的同時,能夠流暢地訪問數(shù)據(jù)庫[7]。Room 是在SQLite 數(shù)據(jù)庫的基礎上又做了一層封裝,開發(fā)者可以使用注解的方式方便的創(chuàng)建數(shù)據(jù)庫和定義訪問數(shù)據(jù)庫的接口,且支持多線程。
上文中我們提到了ViewModel,MVVM (Model-View-ViewModel)的架構(gòu)就是在之前MVP(Model-View-Presenter)基礎之上把Presenter 替換成ViewModel 形成的新的代碼架構(gòu)。ViewModel 作為顯示View 和數(shù)據(jù)存儲的Model 之間的橋梁,一方面可以作為數(shù)據(jù)的容器,另一方面可以作為業(yè)務邏輯的容器,從數(shù)據(jù)源獲取到的數(shù)據(jù)在顯示之前做一些處理。
圖2 MVVM 架構(gòu)圖
記事本應用的主要功能有:
2.1 主界面按照添加時間順序由上而下以列表形式顯示,每一個添加項都已卡片的形式展示。每一項有創(chuàng)建時間、標題、文本內(nèi)容、圖片和語音圖標。其中語音圖標標注時長,并有點擊播放按鈕。
2.2 主界面下部偏右加號按鈕點擊進入添加頁面、添加界面有創(chuàng)建時間、標題輸入框、文本輸入框,下部的按鈕欄中有語音按鈕、圖片按鈕、拍照按鈕。
2.3 主界面列表的每個卡片代表一個記事項,點擊進入編輯界面,此時編輯界面同添加界面,只是多了刪除和分享功能。
2.4 在主界面上長按一個記事項觸發(fā)進入ActionMode 也就是多選編輯界面,多以點擊其它卡片選中,再次點擊取消,ActionBar 也就是頂部欄提示當前已選擇項目的數(shù)目和刪除按鈕,這個功能便于用戶批量刪除。
由于記事本的功能決定的,它把用戶創(chuàng)建界面生成記事項存入本地數(shù)據(jù)庫,然后在主界面再進行讀取展示,點擊編輯再次對數(shù)據(jù)庫中保存的數(shù)據(jù)進行更新或者刪除。而主界面的多選模式則進行對數(shù)據(jù)庫中的記錄進行單個或者批量刪除。由此我們可以想到利用MVVM 架構(gòu)模式,如果把主界面展示的記事項作為一個數(shù)據(jù)item,那么我們可以把item 的列表作為一個LiveData 放入ViewModel 中,主界面的Activity 只需要觀察item的列表是否更新,觸發(fā)更新展示即可。而ViewModel 中可以在對記事項增刪改查操作后,觸發(fā)更新ViewModel 中item 列表項即可。至于LiveData 和Model 之間可以使用前面介紹的Room 組件替換SQLite 組件,實現(xiàn)數(shù)據(jù)庫的創(chuàng)建和接口Dao 的創(chuàng)建。方便業(yè)務層ViewModel 對Model 層增刪改查的工作。下圖是經(jīng)典的基于Jetpack 架構(gòu)組件的架構(gòu)圖,本文設計開發(fā)的記事本應用由于功能決定的可以參考此經(jīng)典架構(gòu)。
圖3 使用JetPack 架構(gòu)組件的典型架構(gòu)圖[8]
客戶端本地數(shù)據(jù)庫的構(gòu)建也比較簡單,一個table 名字為note_table。其中id 為自動生成累加的索引,并設置為primary key。type 取值為枚舉類型的索引,取值范圍0-1,0 代表普通記事項,只有文字內(nèi)容;1 代表有語音,圖片等內(nèi)容的記事項;textx用于保存文字內(nèi)容的字段。image1-image4 存儲用戶選擇的圖片路徑或者拍照后生成的圖片文件的路徑。date 字段用于保存日期對應毫秒。Voicepath 是保存語音文件路徑的字段。Ismedia,用于表示此項記錄是否有多媒體內(nèi)容,其實不用此字段通過判斷多個圖片字段和voicepath 字段也是可以判別的,用ismedia 字段只是多存儲一個便捷識別多媒體記錄項的標志位。
圖4 數(shù)據(jù)庫表結(jié)構(gòu)
記事本App 的應用的實現(xiàn)主要有三個部分,UI 部分、viewmodel 和model 部分,下面分別闡述這三部分的實現(xiàn)以及所用到的技術(shù)。
4.1.1 UI/UI controller
UI/UI controller 部分指顯示界面部分的代碼模塊。采用單Activity 多Fragment 的架構(gòu)設計,這樣設計的好處是便于各個fragment 之間數(shù)據(jù)的傳遞與共享。MainActivity 作為其它兩個Framgnet 的容器,EditNoteFragment 顯示新建和編輯記事項的界面,NotListFragment 顯示所有記事項的列表。
圖5 UI 部分代碼文件結(jié)構(gòu)圖
4.1.2 viewmodel
對 應 UI 部 分 兩 個 fragment,viewmodel 部 分 有NoteItemViewModel 和NoteListViewModel 兩個文件,其中NoteItemViewModel 中包含有LiveDate<NoteItem>的實例;同時兩個ViewModel 實例也持有Model 層數(shù)據(jù)庫的實例。
圖6 ViewModel 部分代碼文件結(jié)構(gòu)圖
4.1.3 model
model 部分是指本地存儲數(shù)據(jù)庫,這部分包含數(shù)據(jù)庫的創(chuàng)建,管理以及Dao 接口封裝對數(shù)據(jù)庫增刪改查的實現(xiàn)。Dao 接口根據(jù)業(yè)務需要封裝了四個功能:(1)查詢所有數(shù)據(jù)庫中記事項的數(shù)據(jù)。(2)查詢單個記事項的數(shù)據(jù),查詢參數(shù)是id,這也是數(shù)據(jù)表中作為primary key 存儲的字段,保證唯一性。(3)更新接口,參數(shù)是NoteItem 實例。(4)刪除單個NoteItem 的接口,參數(shù)是id。
這部分的實現(xiàn)由于使用了JetPack 中的Room 組件,所以我們只需要按照Room 組件的樣式創(chuàng)建數(shù)據(jù)庫存儲數(shù)據(jù)對應的Bean 文件并使用固定的注解標簽例如:@Entity (tableName =“note_table”)就可以創(chuàng)建對應的數(shù)據(jù)表。同樣的對Dao 文件也可以通過在Interface 接口定義的文件中使用@Dao 注解和@Insert 和@Query @Update 等標簽就可以實現(xiàn)。舉例:查詢所有數(shù)據(jù)庫中保存的記事項,可以這樣定義@Query (“SELECT *FROM note_table ORDER BY date DESC”);其中只需要注意note_table 為要查詢的表格的名字即可。
這里主要是通過用的正則表達式來匹配用戶創(chuàng)建記事項中的日期內(nèi)容,如果匹配成功則代表此記事項可以生成對應的提醒,并提示用戶是否插入到日歷的提醒數(shù)據(jù)庫中,達到定時提醒的功能。比如:8 月15 日體檢這樣的內(nèi)容。則會提示用戶是否生成一個8 月15 日的提醒,提醒的內(nèi)容是體檢。如果用戶選擇同意則進入日歷創(chuàng)建提醒的界面讓用戶繼續(xù)操作。當然這里所說的智能也只是窮舉日常中用戶輸入的日期格式,并不能做到全覆蓋,這里的正則表達式可以作為一個服務器接口后臺持續(xù)維護,動態(tài)分發(fā)給用戶使用。這樣可以做到用戶反饋匹配錯誤或者不能智能識別日期的問題可以快速的修正和發(fā)布,而用戶端則不需要更新應用即可以看到修復后的效果,提升用戶體驗。
用戶有從記事本分享到朋友圈或者微信好友的需求,所以我們接入了微信的sdk 并結(jié)合系統(tǒng)的分享功能,通過在分享前重新排版組合生成圖片然后再調(diào)用分享接口的方式實現(xiàn)了一鍵分享的功能。
記事本App 本身是一個功能相對簡單的應用,它的業(yè)務功能決定了界面修改內(nèi)容存儲到數(shù)據(jù)庫,顯示時候又從數(shù)據(jù)庫中讀取這樣的特性,特別適合用JetPack 中的ViewModel 結(jié)合LiveDate 的架構(gòu)組件。所以本文借助記事本App 的設計實現(xiàn)介紹MVVM 的架構(gòu)和ViewModel,LiveDate 以及Room 架構(gòu)組件的使用。此經(jīng)驗可用于開發(fā)設計其它App,提升開發(fā)效率和架構(gòu)的合理性,結(jié)合Kotlin 語言的使用也能進一步提升開發(fā)App 的穩(wěn)定性。