1. 程式人生 > >android 資料儲存的幾種方式

android 資料儲存的幾種方式

總體的來講,資料儲存方式有三種:一個是檔案,一個是資料庫,另一個則是網路。其中檔案和資料庫可能用的稍多一些,檔案用起來較為方便,程式可以自己定義格式;資料庫用起稍煩鎖一些,但它有它的優點,比如在海量資料時效能優越,有查詢功能,可以加密,可以加鎖,可以跨應用,跨平臺等等;網路,則用於比較重要的事情,比如科研,勘探,航空等實時採集到的資料需要馬上通過網路傳輸到資料處理中心進行儲存並進行處理。 


對於Android平臺來講,它的儲存方式也不外乎這幾種,按方式總體來分,也是檔案,資料庫和網路。但從開發者的角度來講它可以分為以下五種方式: 


1.SharedPreferences共享偏好 
2.Internal Storage內部儲存空間 

3.External Storage外部儲存空間 
4.SQLite Database資料庫 
5.Internet網路 
這幾種方式各自有各自的優點和缺點,要根據不同的實際情況來選擇,而無法給出統一的標準。下面就各種方式談談它們的優缺點,以及最合適的使用情況: 


1.Shared Preferences共享偏好 
SharedPreferences是用來儲存一些Key/Value類似的成對的基本資料型別,注意,它只能儲存基本資料型別,也即int, long, boolean, String, float。事實上它完全相當於一個HashMap,唯一不同的就是HashMap中的Value可以是任何物件,而SharedPreferences中的值只能儲存基本資料型別(primitive types)。 



對於它的使用方法,可以參考Android Developer Guide,這裡不重複。 

如此來看,最適合SharedPreferences的地方就是儲存配置資訊,因為很多配置資訊都是Key/Value。事實上,在Android當中SharedPreferences使用最多的地方也是用來儲存配置(Settings)資訊,系統中的Settings中這樣,各個應用中的Settings也是這樣。並且,Android中為了方便的使用SharedPreferences儲存配置資訊,它來專門有PreferenceActivity用來封裝。也就是說如果你想在應用程式中建立配置(Settings),你可以直接使用PreferenceActivity和一些相關的專門為Preference封裝的元件,而不用再直接去建立,讀取和儲存SharedPreference,Framework中的這些元件會為你做這些事。 


再談談一些使用SharedPreference時的技巧,它只能儲存基本資料型別,但假如我想儲存一個數組,怎麼辦?可以把資料進行處理,把它轉化成一個String,取出的時候再還原就好了;再如,如想儲存一個物件,怎麼辦,同樣,可以把物件序列化成為字元序列,或轉成String(Object.toString()),或是把它的HashCode(Object.hashCode())當成Value儲存進去。 

總之,SharedPreferences使用起來十分的方便,可以靈活應用,因為它簡單方便,所以能用它就儘量不要用檔案或是資料庫。 


1.Internal Storage內部儲存空間 
所謂的內部儲存與外部儲存,是指是否是手機內建。手機內建的儲存空間,稱為內部儲存,它是手機一旦出廠就無法改變,它也是手機的硬體指標之一,通常來講手機內建儲存空間越大意味著手機價格會越貴(很多地方把它稱為手機記憶體,但我們做軟體的知道,這並不準確,記憶體是指手機執行時儲存程式,資料和指令的地方;這裡應該是手機內部儲存的簡稱為記憶體,而並非嚴格意義上的記憶體)。 

內部儲存空間十分有限,因而顯得可貴,所以我們要儘可能避免使用;另外,它也是系統本身和系統應用程式主要的資料儲存所在地,一旦內部儲存空間耗盡,手機也就無法使用了。所以對於內部儲存空間,我們要儘量避免使用。上面所談到的Shared Preferences和下面要談到的SQLite資料庫也都是儲存在內部儲存空間上的。 

Android本身來講是一個Linux作業系統,所以它的內部儲存空間,對於應用程式和使用者來講就是“/data/data"目錄。它與其他的(外部的儲存)相比有著比較穩定,儲存方便,操作簡單,更加安全(因為可以控制訪問許可權)等優點。而它唯一的缺點就是它比較有限,比較可貴。 

雖然,可以非常容易的知道程式本身的資料所在路徑,所有的應用程式的資料路徑都是“/data/data/app-package-name/”,所有的程式用到的資料,比如libs庫,SharedPreferences都是存放在這個路徑下面。但我們在使用的時候最好不要,或是千萬不要直接引用這個路徑。 

使用內部儲存主要有二個方式,一個是檔案操作,一個是資料夾操作。無論哪種方式,Context中都提供了相應的函式來支援,使用Context不但操作簡單方便,最重要的是Context會幫助我們管理這些檔案,也可以方便幫助我們控制檔案的訪問許可權。先來系統的說下Context中關於檔案和資料夾操作的函式有哪些。 

       a. 建立一個檔案,並開啟成一個檔案輸出流,需要提供一個String,作為檔名 

1.FileOutputStream  output = Context.openOutputFile(filename, Context.MODE_PRIVATE);   
2.output.write(data);// use output to write whatever you like    
3.output.close();  

1.FileOutputStream  output = Context.openOutputFile(filename, Context.MODE_PRIVATE);     output.write(data);// use output to write whatever you like     output.close();  
       b.  同樣,想開啟一個檔案作為輸入的話,也是隻需要提供檔名 



1.FileInputStream input = Context.openInputFile(filename);   
2.input.read();   
3.input.close();  

1.FileInputStream input = Context.openInputFile(filename);     input.read();     input.close();  
       c.  列出所有的已建立的檔案 


1.String[] files = Context.fileList();   
2.for (String file : files) {   
3.    Log.e(TAG, "file is " + file);   
4.}  

1.String[] files = Context.fileList();     for (String file : files) {         Log.e(TAG, "file is " + file);     }  
      d.  刪除檔案,能建立就要能夠刪除,當然也會提供了刪除檔案的介面,它也非常簡單,只需要提供檔名 



1.if (Context.deleteFile(filename)) {   
2.   Log.e(TAG, "delete file " + filename + " sucessfully“);   
3.} else {   
4.   Log.e(TAG, "failed to delete file " + filename);   
5.}  

1.if (Context.deleteFile(filename)) {        Log.e(TAG, "delete file " + filename + " sucessfully“);     } else {        Log.e(TAG, "failed to delete file " + filename);     }  

      e.  獲取檔案已建立檔案的路徑,它返回一個檔案物件用於操作路徑 



1.File fileDir = Context.getFileDir();   
2.Log.e(TAG, "fileDir " + fileDir.getAbsolutePath();  

1.File fileDir = Context.getFileDir();     Log.e(TAG, "fileDir " + fileDir.getAbsolutePath();  
      f.  建立一個目錄,需要傳入目錄名稱,它返回 一個檔案物件用到操作路徑 


1.File workDir = Context.getDir(dirName, Context.MODE_PRIVATE);   
2.Log.e(TAG, "workdir " + workDir.getAbsolutePath();  

1.File workDir = Context.getDir(dirName, Context.MODE_PRIVATE);       Log.e(TAG, "workdir " + workDir.getAbsolutePath();  
       g. 以File物件方式檢視所建立檔案,需要傳入檔名,會返回檔案物件 



1.File store = Context.openFileStreamPath(filename);   
2.Log.e(TAG, "store " + store.length());  

1.File store = Context.openFileStreamPath(filename);       Log.e(TAG, "store " + store.length());  
      h. 獲取Cache路徑,無需要傳入引數,返回檔案物件 



1.File cachedir = Context.getCacheDir();   
2.Log.e(TAG, "cachedir " + cacheDir.getAbsolutePath());  

1.File cachedir = Context.getCacheDir();      Log.e(TAG, "cachedir " + cacheDir.getAbsolutePath());  


總結一下檔案相關操作,可以得出以下三個特點: 
      1. 檔案操作只需要向函式提供檔名,所以程式自己只需要維護檔名即可; 
      2. 不用自己去建立檔案物件和輸入、輸出流,提供檔名就可以返回File物件或輸入輸出流 

      3. 對於路徑操作返回的都是檔案物件。 

如前所述,內部儲存空間有限,可貴,安全,穩定,所以應該用來儲存比較重要的資料,比如使用者資訊資料,口令祕碼等不需要與其他應用程式共享的資料。也可以用來建立臨時檔案,但一定要注意及時刪除。另外,對於內部儲存還有一個非常重要的特點,那就是在應用程式被解除安裝時,應用程式在內部儲存空間的檔案資料將全部被刪除。系統這樣做的原因很簡單,就是因為內部儲存很有限,它必須保證它的可用性,因為一旦添滿,系統將無法再正常工作。 


1.External Storage外部儲存空間 
再來談談手機外部儲存空間,與內部儲存空間相對,外部儲存空間是指手機出廠的時候不存在,使用者在使用時候可以自由新增的外部儲存介質比如TS卡,SD卡等快閃記憶體儲介質。這些快閃記憶體介質由最初的空間小价格貴,到現在的大容量價格便宜,所以幾乎每個支援外部儲存的手機上面都有大容量(大於等於2G)的快閃記憶體卡。 

Android也是不例外,它完全支援外部儲存介質。其實更確切的說,它是要依賴於外部儲存卡的,因為對於Android系統,如果沒有外部儲存卡,很多的系統應用無法使用,比如多媒體相關的應用程式無法使用。雖然Android很依賴,但是外部儲存卡也有它自身的特點,它最大的優點就是儲存空間大,基本上你可無限制的使用,也不怎麼擔心去清除資料。就目前來看,很多程式都在使用外部儲存卡,但很少有程式去主動清理資料,所以無論你的SD卡有多大,它的可用空間卻越來越少。與內部儲存不同的是,當程式解除安裝時,它在外部儲存所建立的檔案資料是不會被清除的。所以清理外部儲存空間的責任丟給了使用者自己,每隔一段時間就去檢視下SD卡,發現無用資料立馬刪除。外部儲存的缺點就是不是很穩定,對於Android手機來講可以說,很不穩定,本身快閃記憶體介質就容易出問題,SD卡處於不能正常使用的狀態十分多。 

先來說說外部儲存相關的使用方法和API: 

    a. Check media availability檢查介質的可用性 

        如前所述,外部儲存介質的穩定性十分的差,所以在使用之前一定要先檢查它的可用性,如果可用再去用 


view plaincopy to clipboardprint? 
1.final String state = Environment.getExternalStorageState();   
2.if (state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_READ_ONLY)) {// sd card is ready to us }  
view plaincopy to clipboardprint? 
1.final String state = Environment.getExternalStorageState();      if (state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_READ_ONLY)) {// sd card is ready to us }  
      final String state = Environment.getExternalStorageState();      if (state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_READ_ONLY)) {// sd card is ready to us }        b. Get the directory獲取外部儲存卡的路徑 

        事實上,外部儲存卡的路徑是“/mnt/sdcard",所以你直接這樣寫去訪問也能訪問的到。鑑於可讀性和可移植性的考慮,建議這樣寫: 


view plaincopy to clipboardprint? 
1.File sdcardDir = Environment.getExternalStorageDirectory();  
view plaincopy to clipboardprint? 
1.File sdcardDir = Environment.getExternalStorageDirectory();  
    File sdcardDir = Environment.getExternalStorageDirectory();      c. For API 8 or greater, there are some other useful APIs helping to manager files and directories. 

         如果你使用API 8(Android 2.2)或者更高,那麼SDK中又多了幾個操作外部儲存檔案和路徑的介面,文件中也建議開始者更加規範的使用SD卡。比如,建立相應的目錄去儲存相應的資料,Music,Picture,Video等。應用程式目錄也變成了"/Android/data/package-name/data"。具體的使用可以參考文件,這裡不重複。當然,就像程式設計規範一樣,這裡只是規範,你完全可以不遵守它,但出於可讀性和可移植性,還是建議按照文件建議的去做。 

下面總結一下使用時應該注意的一些和外部儲存的特點: 

     a. 外部儲存卡不是隨時想用就能夠用的,所以一定要記得在使用之前檢查它的可用性 

     b. 儲存在外部儲存卡上的資料是所有應用程式都可見,使用者也可見(使用FileManager),所以安全性不是很好,雖然文件聲稱可以在外部儲存卡上寫程式私有資料,但貌似沒用,用FileManager仍然可以刪除或編輯檔案(Market上面的FileManager功能都十分的強大,能讓使用者看到SD卡中的所有檔案,和操作能看到的檔案)。 

     c. Android手機支援把外部儲存卡Mount至PC做為U盤,當連線資料線時,這時SD卡變成了U盤連線到了另外的作業系統中。什麼意思,就是在Android當中雖然有的檔案屬性(隱藏,私有等),到了PC上就不一定管用了,使用者在PC上可以隨意操作檔案(這就是第二點中所提及的)。 

     d. 如果使用外部儲存卡儲存資料,一定要額外做好異常處理:外部儲存卡不可用時把資料存入哪裡;可用的時候再怎麼同步資料(這是比較頭疼的地方,可行的做法就是當SD卡不可用時不準使用者寫資料,但這使用者體驗又不是很好,但如你所知,很多應用都這麼幹);你的資料被破壞了。當然常見的異常也要考慮,比如空間滿了,無法寫入,磁碟壞道等。 

1.SQLite Database資料庫 
Android對資料庫的支援很好,它本身集成了SQLite資料庫,每個應用都可以方便的使用它,或者更確切的說,Android完全依賴於SQLite資料庫,它所有的系統資料和用到的結構化資料都儲存在資料庫中。 

它具有以下優點: 

     a. 效率出眾,這是無可否認的 

     b. 十分適合儲存結構化資料 

     c. 方便在不同的Activity,甚至不同的應用之間傳遞資料 

        先前有一篇文章講到了不同Activity和不同應用之間傳遞資料的麻煩,特別是對於大型資料結構,因為Activity雖是Java物件,但去無法像使用其他類物件那樣去建立一個例項然後使用它,更無法給Activity加上Setters和Getters(雖然這樣做了沒有編譯錯誤)。比較好的解決方案就是把結構化資料寫入資料庫,然後在不同的Activity之間傳遞它們的Uri。 

     d. 由專門的ContentProvider來幫忙管理和維護資料庫 

     e. 可以方便的設定訪問許可權,私有還是都可見 

     f.  操作方便,使用標準的CRUDE語句,ContentResolver.query(), update(), delete() insert(),詳見ContentResolver 

     g. 良好的可移植性和通用性,用標準的SQL語句就能實現CRUDE 

對於它的使用方法可以去參考文件,這裡也說不清楚。 

1.Internet網路 
網路是比較不靠譜的一個,因為移動終端的網路穩定性,以及所產生的流量讓人傷不起,使用者更傷不起。但若是對於非常重要的實時資料,或是需要傳送給遠端伺服器處理的,也可以考慮使用網路實時傳送。這已經有先例了,Apple和Google就是這樣,iPhone裝置和Android裝置都會在使用者不知情的情況 下收集使用者的資訊,然後又在使用者不知情的情況 下發送到Apple和Google的伺服器上,也就是所謂的“跟蹤門”。除此之外,智慧手機(特別是Android和火熱的iPhone)上面的應用程式都會偷偷的在後臺執行,收集使用者資料,然後再偷偷的發伺服器,直接傷害是使用者流量,請看先前的文章。 




對比這幾種方式,可以總結下: 

     1. 簡單資料和配置資訊,SharedPreference是首選; 

     2. 如果SharedPreferences不夠用,那麼就建立一個數據庫 

     3. 結構化資料,一定要建立資料庫,雖然這稍顯煩鎖,但是好處無窮 

     4. 檔案就是用來儲存檔案(也即非配置資訊或結構化資料),如文字檔案,二進位制檔案,PC檔案,多媒體檔案,下載的檔案等等。 

     5. 儘量不要建立檔案 

     6. 如果建立檔案,如果是私密檔案或是重要檔案,就儲存在內部儲存,否則放到外部儲存 

     7. 不要收集使用者資料,更不要發到網路上,雖然你們也有很多無奈。使用者也無奈,也無辜,但更無助 


平臺為開發者準備了這麼多的方式固然是一件好事,但我們要認清每一種的優點和缺點,根據實際情況選擇最合適的。還有一個原則就是最簡單原則,也就是說能用簡單的方式處理,就不要用複雜的方式。比如儲存幾個資料或簡單物件,用SharedPreference也能做到,何必還去寫個ContentProvider呢?