Android數據存儲機制

數據是應用的核心。下面我們來詳細看看Android系統的數據存儲和共享機制。

Android為我們提供瞭4種數據存儲方式,但由於存儲的這些數據都是某個應用程序私有的,所以它又為我們提供瞭一種在不同應用程序之間共享數據的機制,即ContentProvider。

下面我們首先將分別介紹Android的4種數據存儲方式:Shared Preferences、Files、SQLite、NetWork,最後我們將詳細介紹Android的Content Provider機制。

一、Android數據存儲之SharedProviences
它是Android提供的用來存儲一些簡單配置信息的一種機制,例如:登錄用戶的用戶名與密碼。其采用瞭Map數據結構來存儲數據,以鍵值對的方式存儲,可以簡單的讀取與寫入,具體實例如下:
[java] //讀取SharedPreference  
void  readSharedPreferences() 

    String  strName,strPassword; 
    SharedPreferences   user = getSharedPreferences(“user_info”,0); 
    strName = user.getString(“NAME”,””); 
    strPassword = user getString(“PASSWORD”,””); 

//往SharedPreference中寫入內容  
void  writeSharedPreferences(String  strName,String strPassword) 

    SharedPreferences   user = getSharedPreferences(“user_info”,0); 
    //使其處於編輯狀態  
    uer.edit(); 
    user.putString(“NAME”, strName); 
    user.putString(“PASSWORD” ,strPassword); 
    user.commit(); 

//讀取SharedPreference
void  readSharedPreferences()
{
    String  strName,strPassword;
    SharedPreferences   user = getSharedPreferences(“user_info”,0);
    strName = user.getString(“NAME”,””);
    strPassword = user getString(“PASSWORD”,””);
}
//往SharedPreference中寫入內容
void  writeSharedPreferences(String  strName,String strPassword)
{
    SharedPreferences   user = getSharedPreferences(“user_info”,0);
    //使其處於編輯狀態
    uer.edit();
    user.putString(“NAME”, strName);
    user.putString(“PASSWORD” ,strPassword);
    user.commit();
}數據讀取與寫入的方法都非常簡單,隻是在寫入的時候有些區別:先調用edit()使其處於編輯狀態,然後才能修改數據,最後使用commit()提交修改的數據。實際上SharedPreferences是采用瞭XML格式將數據存儲到設備中,在DDMS中的File Explorer中的/data/data/<package name>/shares_prefs下。以上面的數據存儲結果為例,打開後可以看到一個user_info.xml的文件,打開後可以看到:
[java] <?xml version=”1.0″ encoding=”UTF-8″?> 
<map> 
    <string name=”NAME”>moandroid</string> 
    <string name=” PASSWORD”>SharedPreferences</string> 
</map> 
<?xml version=”1.0″ encoding=”UTF-8″?>
<map>
    <string name=”NAME”>moandroid</string>
    <string name=” PASSWORD”>SharedPreferences</string>
</map>使用SharedPreferences是有些限制的:隻能在同一個包內使用,不能在不同的包之間使用

二、Android數據存儲之Files(文件)
文件方式是一種較常用的存儲方法,在Android中讀/寫文件的方法,與Java中實現I/O的程序是完全一樣的,Android為我們提供瞭openFileInput()和openFileOutput()方法來讀取設備上的文件。具體示例如下:
[java] //讀文件  
private void read() {   
        try {   
            FileInputStream inputStream = this.openFileInput("music.cfg");   
            byte[] bytes = new byte[1024];   
            ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();   
            while (inputStream.read(bytes) != -1) {   
                arrayOutputStream.write(bytes, 0, bytes.length);   
            }   
            inputStream.close();   
            arrayOutputStream.close();   
            mbMusic = Boolean.getBoolean(new String(arrayOutputStream.toByteArray()));   
        } catch (FileNotFoundException e) {   
            e.printStackTrace();   
        } catch (IOException e) {   
            e.printStackTrace();   
        }   
    }   
