第 17 章 存儲與加載本地文件

請參考教材,全面理解和完成本章節內容... ... 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卡上。但出於安全考慮,一般不推薦這麼作。這其中最重要的一個因素就是,外部存儲上的數據存取並不只僅侷限於應用自己,也就是說,任何人均可以讀取、寫入以及刪除這些數據。 學習

17.1 CriminalIntent 應用的數據存取

爲應用添加數據持久存儲功能主要涉及兩大處理過程:將數據保存至文件系統、以及應用啓動時從新加載保存的數據。每一個處理過程又分爲兩個步驟。保存數據時,首先將數據轉換爲可保存格式(如JSON數據格式),而後將數據寫入文件;讀取數據時,則恰好相反,首先從文件中讀取格式化的數據,而後將其解析爲應用所需的內容。

提示:

JSONJavaScript Object NotationXML是一種比較流行的數據交換格式

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應用數據的保存與讀取。

image

17-1 CriminalIntent應用的數據存取

應用讀取文件的最便捷方式是使用Context類的I/O方法。這些方法能夠返回標準的Java類實例,如java.io.File和java.io.FileInputStream。(Context類幾乎是全部關鍵應用組件的超類,常見的幾個應用組件有:ApplicationActivityService。)

17.1.1 保存crime數據到JSON文件

在CriminalIntent應用中,CrimeLab類將負責觸發數據的保存與加載,而建立和解析JSON數據的工做則交由新的CriminalIntentJSONSerializer類以及當前的Crime類處理。

建立CriminalIntentJSONSerializer

新的CriminalIntentJSONSerializer類負責讀取Crime數組列表中的數據,而後進行數據格式轉換,最後寫入JSON文件。

建立CriminalIntentJSONSerializer類,參照代碼清單17-1輸入實現代碼(注: 代碼中toJSON()方法會產生錯誤,由於稍後咱們纔會在Crime類中實現該方法。暫時忽略它)。

代碼清單17-1 編碼實施CriminalIntentJSONSerializer

image

有個問題,須要討論:

Q:爲何不將對象序列化放到CrimeLab類中完成,而是單獨放到CriminalIntentJSONSerializer類中實現?

A:雖然對象序列化也能夠直接在CrimeLab類中完成,但將其封裝到獨立的單元會有諸多優勢:

  • 首先,對應用中其餘代碼部分的依賴度較低,獨立封裝類更容易進行單元測試。
  • 其次,CriminalIntentJSONSerializer類的構造方法可接受Context實例參數。這意味着該類不作任何修改就能夠在多處複用,由於使用者只需實現任意一個Context類做爲參數傳入便可。

saveCrimes(ArrayList<Crime>)方法中,應首先建立一個JSONArray數組對象。而後針對數組列表中的全部crime記錄調用toJSON()方法,並將結果保存到JSONArray數組中。

要打開文件並寫入數據,需使用Context.openFileOutput()方法。該方法接受文件名以及文件操做模式參數,會自動將傳入的文件名附加到應用沙盒文件目錄路徑以後,造成一個新路徑,而後在新路徑下建立並打開文件,等待數據寫入。如選擇手動獲取私有文件目錄並在其下建立和打開文件,記得老是使用Context.getFilesDir()替代方法。不過,如需建立不一樣使用權限的文件,仍是少不了要使用openFileOutput()方法。

新建文件打開後,便可使用標準的Java接口類來寫入數據。這裏,咱們使用了java.io類包中的三個類:WriterOutputStreamOutputStreamWriter。整個過程大體描述以下:

  • 首先調用openFileOutput()方法得到OutputStream對象,
  • 而後用其建立一個新的OutputStreamWriter對象,
  • 最後調用OutputStreamWriter的寫入方法寫入數據。

至於Java的Strings與最終寫入文件的原始字節流之間的轉換,則沒必要擔憂,OutputStreamWriter會搞定一切。

實現Crime類的JSON序列化功能

爲了以JSON文件格式保存mCrimes數組,首先必須能以JSON文件格式保存單個Crime實例對象。在Crime.java中,添加下列常量,而後實現toJSON()方法,以JSON格式保存Crime對象,並返回可放入JSONArrayJSONObject類實例,如代碼清單17-2所示。

代碼清單17-2 實現toJSON()方法(Crime.java)

image

以上代碼中,使用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)

image

簡單起見,CriminalIntent應用只記錄錯誤信息並輸出至控制檯。實際開發時,如文件保存失敗,最好考慮採用某種方式直接提醒用戶,例如,使用Toast或對話框。

onPause()方法中保存應用數據

應該在哪裏調用saveCrimes()方法呢?onPause()生命週期方法是最安全的選擇,如代碼清單17-4所示。爲何不選擇onStop()或者onDestroy()方法?前面咱們講過,操做系統須要回收內存時,會銷燬暫停的activity,所以不該考慮它們,不然將會失去保存數據的機會。

代碼清單17-4 在onPause()方法中保存數據(CrimeFragment.java)

image

運行CriminalIntent應用。添加一兩條crime記錄,而後點擊Home鍵暫停activity,保存crime記錄列表到文件中。最後查看LogCat確認成功與否。

17.1.2 從文件中讀取crime數據

如今咱們來進行逆向操做,實現應用啓動後,從文件中讀取crime數據。首先,在Crime.java中,添加一個接受JSONObject對象的構造方法,如代碼清單17-5所示。

代碼清單17-5 實現Crime(JSONObject)方法(Crime.java)

image

而後,在CriminalIntentJSONSerializer.java中,添加一個從文件中加載crime記錄的loadCrimes()方法,如代碼清單17-6所示。

代碼清單17-6 實現loadCrimes()方法(CriminalIntentJSONSerializer.java)

image

以上代碼能夠看到,聯合使用Java、JSON類,以及ContextopenFileInput(...)方法,咱們從文件中讀取數據並轉換爲JSONObjects類型的string,而後再解析爲JSONArray,接着再解析爲ArrayList,最後返回得到的ArrayList

注意,在finally代碼塊中,應調用reader.close()方法。這樣,即便發生錯誤,也能夠保證完成底層文件句柄的釋放。

最後,在CrimeLab的構造方法中,在應用首次訪問單例對象時,代替老是建立空的crime數組列表,將crime數據加載到ArrayList數組列表中。在CrimeLab.java中,完成相應的代碼修改,如代碼清單17-7所示。

代碼清單17-7 加載crime記錄(CrimeLab.java)

image

以上代碼中,咱們首先嚐試加載crime數據。如加載失敗,則新建一個空數組列表。

如今,CriminalIntent應用能夠保存應用啓停間的數據了。咱們可模擬一些不一樣場景進行測試。運行應用,添加幾條crime記錄,或修改現有記錄,而後切換到其餘應用,如網絡瀏覽器。此時,CriminalIntent應用極可能會被操做系統關閉。從新啓動它,檢查更新的數據是否已保存。也可測試強制關閉應用,而後從Eclipse中從新啓動應用的場景。

如今能夠放心地記錄各類使人討厭的辦公室陋習了。既然應用已可靠地實現了數據持久化,後續CriminalIntent應用的功能升級過程當中,可直接使用已保存的crime記錄。今後,咱們不再須要在每次應用啓動後,反反覆覆地添加crime記錄了。

相關文章
相關標籤/搜索