請參考教材,全面理解和完成本章節內容... ... java
複製工程ch16,將工程目錄更名爲ch17. web
在手機上徹底退出你的「陋習手記」App(不是把應用隱藏起來),再從新執行「陋習手記」App,哇!個人以前的手記哪裏去了? 數據庫
幾乎全部應用都須要有個地方存儲數據。本章,咱們將升級CriminalIntent應用,實現保存並加載存儲在設備上的JSON文件數據。 json
Android設備上的全部應用都擁有一個獨立的文件系統,應用程序只能在本身的文件系統區域內存儲和讀取文件,不能夠去其它地方訪問,此區域被稱爲沙盒。全部的非代碼文件都要保存在此,例如圖像,圖標,聲音,映像,屬性列表,文本文件等。將文件保存在沙盒中可阻止其餘應用的訪問、甚至是其餘用戶的私自窺探(固然,要是設備被root了的話,則用戶能夠隨意獲取任何數據)。 數組
提示 瀏覽器
沙盒也叫沙箱sandbox,且多用於計算機安全技術。其原理是經過重定向技術,把程序生成和修改的文件定向到自身文件夾中。沙盒中的程序和文件所作的任何改動對操做系統不會形成任何損失。這種技術被計算機技術人員普遍使用.安全
每一個應用的沙盒目錄都是設備/data/data目錄的子目錄,且默認以應用包命名。例如,CriminalIntent應用的沙盒目錄全路徑爲:/data/data/com.jet.criminalintent。 網絡
好消息是,應用開發時,沒必要在內存中存放應用的沙盒目錄路徑。須要知道路徑時,咱們可直接調用Android API中的便利方法來獲取它。 單元測試
除沙盒目錄外,應用也可將文件保存在外部存儲介質上,如經常使用的SD存儲卡等。通常來講設備並不內置SD卡,所以需用戶自行配置。雖然文件甚至整個應用均可以存儲到SD卡上。但出於安全考慮,一般不推薦這麼作。這其中最重要的一個因素就是,外部存儲上的數據存取並不只僅侷限於應用自己,也就是說,任何人均可以讀取、寫入以及刪除這些數據。 學習
爲應用添加數據持久存儲功能主要涉及兩大處理過程:將數據保存至文件系統、以及應用啓動時從新加載保存的數據。每一個處理過程又分爲兩個步驟。保存數據時,首先將數據轉換爲可保存格式(如JSON數據格式),而後將數據寫入文件;讀取數據時,則恰好相反,首先從文件中讀取格式化的數據,而後將其解析爲應用所需的內容。
提示:
JSON(JavaScript Object Notation)和XML是一種比較流行的數據交換格式。
JSON(JavaScript Object Notation) 是一種輕量級的數據交換格式。JSON採用徹底獨立於語言的文本格式,這些特性使JSON成爲理想的數據交換語言。易於人閱讀和編寫,同時也易於機器解析和生成。
採用JSON的格式的數據,採用一對"名稱/值「的形式,簡單如:
{"firstName":"Brett","lastName":"McLaughlin","email":"aaaa"}
若是用它保存數組,優點更明顯,如:
{
"people":[
{"firstName":"Brett","lastName":"McLaughlin","email":"aaaa"},
{"firstName":"Jason","lastName":"Hunter","email":"bbbb"},
{"firstName":"Elliotte","lastName":"Harold","email":"cccc"}
]
}
JSON適用於webservices接口服務的開發。Android SDK內置了標準的
org.json
類包,咱們能夠利用其中的類和方法來建立和解析JSON文件。要了解更多有關org.json
包的信息,可查閱Android開發者文檔。也可訪問網址http://json.org,瞭解更多有關JSON數據交換格式的內容;而XML是另外一種數據交換格式,可用來格式化數據以便寫入文件。一樣,Android提供了建立和解析XML文件的類和方法。
CriminalIntent應用中,保存的數據格式是JSON。咱們將使用Context
類的I/O方法寫入或讀取文件。圖17-1整體描繪了應如何實現CriminalIntent應用數據的保存與讀取。
圖17-1 CriminalIntent應用的數據存取
應用讀取文件的最便捷方式是使用Context
類的I/O方法。這些方法能夠返回標準的Java類實例,如java.io.File和java.io.FileInputStream。(Context
類幾乎是全部關鍵應用組件的超類,常見的幾個應用組件有:Application
、Activity
和Service
。)
在CriminalIntent應用中,CrimeLab
類將負責觸發數據的保存與加載,而建立和解析JSON數據的工做則交由新的CriminalIntentJSONSerializer
類以及當前的Crime
類處理。
建立CriminalIntentJSONSerializer
類
新的CriminalIntentJSONSerializer
類負責讀取Crime
數組列表中的數據,而後進行數據格式轉換,最後寫入JSON文件。
建立CriminalIntentJSONSerializer
類,參照代碼清單17-1輸入實現代碼(注: 代碼中toJSON()方法會產生錯誤,由於稍後咱們纔會在Crime
類中實現該方法。暫時忽略它)。
代碼清單17-1 編碼實施CriminalIntentJSONSerializer
類
有個問題,須要討論:
Q:爲何不將對象序列化放到
CrimeLab
類中完成,而是單獨放到CriminalIntentJSONSerializer
類中實現?A:雖然對象序列化也能夠直接在
CrimeLab
類中完成,但將其封裝到獨立的單元會有諸多優勢:
- 首先,對應用中其餘代碼部分的依賴度較低,獨立封裝類更容易進行單元測試。
- 其次,
CriminalIntentJSONSerializer
類的構造方法可接受Context
實例參數。這意味着該類不作任何修改就能夠在多處複用,由於使用者只需實現任意一個Context類做爲參數傳入便可。
在saveCrimes(ArrayList<Crime>)
方法中,應首先建立一個JSONArray
數組對象。而後針對數組列表中的全部crime記錄調用toJSON()
方法,並將結果保存到JSONArray
數組中。
要打開文件並寫入數據,需使用Context.openFileOutput()
方法。該方法接受文件名以及文件操做模式參數,會自動將傳入的文件名附加到應用沙盒文件目錄路徑以後,造成一個新路徑,而後在新路徑下建立並打開文件,等待數據寫入。如選擇手動獲取私有文件目錄並在其下建立和打開文件,記得老是使用Context.getFilesDir()
替代方法。不過,如需建立不一樣使用權限的文件,仍是少不了要使用openFileOutput()
方法。
新建文件打開後,便可使用標準的Java接口類來寫入數據。這裏,咱們使用了java.io
類包中的三個類:Writer
、OutputStream
和OutputStreamWriter
。整個過程大體描述以下:
openFileOutput()
方法得到OutputStream
對象, OutputStreamWriter
對象, OutputStreamWriter
的寫入方法寫入數據。至於Java的Strings
與最終寫入文件的原始字節流之間的轉換,則沒必要擔憂,OutputStreamWriter
會搞定一切。
實現Crime
類的JSON序列化功能
爲了以JSON文件格式保存mCrimes
數組,首先必須能以JSON文件格式保存單個Crime實例對象。在Crime.java中,添加下列常量,而後實現toJSON()
方法,以JSON格式保存Crime
對象,並返回可放入JSONArray
的JSONObject
類實例,如代碼清單17-2所示。
代碼清單17-2 實現toJSON()
方法(Crime.java)
以上代碼中,使用JSONObject
類中的方法,咱們將Crime
對象數據轉換爲可寫入JSON文件的JSONObject
對象數據。
在CrimeLab
類中保存crime記錄
有了CriminalIntentJSONSerializer
類以及支持JSON序列化的Crime
類,如今可將crime列表轉換爲JSON格式,並保存到文件中。
什麼時「點」保存數據合適呢?適用於移動應用的一個廣泛規則是:儘量頻繁地保存數據,尤爲是用戶數據修改行爲發生時。既然修改crime記錄後的數據更新都需CrimeLab
類處理,那麼最靠譜的就是在該類中將數據保存到文件中。
若是數據保存過於頻繁,會拖慢應用的運行,影響到用戶的使用體驗。咱們的代碼中,數據只要有更新,都是從新將所有crime數據寫入文件中。考慮到CriminalIntent應用的規模,這樣作不會太耗時。然而,對於超頻繁數據保存的應用來講,應考慮採用某種方式只保存修改過的數據,而不是每次都保存所有數據,好比說使用SQLite數據庫等。後幾章咱們將學習如何在應用中使用SQLite數據庫。
在CrimeLab.java中,在類構造方法裏建立一個CriminalIntentJSONSerializer
實例。而後再添加一個序列化crime對象的saveCrimes()方法。同時,爲確認文件保存操做是否成功,再添加一些相應的日誌記錄代碼。如代碼清單17-3所示。
代碼清單17-3 在CrimeLab
類中進行數據持久保存(CrimeLab.java)
簡單起見,CriminalIntent應用只記錄錯誤信息並輸出至控制檯。實際開發時,如文件保存失敗,最好考慮採用某種方式直接提醒用戶,例如,使用Toast
或對話框。
在onPause()
方法中保存應用數據
應該在哪裏調用saveCrimes()
方法呢?onPause()
生命週期方法是最安全的選擇,如代碼清單17-4所示。爲何不選擇onStop()
或者onDestroy()
方法?前面咱們講過,操做系統須要回收內存時,會銷燬暫停的activity,所以不該考慮它們,不然將會失去保存數據的機會。
代碼清單17-4 在onPause()
方法中保存數據(CrimeFragment.java)
運行CriminalIntent應用。添加一兩條crime記錄,而後點擊Home鍵暫停activity,保存crime記錄列表到文件中。最後查看LogCat確認成功與否。
如今咱們來進行逆向操做,實現應用啓動後,從文件中讀取crime數據。首先,在Crime.java中,添加一個接受JSONObject
對象的構造方法,如代碼清單17-5所示。
代碼清單17-5 實現Crime(JSONObject)
方法(Crime.java)
而後,在CriminalIntentJSONSerializer.java中,添加一個從文件中加載crime記錄的loadCrimes()方法,如代碼清單17-6所示。
代碼清單17-6 實現loadCrimes()
方法(CriminalIntentJSONSerializer.java)
以上代碼能夠看到,聯合使用Java、JSON類,以及Context
的openFileInput(...)
方法,咱們從文件中讀取數據並轉換爲JSONObjects
類型的string
,而後再解析爲JSONArray
,接着再解析爲ArrayList
,最後返回得到的ArrayList
。
注意,在finally代碼塊中,應調用reader.close()
方法。這樣,即便發生錯誤,也能夠保證完成底層文件句柄的釋放。
最後,在CrimeLab
的構造方法中,在應用首次訪問單例對象時,代替老是建立空的crime數組列表,將crime數據加載到ArrayList
數組列表中。在CrimeLab.java中,完成相應的代碼修改,如代碼清單17-7所示。
代碼清單17-7 加載crime記錄(CrimeLab.java)
以上代碼中,咱們首先嚐試加載crime數據。如加載失敗,則新建一個空數組列表。
如今,CriminalIntent應用能夠保存應用啓停間的數據了。咱們可模擬一些不一樣場景進行測試。運行應用,添加幾條crime記錄,或修改現有記錄,而後切換到其餘應用,如網絡瀏覽器。此時,CriminalIntent應用極可能會被操做系統關閉。從新啓動它,檢查更新的數據是否已保存。也可測試強制關閉應用,而後從Eclipse中從新啓動應用的場景。
如今能夠放心地記錄各類使人討厭的辦公室陋習了。既然應用已可靠地實現了數據持久化,後續CriminalIntent應用的功能升級過程當中,可直接使用已保存的crime記錄。今後,咱們不再須要在每次應用啓動後,反反覆覆地添加crime記錄了。