//寫文件  
private void writeFile() {   
    try {   
        /** 根據用戶提供的文件名,以及文件的應用模式,打開一個輸出流
         * openFileOutput(String name, int mode);
         * 第一個參數,代表文件名稱,註意這裡的文件名稱不能包括任何的/或者/這種分隔符,隻能是文件名
         *          該文件會被保存在/data/data/應用名稱/files/music.cfg
         * 第二個參數,代表文件的操作模式
         *          MODE_PRIVATE 私有(隻能創建它的應用訪問) 重復寫入時會文件覆蓋
         *          MODE_APPEND  私有   重復寫入時會在文件的末尾進行追加,而不是覆蓋掉原來的文件
         *          MODE_WORLD_READABLE 公用  可讀
         *          MODE_WORLD_WRITEABLE 公用 可讀寫
         *  
         */   
        FileOutputStream outputStream = openFileOutput("music.cfg",   
            Activity.MODE_PRIVATE);   
        outputStream.write(String.valueOf(mbMusic).getBytes());   
        outputStream.flush();   
        outputStream.close();   
        Toast.makeText(FilesTest.this, "保存成功", Toast.LENGTH_LONG).show();   
    } catch (FileNotFoundException e) {   
        e.printStackTrace();   
    } catch (IOException e) {   
        e.printStackTrace();   
    }   
}   
//讀文件
private void read() { 
        try { 
            FileInputStream inputStream = this.openFileInput("music.cfg"); 
            byte[] bytes = new byte[1024]; 
            ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(); 
            while (inputStream.read(bytes) != -1) { 
                arrayOutputStream.write(bytes, 0, bytes.length); 
            } 
            inputStream.close(); 
            arrayOutputStream.close(); 
            mbMusic = Boolean.getBoolean(new String(arrayOutputStream.toByteArray())); 
        } catch (FileNotFoundException e) { 
            e.printStackTrace(); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
    } 
//寫文件
private void writeFile() { 
    try { 
        /** 根據用戶提供的文件名,以及文件的應用模式,打開一個輸出流
         * openFileOutput(String name, int mode);
         * 第一個參數,代表文件名稱,註意這裡的文件名稱不能包括任何的/或者/這種分隔符,隻能是文件名
         *          該文件會被保存在/data/data/應用名稱/files/music.cfg
         * 第二個參數,代表文件的操作模式
         *          MODE_PRIVATE 私有(隻能創建它的應用訪問) 重復寫入時會文件覆蓋
         *          MODE_APPEND  私有   重復寫入時會在文件的末尾進行追加,而不是覆蓋掉原來的文件
         *          MODE_WORLD_READABLE 公用  可讀
         *          MODE_WORLD_WRITEABLE 公用 可讀寫
         * 
         */ 
        FileOutputStream outputStream = openFileOutput("music.cfg", 
            Activity.MODE_PRIVATE); 
        outputStream.write(String.valueOf(mbMusic).getBytes()); 
        outputStream.flush(); 
        outputStream.close(); 
        Toast.makeText(FilesTest.this, "保存成功", Toast.LENGTH_LONG).show(); 
    } catch (FileNotFoundException e) { 
        e.printStackTrace(); 
    } catch (IOException e) { 
        e.printStackTrace(); 
    } 

如果在開發一個應用時,需要通過加載一個文件的內容來初始化程序,就可以在編譯程序之前,在/res/raw/tempFile中建立一個文件,然後直接讀取內容(具體方法參見本人另一片文章:Android各種資源詳解 )。
對於sd卡上的文件讀寫,可以File file = new File("/sdcard/");然後遍歷sdcard目錄,對其中文件進行相關操作,具體方法不再贅述。
三、 Android數據存儲之NetWork
通過網絡方式來獲取和保存數據資源,需要設備保持聯網狀態。將數據存儲到網絡的方法很多,如:將要保存的數據以文件方式上傳到文件服務器、發送電子郵件等等。
也可以將網絡上的資源獲取到本地。下面的例子是一個將網絡上的文件獲取到本地並顯示。
[java] try{ 
    URL uri = new URL("http://192.168.2.22:8080/test.txt"); 
    URLConnection con = uri.openConnection(); 
    InputStream is = con.getInputStream(); 
    BufferedInputStream bis = new BufferedInputStream(is); 
    ByteArrayBuffer baf = new ByteArrayBuffer(100); 
    int current = 0; 
    while ((current = bis.read()) != -1){ 
        baf.append((byte)current); 
    } 
    text = new String(baf.toByteArray()); 
}catch (Exception e){ 
    text = e.getMessage(); 

textView.setText(text); 
try{
    URL uri = new URL("http://192.168.2.22:8080/test.txt");
    URLConnection con = uri.openConnection();
    InputStream is = con.getInputStream();
    BufferedInputStream bis = new BufferedInputStream(is);
    ByteArrayBuffer baf = new ByteArrayBuffer(100);
    int current = 0;
    while ((current = bis.read()) != -1){
        baf.append((byte)current);
    }
    text = new String(baf.toByteArray());
}catch (Exception e){
    text = e.getMessage();
}
textView.setText(text);再來看看如何訪問web網頁,代碼如下:
[java] public class WebViewTest extends Activity { 
    private WebView webView; 
    /** Called when the activity is first created. */   
       
    @Override   
    public void onCreate(Bundle savedInstanceState) {   
        super.onCreate(savedInstanceState);   
        setContentView(R.layout.main);   
  
        WebView wView = (WebView)findViewById(R.id.webview);      
        WebSettings wSet = wView.getSettings();      
        wSet.setJavaScriptEnabled(true); 
        //解決亂碼問題  
        wSet.setDefaultTextEncodingName("utf-8");   
         
        //打開本包內asset目錄下的index.html文件  
        wView.loadUrl("file:///android_asset/index.html");    
         
        //打開本地sd卡內的index.html文件  
        //wView.loadUrl("content://com.android.htmlfileprovider/sdcard/index.html");  
         
        //打開指定url的html文件  
        //wView.loadUrl("http://wap.baidu.com");    
    }   

public class WebViewTest extends Activity {
    private WebView webView;
    /** Called when the activity is first created. */ 
     
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
 
        WebView wView = (WebView)findViewById(R.id.webview);    
        WebSettings wSet = wView.getSettings();    
        wSet.setJavaScriptEnabled(true);
        //解決亂碼問題
        wSet.setDefaultTextEncodingName("utf-8"); 
       
        //打開本包內asset目錄下的index.html文件
        wView.loadUrl("file:///android_asset/index.html");  
       
        //打開本地sd卡內的index.html文件
        //wView.loadUrl("content://com.android.htmlfileprovider/sdcard/index.html");
       
        //打開指定url的html文件
        //wView.loadUrl("http://wap.baidu.com"); 
    } 
}佈局文件如下:
[java] <?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    > 
<TextView   
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello" 
    /> 
<WebView 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:layout_weight="1" 
    android:id="@+id/webview" 
/> 
</LinearLayout> 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView 
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
    />
<WebView
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    android:id="@+id/webview"
/>
</LinearLayout>在/assets/目錄下建一文件名為:index.html
[html] <html> 
<head> 
<title><h3>my first web page</h3></title> 
</head> 
<body> 
<form method="post"> 
<table> 
<tr> 
<td>用戶名:</td> 
<td><input type="text"></td> 
</tr> 
<tr> 
<td>密碼:</td> 
<td><input type="password"></td> 
</tr> 
<tr> 
<td><input type="button" value="重置" onclick="alert('確認重置嗎?');"></td>      
<td><input type="button" value="登錄" onclick="alert('確認登錄嗎?');"></td> 
</tr> 
</form> 
</body> 
</html>  

運行項目結果如圖所示:

在Android訪問網絡時要註意:
(1)、android訪問網絡資源必須要有:<uses-permission android:name="android.permission.INTERNET" />權限,不然運行會提示:Permission denied。
(2)、若使用Android模擬器訪問本地機器,不能用localhost或者127.0.0.1,而要用10.0.2.2,不然會連接不上,這是因為android模擬器(simulator)把它自己作為瞭localhost,也就是說,代碼中使用localhost或者127.0.0.1來訪問,都是訪問模擬器自己;如果你想在模擬器上面訪問你的電腦,那麼就使用android內置的IP10.0.2.2吧。
四、Android數據存儲之SQLite
SQLite,是一款輕型的數據庫,是遵守ACID的關聯式數據庫管理系統,它的設計目標是嵌入式的,而且目前已經在很多嵌入式產品中使用瞭它,它占用資源非常的低,在嵌入式設備中,可能隻需要幾百K的內存就夠瞭。它能夠支持Windows/Linux/Unix等等主流的操作系統,同時能夠跟很多程序語言相結合,比如Tcl、C#、PHP、Java等,還有ODBC接口,同樣比起Mysql、PostgreSQL這兩款開源世界著名的數據庫管理系統來講,它的處理速度比他們都快。
下面試一個例子,通過它我們來看看有關SQLite數據庫的CRUD等操作。
佈局文件main.xml:
[java] <?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    > 
<ListView 
    android:id="@+id/listView" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
/> 
</LinearLayout> 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<ListView
    android:id="@+id/listView"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
/>
</LinearLayout>佈局文件:listitem.xml
[java] <?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/linearlayout" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    > 
    <TextView 
        android:id="@+id/ItemTitle" 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
    /> 
    <TextView 
        android:id="@+id/ItemText" 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
    /> 
</LinearLayout> 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linearlayout"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <TextView
        android:id="@+id/ItemTitle"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
    />
    <TextView
        android:id="@+id/ItemText"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
    />
</LinearLayout>Java類:
[java] public class SqlLiteTest extends Activity { 
    private ListView listView; 
    //記錄條數  
    private static int count = 0; 
    //數據庫對象  
    private SQLiteDatabase sqliteDatabase; 
    private final static String DATABASE_NAME = "sql.db"; 
    private final static String TABLE_NAME = "t_student"; 
     
    private final static String ID = "_id"; 
    private final static String NUM = "num"; 
    private final static String DATA = "data"; 
    //建表語句  
    private final static String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME 
                                                + "(" + ID + " INTEGER PRIMARY KEY," 
                                                + NUM + " INTEGER," + DATA + " TEXT)"; 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
        listView = (ListView)findViewById(R.id.listView); 
        //打開數據庫  
        sqliteDatabase = this.openOrCreateDatabase(DATABASE_NAME, MODE_PRIVATE, null); 
         
        deleteTable(); 
        //創建表  
        sqliteDatabase.execSQL(CREATE_TABLE); 
        //更新ListView  
        updateAdapter(); 
    } 
    //按鍵監聽  
    public boolean onKeyUp(int keyCode,KeyEvent evnet){ 
        switch (keyCode){ 
            case KeyEvent.KEYCODE_DPAD_UP: 
                addData(); 
                break; 
            case KeyEvent.KEYCODE_DPAD_DOWN: 
                delete(); 
                break; 
            case KeyEvent.KEYCODE_DPAD_LEFT: 
                updateData(); 
                break; 
            case KeyEvent.KEYCODE_DPAD_RIGHT: 
                deleteTable(); 
                break; 
            case KeyEvent.KEYCODE_DPAD_CENTER: 
                deleteDataBase(); 
                break; 
        } 
        return true; 
    } 
    //更新listView視圖  
    private void updateAdapter(){ 
        Cursor cursor = sqliteDatabase.query(TABLE_NAME, new String[]{ID,NUM,DATA}, null, null, null, null, null); 
         
        count = cursor.getCount(); 
        if (null != cursor && count >= 0){ 
            //ListAdapter 是ListView 和數據的橋梁  
            ListAdapter adapter = new SimpleCursorAdapter(this, R.layout.listitem, cursor, new String[]{NUM,DATA}, new int[]{R.id.ItemTitle,R.id.ItemText}); 
             
            //將adapter添加到listView中  
            listView.setAdapter(adapter); 
        } 
    } 
    //插入數據  
    private void addData(){ 
        //要更新的數據  
        ContentValues contentValue = new ContentValues(); 
        contentValue.put(NUM, count); 
        contentValue.put(DATA,"測試數據庫數據:" + count); 
        //插入數據  
        sqliteDatabase.insert(TABLE_NAME, null, contentValue); 
        count++; 
        updateAdapter(); 
    } 
    //刪除數據庫  
    private void deleteDataBase(){ 
        this.deleteDatabase(DATABASE_NAME); 
        this.finish(); 
    } 
    //刪除表  
    private void deleteTable(){ 
        sqliteDatabase.execSQL("drop table if exists " + "sql." + TABLE_NAME); 
        //this.finish();  
    } 
    //更新一條數據  
    private  void updateData(){ 
        //要更新的數據  
        ContentValues contentValue = new ContentValues(); 
        contentValue.put(NUM, count); 
        contentValue.put(DATA,"修改後的數據:" + count); 
        //執行更改  
        sqliteDatabase.update(TABLE_NAME, contentValue, NUM + "=" + Integer.toString(count – 1), null); 
        //更新ListView數據  
        updateAdapter(); 
    } 
    //刪除指定的數據  
    private void delete(){ 
        sqliteDatabase.execSQL("delete from " + TABLE_NAME + " where _id = " + Integer.toString(count)); 
        count–; 
        if (count < 0){ 
            count = 0; 
        } 
        updateAdapter(); 
    } 

public class SqlLiteTest extends Activity {
    private ListView listView;
    //記錄條數
    private static int count = 0;
    //數據庫對象
    private SQLiteDatabase sqliteDatabase;
    private final static String DATABASE_NAME = "sql.db";
    private final static String TABLE_NAME = "t_student";
   
    private final static String ID = "_id";
    private final static String NUM = "num";
    private final static String DATA = "data";
    //建表語句
    private final static String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME
                                                + "(" + ID + " INTEGER PRIMARY KEY,"
                                                + NUM + " INTEGER," + DATA + " TEXT)";
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        listView = (ListView)findViewById(R.id.listView);
        //打開數據庫
        sqliteDatabase = this.openOrCreateDatabase(DATABASE_NAME, MODE_PRIVATE, null);
       
        deleteTable();
        //創建表
        sqliteDatabase.execSQL(CREATE_TABLE);
        //更新ListView
        updateAdapter();
    }
    //按鍵監聽
    public boolean onKeyUp(int keyCode,KeyEvent evnet){
        switch (keyCode){
            case KeyEvent.KEYCODE_DPAD_UP:
                addData();
                break;
            case KeyEvent.KEYCODE_DPAD_DOWN:
                delete();
                break;
            case KeyEvent.KEYCODE_DPAD_LEFT:
                updateData();
                break;
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                deleteTable();
                break;
            case KeyEvent.KEYCODE_DPAD_CENTER:
                deleteDataBase();
                break;
        }
        return true;
    }
    //更新listView視圖
    private void updateAdapter(){
        Cursor cursor = sqliteDatabase.query(TABLE_NAME, new String[]{ID,NUM,DATA}, null, null, null, null, null);
       
        count = cursor.getCount();
        if (null != cursor && count >= 0){
            //ListAdapter 是ListView 和數據的橋梁
            ListAdapter adapter = new SimpleCursorAdapter(this, R.layout.listitem, cursor, new String[]{NUM,DATA}, new int[]{R.id.ItemTitle,R.id.ItemText});
           
            //將adapter添加到listView中
            listView.setAdapter(adapter);
        }
    }
    //插入數據
    private void addData(){
        //要更新的數據
        ContentValues contentValue = new ContentValues();
        contentValue.put(NUM, count);
        contentValue.put(DATA,"測試數據庫數據:" + count);
        //插入數據
        sqliteDatabase.insert(TABLE_NAME, null, contentValue);
        count++;
        updateAdapter();
    }
    //刪除數據庫
    private void deleteDataBase(){
        this.deleteDatabase(DATABASE_NAME);
        this.finish();
    }
    //刪除表
    private void deleteTable(){
        sqliteDatabase.execSQL("drop table if exists " + "sql." + TABLE_NAME);
        //this.finish();
    }
    //更新一條數據
    private  void updateData(){
        //要更新的數據
        ContentValues contentValue = new ContentValues();
        contentValue.put(NUM, count);
        contentValue.put(DATA,"修改後的數據:" + count);
        //執行更改
        sqliteDatabase.update(TABLE_NAME, contentValue, NUM + "=" + Integer.toString(count – 1), null);
        //更新ListView數據
        updateAdapter();
    }
    //刪除指定的數據
    private void delete(){
        sqliteDatabase.execSQL("delete from " + TABLE_NAME + " where _id = " + Integer.toString(count));
        count–;
        if (count < 0){
            count = 0;
        }
        updateAdapter();
    }
}運行,按上下左右鍵分別進行,增加,刪除,修改以及刪除數據庫操作。結果如下圖所示:

摘自 fwwdn的專欄

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *