Android SDK簡明教程:應用程式資料

51CTO發表於2014-11-25

在本系列教程當中,我們將學習如何從零開始進行Android SDK開發。我們已經熟悉了Android應用程式的結構與基本組成元素,其中包括資源、清單與使用者介面。在著手進行Android平臺的功能性應用開發之後,大家肯定需要儲存這樣或者那樣的資料資訊。Android平臺提供多種選項,用於打理應用程式中的資料儲存任務,而這正是今天這篇文章要討論的核心內容。

介紹

從廣義上講,Android應用中的資料儲存選項共有五種主要型別:將資料儲存在應用的共享偏好當中、儲存在內部儲存(專屬於應用本身)當中、儲存在外部儲存(向裝置公開)當中、儲存在資料庫當中以及儲存在可通過裝置網際網路連線訪問的Web資源當中。受篇幅所限,我們無法詳細對這些選項作出論述,但會對每種方案的基礎特性加以概括、從而幫助大家在需要使用持久化資料時理清儲存問題的解決思路。

1. 共享偏好

第一步

共享偏好允許大家以鍵-值對的形式儲存基本資料型別。應用程式的共享偏好檔案通常被視為最簡單的資料儲存選項,但從本質上說它對於儲存物件提出了一定程度的限制。大家可以通過它儲存基本型別數字(如整數、長數以及浮點數字)、布林值以及文字字串。我們需要為自己儲存的每個數值分配一個名稱,從而在應用程式執行時據此對其進行檢索。由於大家很可能在自己建立的第一款應用中就用到共享偏好,因此我們人把它作為講解的重點、以更為詳盡的方式(相較於其它選項)進行表述,從而幫助各位鞏固必要知識。

大家可以在自己的主Activity類中嘗試這些程式碼,並在稍後執行本系列教程的應用示例時對其加以測試。在理想情況下,共享偏好應該可以符合應用程式中的使用者配置選項,如同選擇外觀設定一樣。大家應該還記得,我們曾經建立過一個簡單的按鈕,使用者點選它之後螢幕上會顯示出“Ouch”文字內容。現在讓我們假設自己希望使用者在點選一次之後,該按鈕上會持續顯示“Ouch”字樣,且該狀態在應用程式執行過程中始終保持不變。這意味著按鈕上的初始文字僅在使用者首次點選操作之前存在。

讓我們為應用程式新增共享偏好內容。在該類的起始位置、onCreate方法之前,我們為共享偏好選擇一個名稱:

public static final String MY_APP_PREFS = "MyAppPrefs";

利用“public static”修飾符,我們可以訪問處於應用內任何類中的這項變數,因此我們只需要將偏好名稱字串儲存在這裡即可。我們使用大寫是因為該變數屬於常數,“final”修飾符也是因此而存在。每一次檢索或者在應用程式偏好當中設定資料條目時,大家都必須使用同樣的名稱。

第二步

現在我們來編寫共享偏好內容。在我們的onClick方法中、按鈕“Ouch”文字設定部分的下方,嘗試通過名稱取回這條共享偏好:

SharedPreferences thePrefs = getSharedPreferences(MY_APP_PREFS, 0);

大家需要為“android.conent.SharedPreferences”類新增一條匯入。將滑鼠懸停在“SharedPreferences”文字上方,並利用Eclipse提示完成匯入。第一項引數是我們所定義的偏好名稱,第二項則是我們作為預設選項的基本模式。

現在我們需要為共享偏好指定一套編輯器,從而實現對其中數值的設定:

SharedPreferences.Editor prefsEd = thePrefs.edit();

現在我們可以向共享偏好當中寫入值了:

prefsEd.putBoolean(“btnPressed”, true);

這裡我們使用了布林型別,因為當前狀態只分為兩種——使用者已經或者尚未按下按鈕。編輯器提供多種不同型別,我們可以從中選擇以儲存這套共享偏好,其中每種方法都擁有自己的名稱與值引數。最後,我們需要提交編輯結果:

prefsEd.commit();

第三步

現在讓我們利用已經儲存的值來檢測使用者執行應用程式後,按鈕應該顯示什麼樣的內容。在onCreate中的現有程式碼之後新增共享偏好:

SharedPreferences thePrefs = getSharedPreferences(MY_APP_PREFS, 0);

這一次我們不必使用編輯器,因為我們只需要獲取一個值:

boolean pressed = thePrefs.getBoolean("btnPressed", false)

現在我們利用已經設定過的名稱檢索該值,並讀取變數中的結果。如果該值尚未被設定,返回的則為第二項引數,也就是預設值——代表否定含義。現在讓我們使用該值:

if(pressed) theButton.setText("Ouch");

如果使用者在應用程式執行之後按下該按鈕,則按鈕直接顯示“Ouch”字樣。在本系列的後續文章當中,大家會看到我們在應用執行中進行這一操作的情況。這個簡單的例子很好地詮釋了共享偏好的使用過程。大家會發現,共享偏好在幫助應用程式通過外觀及使用感受迎合使用者喜好方面具有重要的作用。

2. 私有內部檔案

第一步

大家可以將檔案儲存在使用者裝置的內部以及外部儲存當中。如果將檔案儲存在內部儲存中,Android系統會將其視為專屬於當前應用的私有資料。這類檔案基本上屬於應用程式的組成部分,我們無法在應用程式之外直接對其進行訪問。再有,如果應用程式被移除、這些檔案也會同時被清空。

大家可以利用以下輸出例程在記憶體儲存中建立一個檔案:

FileOutputStream fileOut = openFileOutput("my_file", Context.MODE_PRIVATE);

大家需要為“java.io.FileOutputStream”類進行匯入新增。我們提供了檔名稱與模式,選擇私有模式意味著該檔案將只能被該應用程式所使用。如果大家現在就把這部分程式碼加入到Activity當中,例如onClick方法中,Eclipse將彈出錯誤提示。這是因為當我們進行輸入/輸出操作時,應用程式可能遭遇一些需要應對的錯誤。如果大家的輸入/輸出操作無法解決這類錯誤,Eclipse就會提示異常狀況、應用程式也會中止執行。為了保證應用程式在這種情況下仍能正常執行,我們需要將自己的輸入/輸出程式碼封裝在try程式碼塊當中:

try{     FileOutputStream fileOut = openFileOutput("my_file", Context.MODE_PRIVATE); 
} catch(IOException ioe){     
Log.e("APP_TAG", "IO Exception", ioe); }

如果輸入/輸出操作導致異常,那麼catch塊中的上述程式碼就會付諸執行,從而將錯誤資訊寫入到日誌當中。大家今後會經常用到應用程式中的Log類(匯入‘android.util.Log’),它會記錄程式碼執行時所發生的具體情況。我們可以為字串標籤定義一個類變數,也就是上述程式碼中的第一條引數。這樣一旦出現錯誤,大家就可以在Android LogCat中檢視異常資訊了。

第二步

現在回到try塊,在建立了檔案輸出例程之後,大家可以嘗試將以下程式碼寫入檔案:

String fileContent = "my data file content"; 
fileOut.write(fileContent.getBytes());

在將所有必要內容寫入資料檔案之後,利用以下程式碼作為結尾:

fileOut.close();

第三步

當大家需要檢索內部檔案中的內容時,可以通過以下流程實現:

try{    
 FileInputStream fileIn = openFileInput("my_file");     
//read the file } catch(IOException ioe){     
Log.e("APP_TAG", "IO Exception", ioe); }

在try塊當中,利用利用緩衝讀取器讀取檔案內容:

InputStreamReader streamIn = new InputStreamReader(fileIn); 
BufferedReader fileRead = new BufferedReader(streamIn); 
StringBuilder fileBuild = new StringBuilder(""); 
String fileLine=fileRead.readLine(); while(fileLine!=null){    
 fileBuild.append(fileLine+"\n");     
fileLine=fileRead.readLine(); } 
String fileText = fileBuild.toString(); streamIn.close();

大家不要被其中所涉及的大量不同物件所嚇倒,這其實屬於標準的Java輸入/輸出操作。其中的while迴圈會在檔案中的每一行執行一次。在執行完成後,“fileText”變數將把檔案內容儲存為字串、以備我們直接使用。

3. 公共外部檔案

第一步

只要使用者裝置支援,我們的應用程式也可以將檔案儲存在外部儲存當中。外部儲存種類繁多,包括SD卡、其它行動式介質或者使用者無法移除但被系統認定為外部型別的記憶體儲存機制。當我們將檔案儲存在外部儲存中時,其內容將完全公開、大家也無法以任何方式阻止使用者或者其它應用對其進行訪問。

在我們嘗試將資料儲存在外部儲存中之前,必須首先檢查對應儲存機制是否可用——儘量避免意外狀況絕對是種好習慣:

String extStorageState = Environment.getExternalStorageState();

系統會將資訊以字串的形式返回,大家可以對其進行分析、並與Environment類中的外部儲存狀態欄位加以比對:

if(Environment.MEDIA_MOUNTED.equals(extStorageState)){     
//ok to go ahead and read/ write to external storage } 
else if(Environment.MEDIA_MOUNTED_READ_ONLY.equals(extStorageState)){    
 //can only read } else{     //cannot read or write }

即使裝置上確實存在外部儲存,我們也不能先入為主地假定應用可以向其寫入資料。

第二步

在證實了我們確實能夠向外部儲存寫入資料之後,大家接下來需要檢索目錄以指定檔案儲存的位置。以下應用程式設定內容指向八級及更高API:

File myFile = new File(getExternalFilesDir(null), "MyFile.txt");

這樣大家就可以對該檔案進行寫入與讀取了。不過也別忘了在專案的清單檔案中新增以下僅限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

隨著我們開發的應用程式變得愈發複雜,大家可能希望將自己儲存得到的檔案與其它應用共享。在這種情況下,大家可以使用公共目錄下的各類通用條目,例如圖片以及音樂檔案。

4. 資料庫

隨著我們的應用程式所涉及的複雜結構資料越來越多,共享偏好或者內部/外部檔案可能已經無法滿足實際需求,這時候大家就應該考慮使用資料庫方案了。Android支援開發人員在應用程式內部建立並訪問SQLite資料庫。在我們建立一套資料庫時,其將作為私有元件服務單純服務於相關應用程式。

在Android應用中利用SQLite資料庫的方法多種多樣,推薦大家使用擴充套件SQLiteOpenHelper的類來實現這方面需求。在該類當中,我們需要定義資料庫屬性、建立各種類變數(包括我們所定義的資料庫列表名稱及其SQL建立字串),具體程式碼如下所示:

private static final String NOTE_TABLE_CREATE =     "CREATE TABLE Note (noteID INTEGER PRIMARY KEY AUTOINCREMENT, " +    "noteTxt TEXT);";

這裡所舉的例子只涉及一套非常簡單的表格,其中包含兩列,一列內容為ID、另一列內容為文字;兩列都用於記錄使用者註釋資訊。在SQLiteOpenHelper類當中,大家可以重寫onCreate方法來建立自己的資料庫。在應用程式的其它部分當中,例如Activity類中,大家可以通過SQLiteOpenHelper實現對資料庫的訪問,並利用WritableDatabase方法插入新記錄、利用getReadableDatabase方法來查詢現有記錄,而後將結果顯示在應用程式UI當中。

在對查詢結果進行迭代時,我們的應用程式將使用Cursor類——該類會依次引用結果集中的每一行內容。

5. 網際網路資料

很多應用都會使用網際網路資料資源,而且某些應用甚至基本是由一套介面與大量Web資料來源所構成。大家可以利用使用者裝置上的網際網路連線來儲存並檢索來自Web的資料,只要網路連線有效、這一機制就能正常運作。為了實現這一目標,我們需要在自己的清單檔案中新增“android.permission.INTERNET”許可權。

如果我們希望自己的應用能夠從網際網路中獲取資料,則必須保證這一流程脫離應用主UI執行緒。利用AsyncTask,大家可以通過後臺程式的方式從Web源獲取資料、在資料下載完成後將結果寫入UI、最後讓UI正常執行自身功能。

大家還可以將一個內部AsyncTask類新增到Activity類當中,並在需要獲取資料的時候在該Activity中建立一個AsyncTask例項。通過在AsyncTask中引入doInBackground與onPostExecute兩種方法,大家可以檢索Activity中所獲取到的資料並將其寫入使用者介面。

獲取Web資料在應用開發工作當中屬於中等難度的任務,大家最好在熟練掌握了Android開發知識之後再進行嘗試。不過大家可能很快就會發現,這樣的資料獲取機制對不少應用都非常適合,因為這能有效利用使用者裝置的連線資源。Java與Android都提供相關工具,用於處理返回的結構化資料——例如JSON feed。

結論

在今天的文章中,我們基本瞭解了開發Android應用程式時需要接觸到的資料儲存方案。無論大家最終選擇哪種方案,都應該以實際需求作為參考標準,因為不同的方案只適合特定需求。在本系列教程的下一篇當中,我們將共同探討如何將物理裝置與已安裝的Eclipse相連、同時學習如何建立虛擬裝置。在此之後,我們還將探索如何讓應用程式執行在這兩種型別的裝置之上。順便向大家報告,再有兩篇文章本系列教程就將徹底結束;在最後一篇文章中,我們將研究通用類以及Android Activity生命週期,從而幫助大家做好開發應用程式的一切準備。

相關文章