Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android應用開發入門(三十一)數據持久化之內部存儲、Sdcard存儲介紹

Android應用開發入門(三十一)數據持久化之內部存儲、Sdcard存儲介紹

編輯:Android開發實例

前言

  之前一直在講AndroidUI的內容,但是還沒有完結,之後會慢慢補充。今天講講其他的,關於數據持久化的內容。對於一個應用程序而言,不可避免的要能夠對數據進行存儲,Android程序也不例外。而在Android中,提供了幾種實現數據持久化的方法。後面會分別介紹。

  在Android中,可以使用幾種方式實現數據持久化:

  • Shared Preferences:共享參數形式,一種以Key-Value的鍵值對形式保存數據的方式,Android內置的,一般應用的配置信息,推薦使用此種方式保存。
  • Internal Storage:使用Android設備自帶的內存存儲數據。
  • External Storage:使用外部存儲設備存儲數據,一般是指Sdcard。
  • SQLite Databases:以SQLite數據庫存儲結構化的數據。
  • Network Connection:使用基於網絡的服務獲取數據,可以參見另外一篇:http://www.fengfly.com/plus/view-213371-1.html。

  後面幾天會分別介紹以上幾種方式實現的數據持久化,對於SharedPreferences而言,之前寫過一篇博客,但是自己不是很滿意,之後有時間會再重新寫一份關於SharedPreferences的博客,有興趣的朋友可以先去看看,http://www.fengfly.com/plus/view-213364-1.html。今天先介紹Internal Storage以及External Storage。

Internal Storage

  內部存儲,在Android中,開發者可以直接使用設備的內部存儲器中保存文件,默認情況下,以這種方式保存的和數據是只能被當前程序訪問,在其他程序中是無法訪問到的,而當用戶卸載該程序的時候,這些文件也會隨之被刪除。

  使用內部存儲保存數據的方式,基本上也是先獲得一個文件的輸出流,然後以write()的方式把待寫入的信息寫入到這個輸出流中,最後關閉流即可,這些都是Java中IO流的操作。具體步驟如下:

  • 使用Context.openFileOutput()方法獲取到一個FileOutputStream對象。
  • 把待寫入的內容通過write()方法寫入到FileOutputStream對象中。
  • 最後使用close()關閉流。

  上面介紹的Context.openFileOutput()方法有兩個重載函數,它們的簽名分別是:

  • FileOutputStream openFileOutput(String name):以MODE_PRIVATE的模式打開name文件。
  • FileOutputStream openFileOutput(String name,int mode):以mode的模式打開name文件。

  上面第二個重載函數中,mode為一個int類型的數據,這個一般使用Context對象中設置好的常量參數,有如下幾個:

  • MODE_APPEND:以追加的方式打開一個文件,使用此模式寫入的內容均追加在原本內容的後面。
  • MODE_PRIVATE:私有模式(默認),如果文件已經存在會重新創建並替換原文件,如果不存在直接創建。
  • MODE_WORLD_READABLE:以只讀的方式打開文件。
  • MODE_WORLD_WRITEABLE:以只寫的方式打開文件。

  還有幾個方法需要特別注意一下,這幾個方法對於文件關系提供了更好的支持,配合上面介紹的方式,就可以對文件的數據進行常規的CRUD操作(增刪改查),方法如下:

  • File getFIlesDir():獲取文件系統的絕對路徑。
  • boolean deleteFile(String name):刪除一個指定文件名為name的文件。
  • String[] fileList():當前應用內部存儲路徑下的所有文件名。

   講了這麼多,下面通過一個簡單的Demo來演示一下上面提到的內容。在這個Demo中,指定文件名和內容,既可創建文件,並且可以對其內容進行追加、修改、刪除、查詢等操作。  

  布局代碼:

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3.     android:layout_width="match_parent" 
  4.     android:layout_height="match_parent" 
  5.     android:orientation="vertical" > 
  6.  
  7.     <TextView 
  8.         android:layout_width="match_parent" 
  9.         android:layout_height="wrap_content" 
  10.         android:text="file name:" /> 
  11.  
  12.     <EditText 
  13.         android:id="@+id/etInternalFilename" 
  14.         android:layout_width="match_parent" 
  15.         android:layout_height="wrap_content" /> 
  16.  
  17.     <TextView 
  18.         android:layout_width="match_parent" 
  19.         android:layout_height="wrap_content" 
  20.         android:text="Content:" /> 
  21.  
  22.     <EditText 
  23.         android:id="@+id/etInternalContent" 
  24.         android:layout_width="match_parent" 
  25.         android:layout_height="wrap_content" /> 
  26.  
  27.     <LinearLayout 
  28.         android:layout_width="match_parent" 
  29.         android:layout_height="wrap_content" 
  30.         android:orientation="horizontal" > 
  31.  
  32.         <Button 
  33.             android:id="@+id/btnInternalSave" 
  34.             android:layout_width="wrap_content" 
  35.             android:layout_height="wrap_content" 
  36.             android:text="save" /> 
  37.  
  38.         <Button 
  39.             android:id="@+id/btnInternalDelete" 
  40.             android:layout_width="wrap_content" 
  41.             android:layout_height="wrap_content" 
  42.             android:text="delete" /> 
  43.  
  44.         <Button 
  45.             android:id="@+id/btnInternalAppend" 
  46.             android:layout_width="wrap_content" 
  47.             android:layout_height="wrap_content" 
  48.             android:text="append" /> 
  49.  
  50.         <Button 
  51.             android:id="@+id/btnInternalQuery" 
  52.             android:layout_width="wrap_content" 
  53.             android:layout_height="wrap_content" 
  54.             android:text="query" /> 
  55.     </LinearLayout> 
  56. <!-- 以一個ListView的形式展示當前程序內部存儲路徑下的所有文件 --> 
  57.     <ListView 
  58.         android:id="@+id/lvInternalData" 
  59.         android:layout_width="match_parent" 
  60.         android:layout_height="fill_parent" > 
  61.     </ListView> 
  62.  
  63. </LinearLayout> 

   專門寫一個內部存儲的操作類,對其實現CRUD操作:  

  1. package com.example.internal;  
  2.  
  3. import java.io.ByteArrayOutputStream;  
  4. import java.io.File;  
  5. import java.io.FileInputStream;  
  6. import java.io.FileNotFoundException;  
  7. import java.io.FileOutputStream;  
  8. import java.io.IOException;  
  9.  
  10. import android.content.Context;  
  11. import android.os.Environment;  
  12. import android.util.Log;  
  13.  
  14. public class MyInternalStorage {  
  15.     //需要保存當前調用對象的Context  
  16.     private Context context;  
  17.  
  18.     public MyInternalStorage(Context context) {  
  19.         this.context = context;  
  20.     }  
  21.     /**  
  22.      * 保存內容到內部存儲器中  
  23.      * @param filename 文件名  
  24.      * @param content 內容  
  25.      */ 
  26.     public void save(String filename, String content) throws IOException {  
  27.         // FileOutputStream fos=context.openFileOutput(filename,  
  28.         // Context.MODE_PRIVATE);  
  29.         File file = new File(context.getFilesDir(), filename);  
  30.         FileOutputStream fos = new FileOutputStream(file);  
  31.  
  32.         fos.write(content.getBytes());  
  33.         fos.close();  
  34.     }  
  35.     /**  
  36.      *  通過文件名獲取內容  
  37.      * @param filename 文件名  
  38.      * @return 文件內容  
  39.      */ 
  40.     public String get(String filename) throws IOException {  
  41.         FileInputStream fis = context.openFileInput(filename);  
  42.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  43.         byte[] data = new byte[1024];  
  44.         int len = -1;  
  45.         while ((len = fis.read(data)) != -1) {  
  46.             baos.write(data, 0, len);  
  47.         }  
  48.         return new String(baos.toByteArray());  
  49.     }  
  50.     /**  
  51.      * 以追加的方式在文件的末尾添加內容  
  52.      * @param filename 文件名  
  53.      * @param content 追加的內容  
  54.      */ 
  55.     public void append(String filename, String content) throws IOException {  
  56.         FileOutputStream fos = context.openFileOutput(filename,  
  57.                 Context.MODE_APPEND);  
  58.         fos.write(content.getBytes());  
  59.         fos.close();  
  60.     }  
  61.     /**  
  62.      * 刪除文件  
  63.      * @param filename 文件名  
  64.      * @return 是否成功  
  65.      */ 
  66.     public boolean delete(String filename) {  
  67.         return context.deleteFile(filename);  
  68.     }  
  69.     /**  
  70.      * 獲取內部存儲路徑下的所有文件名  
  71.      * @return 文件名數組  
  72.      */ 
  73.     public String[] queryAllFile() {  
  74.         return context.fileList();  
  75.     }  
  76.  

   Activity代碼:

  1. package com.example.datastoragedemo;  
  2.  
  3. import java.io.IOException;  
  4.  
  5. import com.example.internal.MyInternalStorage;  
  6.  
  7. import android.app.Activity;  
  8. import android.os.Bundle;  
  9. import android.view.View;  
  10. import android.view.View.OnClickListener;  
  11. import android.widget.AdapterView;  
  12. import android.widget.AdapterView.OnItemClickListener;  
  13. import android.widget.ArrayAdapter;  
  14. import android.widget.Button;  
  15. import android.widget.EditText;  
  16. import android.widget.ListView;  
  17. import android.widget.Toast;  
  18.  
  19. public class InternalStorageActivity extends Activity {  
  20.     private EditText etFilename, etContent;  
  21.     private Button btnSave, btnQuery, btnDelete, btnAppend;  
  22.     private ListView lvData;  
  23.  
  24.     @Override 
  25.     protected void onCreate(Bundle savedInstanceState) {  
  26.         // TODO Auto-generated method stub  
  27.         super.onCreate(savedInstanceState);  
  28.         setContentView(R.layout.activity_internalstorage);  
  29.  
  30.         etFilename = (EditText) findViewById(R.id.etInternalFilename);  
  31.         etContent = (EditText) findViewById(R.id.etInternalContent);  
  32.         btnSave = (Button) findViewById(R.id.btnInternalSave);  
  33.         btnQuery = (Button) findViewById(R.id.btnInternalQuery);  
  34.         btnDelete = (Button) findViewById(R.id.btnInternalDelete);  
  35.         btnAppend = (Button) findViewById(R.id.btnInternalAppend);  
  36.         lvData = (ListView) findViewById(R.id.lvInternalData);  
  37.  
  38.         btnSave.setOnClickListener(click);  
  39.         btnQuery.setOnClickListener(click);  
  40.         btnDelete.setOnClickListener(click);  
  41.         btnAppend.setOnClickListener(click);  
  42.  
  43.     }  
  44.  
  45.     private View.OnClickListener click = new OnClickListener() {  
  46.  
  47.         @Override 
  48.         public void onClick(View v) {  
  49.             MyInternalStorage myInternal = null;  
  50.             String filename = null;  
  51.             String content = null;  
  52.             switch (v.getId()) {  
  53.             case R.id.btnInternalSave:  
  54.                 filename = etFilename.getText().toString();  
  55.                 content = etContent.getText().toString();  
  56.                 myInternal = new MyInternalStorage(InternalStorageActivity.this);  
  57.                 try {  
  58.                     myInternal.save(filename, content);  
  59.                     Toast.makeText(InternalStorageActivity.this, "保存文件成功",  
  60.                             Toast.LENGTH_SHORT).show();  
  61.                 } catch (IOException e) {  
  62.                     // TODO Auto-generated catch block  
  63.                     e.printStackTrace();  
  64.                     Toast.makeText(InternalStorageActivity.this, "保存文件失敗",  
  65.                             Toast.LENGTH_SHORT).show();  
  66.                 }  
  67.  
  68.                 break;  
  69.  
  70.             case R.id.btnInternalDelete:  
  71.                 filename = etFilename.getText().toString();  
  72.                 myInternal = new MyInternalStorage(InternalStorageActivity.this);  
  73.                 myInternal.delete(filename);  
  74.                 Toast.makeText(InternalStorageActivity.this, "刪除文件成功",  
  75.                         Toast.LENGTH_SHORT).show();  
  76.                 break;  
  77.             case R.id.btnInternalQuery:  
  78.                 myInternal = new MyInternalStorage(InternalStorageActivity.this);  
  79.                 String[] files = myInternal.queryAllFile();  
  80.                 ArrayAdapter<String> fileArray = new ArrayAdapter<String>(  
  81.                         InternalStorageActivity.this,  
  82.                         android.R.layout.simple_list_item_1, files);  
  83.                 lvData.setAdapter(fileArray);  
  84.                 Toast.makeText(InternalStorageActivity.this, "查詢文件列表",  
  85.                         Toast.LENGTH_SHORT).show();  
  86.                 break;  
  87.             case R.id.btnInternalAppend:  
  88.                 filename = etFilename.getText().toString();  
  89.                 content = etContent.getText().toString();  
  90.                 myInternal = new MyInternalStorage(InternalStorageActivity.this);  
  91.                 try {  
  92.                     myInternal.append(filename, content);  
  93.                     Toast.makeText(InternalStorageActivity.this, "文件內容追加成功",  
  94.                             Toast.LENGTH_SHORT).show();  
  95.                 } catch (IOException e) {  
  96.                     // TODO Auto-generated catch block  
  97.                     e.printStackTrace();  
  98.                     Toast.makeText(InternalStorageActivity.this, "文件內容追加失敗",  
  99.                             Toast.LENGTH_SHORT).show();  
  100.                 }  
  101.                 break;  
  102.             }  
  103.  
  104.         }  
  105.     };  
  106.  
  107.     private OnItemClickListener itemClick = new OnItemClickListener() {  
  108.  
  109.         @Override 
  110.         public void onItemClick(AdapterView<?> parent, View view, int position,  
  111.                 long id) {  
  112.             ListView lv = (ListView) parent;  
  113.             ArrayAdapter<String> adapter = (ArrayAdapter<String>) lv  
  114.                     .getAdapter();  
  115.             String filename = adapter.getItem(position);  
  116.             etFilename.setText(filename);  
  117.             MyInternalStorage myInternal = new MyInternalStorage(  
  118.                     InternalStorageActivity.this);  
  119.             String content;  
  120.             try {  
  121.                 content = myInternal.get(filename);  
  122.                 etContent.setText(content);  
  123.             } catch (IOException e) {  
  124.                 // TODO Auto-generated catch block  
  125.                 e.printStackTrace();  
  126.             }  
  127.  
  128.         }  
  129.     };  
  130.  

 效果展示,在示例中,先添加三個文件,最後刪除一個,分別查詢文件列表:

  使用內部存儲的方式進行數據持久化,文件的地址將保存在/data/data/<package_name>/files/路徑下,上面創建了三個文件,最後刪掉了一個,如果是使用的模擬器,可以直接在File Explorer中查看:

緩存(cache)

  既然提到了內部存儲,這裡再簡單的說說關於緩存文件(cache files)。cache files的操作與操作內部存儲中的文件方式基本一致,只是獲取文件的路徑有說不同。如果需要使用緩存的方式進行數據持久話,那麼需要使用Context.getCacheDir()方法獲取文件保存的路徑。

  對於緩存文件而言,當設備內部內存存儲空間不足的時候,Android會有自動刪除的機制刪除這些緩存文件,用來恢復可用空間,所以對於緩存文件而言,內容一般最好控制在1MB之下,並且也不要存放重要的數據,因為很可能下次去取數據的時候,已經被Android系統自動清理了。

External Storage

  使用外部存儲實現數據持久化,這裡的外部存儲一般就是指的是sdcard。使用sdcard存儲的數據,不限制只有本應用訪問,任何可以有訪問Sdcard權限的應用均可以訪問,而Sdcard相對於設備的內部存儲空間而言,會大很多,所以一般比較大的數據,均會存放在外部存儲中。

  使用SdCard存儲數據的方式與內部存儲的方式基本一致,但是有三點需要注意的:

  • 第一點,需要首先判斷是否存在可用的Sdcard,這個可以使用一個訪問設備環境變量的類Environment進行判斷,這個類提供了一系列的靜態方法,用於獲取當前設備的狀態,在這裡獲取是否存在有效的Sdcard,使用的是Environment.getExternalStorageState()方法,返回的是一個字符串數據,Environment封裝好了一些final對象進行匹配,除了Environment.MEDIA_MOUNTED外,其他均為有問題,所以只需要判斷是否是Environment.MEDIA_MOUNTED狀態即可。
  • 第二點,既然轉向了Sdcard,那麼存儲的文件路徑就需要相對變更,這裡可以使用Envir.getExternalStorageDirectory()方法獲取當Sdcard的根目錄,可以通過它訪問到相應的文件。
  • 第三點,需要賦予應用程序訪問Sdcard的權限,Android的權限控制尤為重點,在Android程序中,如果需要做一些越界的操作,均需要對其進行授權才可以訪問。在AndroidManifest.xml中添加代碼:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>   

  因為訪問Sdcard的方式和訪問內部存儲的方式差不多,這裡就展示一個Save的方法,用於保存文件,其他CRUD操作,這裡就不再一一給出了。

  1. public void saveToSdcard(String filename, String content) throws IOException {  
  2.  
  3.         if (Environment.MEDIA_MOUNTED.equals(Environment  
  4.                 .getExternalStorageState())) {  
  5.             Log.i("main", "本設備有存儲卡!");  
  6.             File file = new File(Environment.getExternalStorageDirectory(),  
  7.                     filename);  
  8.             FileOutputStream fos = null;  
  9.             fos = new FileOutputStream(file);  
  10.             fos.write(content.getBytes());  
  11.             fos.close();  
  12.         }  
  13.     } 

  而如果使用SdCard存儲文件的話,存放的路徑在Sdcard的根目錄下,如果使用模擬器運行程序的話,創建的文件在/mnt/sdcard/目錄下:

  補充:對於現在市面上很多Android設備,自帶了一個大的存儲空間,一般是8GB或16GB,並且又支持了Sdcard擴展,對於這樣的設備,使用Enviroment.getExternalStorageDirectory()方法只能獲取到設備自帶的存儲空間,對於另外擴展的Sdcard而言,需要修改路徑。

  源碼下載

 

總結

  以上就介紹了內部存儲、外部存儲、緩存存儲的方式方法。開發者可以根據需要選擇不同的方式進行數據持久化。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved