Android開發之數據保存技術

 

       本文主要講解Android開發的數據保存技術。Android的數據保存技術主要有preference,本地文件,SQLite輕量級數據庫,和Content Provider。本文只要講SQLite和Conent Provider。preference和本地文件,將放在後面討論。

SQLite

       Android通過SQLite庫提供了完善的關係數據庫功能,而沒有強加任何額外的限制。通過使用SQLite,可以爲每一個應用程序創建獨立的關係數據庫。

       所有的Android數據庫都存儲在/data/data/<package_name>/databases文件夾下。默認條件下,所有的數據庫都是私有的,並且只能被創建它們的應用程序訪問。要跨應用程序共享數據庫,可以使用內容提供器。

       SQLite是一個關係數據管理系統。它被普遍認爲是:開源,兼容標準,輕量級,Single-tier。

可以用一個例子來演示SQLite。該例子將記錄存儲在一個數據庫中,然後顯示出來。

       新建一個HelloSQLite的程序。

       需要在某個地方放置該數據庫中描述的一些常量,所以要創建一個Constants接口。

代碼如下:

/*

 *  Android開發之數據保存技術(一)

 *  Constants.java

 *  Created on: 2011-8-15

 *  Author: blueeagle

 *  Email: [email protected]

 */

 

  1. package com.blueeagle;  
  2.   
  3.    
  4.   
  5. import android.provider.BaseColumns;  
  6.   
  7. public interface Constants extends BaseColumns {  
  8.   
  9.     /** Called when the activity is first created. */  
  10.   
  11.     public static final String TABLE_NAME = "HelloSQLite";  
  12.   
  13.     public static final String TIME = "time";  
  14.   
  15.     public static final String TITLE = "title";  
  16.   
  17.     }  


       每個事件都將作爲HeloSQLite表中的一行進行存儲。每行都包含一個_id、time和title列。_id是主鍵,在擴展的BaseColums接口中聲明。time和title分別作爲時間和事件標記。

 

使用SQliteOpenHelper。接下來,創建一個名爲SQLiteData的幫助器來表示數據庫本身。這個類擴展自Android的SQLiteOpenHelper類,它負責管理數據庫的創建和版本。需要做的就是提供一個構造方法並且覆寫兩個方法。

       代碼如下:

  1. /* 
  2.  
  3.  *  Android開發之數據保存技術(一) 
  4.  
  5.  *  MySQLite.java 
  6.  
  7.  *  Created on: 2011-8-16 
  8.  
  9.  *  Author: blueeagle 
  10.  
  11.  *  Email: [email protected] 
  12.  
  13.  */  
  14.   
  15. package com.blueeagle;  
  16.   
  17.    
  18.   
  19. import android.content.Context;  
  20.   
  21. import android.database.sqlite.SQLiteDatabase;  
  22.   
  23. import android.database.sqlite.SQLiteDatabase.CursorFactory;  
  24.   
  25. import android.database.sqlite.SQLiteOpenHelper;  
  26.   
  27. import static com.blueeagle.Constants.TABLE_NAME;  
  28.   
  29. import static com.blueeagle.Constants.TIME;  
  30.   
  31. import static com.blueeagle.Constants.TITLE;  
  32.   
  33. import static android.provider.BaseColumns._ID;  
  34.   
  35.    
  36.   
  37. public class MySQLite extends SQLiteOpenHelper {  
  38.   
  39.       
  40.   
  41.     private static final String DATABASE_NAME = "MySQLite.db";  
  42.   
  43.     private static final int DATABASE_VERSION = 1;  
  44.   
  45.       
  46.   
  47.     public MySQLite(Context context, String name, CursorFactory factory,  
  48.   
  49.            int version) {  
  50.   
  51.        super(context, DATABASE_NAME, factory, DATABASE_VERSION);  
  52.   
  53.        // TODO Auto-generated constructor stub   
  54.   
  55.     }  
  56.   
  57.    
  58.   
  59.     @Override  
  60.   
  61.     public void onCreate(SQLiteDatabase db) {  
  62.   
  63.        // TODO Auto-generated method stub   
  64.   
  65.        db.execSQL("CREATE TABLE "+ TABLE_NAME+"("+_ID+"INTEGER PRIMARY KEY AUTOINCREMENT,"+TIME+"INTEGER,"+TITLE+"TEXT NOT NULL);");  
  66.   
  67.     }  
  68.   
  69.    
  70.   
  71.     @Override  
  72.   
  73.     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
  74.   
  75.        // TODO Auto-generated method stub   
  76.   
  77.        db.execSQL("DROP TABLE IF EXISTS" + TABLE_NAME);  
  78.   
  79.        onCreate(db);  
  80.   
  81.          
  82.   
  83.     }  
  84.   
  85. }  


首次訪問數據庫時,SQLiteOpenHelper將注意到該數據庫不存在,並調用onCreate()方法來創建它。

定義Activity主程序。

在HelloSQLite程序中做的第一次嘗試是使用本地的SQLite數據庫來存儲事件,並將這些事件顯示爲TextView中的一個字符串。

佈局xml文件如下:

  1. main.xml  
  2.   
  3. <?xml version="1.0" encoding="utf-8"?>  
  4.   
  5. <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"  
  6.   
  7.     android:layout_width="fill_parent"  
  8.   
  9.     android:layout_height="fill_parent"  
  10.   
  11.     >  
  12.   
  13. <TextView    
  14.   
  15.     android:id="@+id/myText"  
  16.   
  17.     android:layout_width="fill_parent"   
  18.   
  19.     android:layout_height="wrap_content"   
  20.   
  21.     />  
  22.   
  23. </ScrollView>  


 

這段代碼使用一個ScrollView,以防太多的時間沾滿了屏幕。

對於HelloSQLite.java的實現如下所示:相關代碼有註釋說明。

  1. package com.blueeagle;  
  2.   
  3. /* 
  4.  
  5.  *  Android開發之數據保存技術(一) 
  6.  
  7.  *  HelloSQLite.java 
  8.  
  9.  *  Created on: 2011-8-16 
  10.  
  11.  *  Author: blueeagle 
  12.  
  13.  *  Email: [email protected] 
  14.  
  15.  */  
  16.   
  17.    
  18.   
  19. import android.app.Activity;  
  20.   
  21. import android.content.ContentValues;  
  22.   
  23. import android.database.Cursor;  
  24.   
  25. import android.database.sqlite.SQLiteDatabase;  
  26.   
  27. import android.os.Bundle;  
  28.   
  29. import android.widget.TextView;  
  30.   
  31. import static com.blueeagle.Constants.TABLE_NAME;  
  32.   
  33. import static com.blueeagle.Constants.TIME;  
  34.   
  35. import static com.blueeagle.Constants.TITLE;  
  36.   
  37. import static android.provider.BaseColumns._ID;  
  38.   
  39.    
  40.   
  41. public class HelloSQLite extends Activity {  
  42.   
  43.     /** Called when the activity is first created. */  
  44.   
  45.     private MySQLite mySqlite;  
  46.   
  47.     private static String[] FROM={_ID,TIME,TITLE};  
  48.   
  49.     private static String ORDER_BY = TIME+" DESC";  
  50.   
  51.     @Override  
  52.   
  53.     public void onCreate(Bundle savedInstanceState) {  
  54.   
  55.         super.onCreate(savedInstanceState);  
  56.   
  57.         setContentView(R.layout.main);  
  58.   
  59.           
  60.   
  61.         mySqlite = new MySQLite(this);  
  62.   
  63.         try{  
  64.   
  65.         addItem("Hello,Android!");  
  66.   
  67.         Cursor cursor = getItems();  
  68.   
  69.         showItems(cursor);  
  70.   
  71.         }  
  72.   
  73.         finally{  
  74.   
  75.         mySqlite.close();  
  76.   
  77.         }  
  78.   
  79.     }  
  80.   
  81.     //顯示查詢結果   
  82.   
  83.     private void showItems(Cursor cursor) {  
  84.   
  85.        // TODO Auto-generated method stub   
  86.   
  87.        StringBuilder builder = new StringBuilder("Saved items:\n");  
  88.   
  89.        while(cursor.moveToNext()){  
  90.   
  91.            long id = cursor.getLong(0);  
  92.   
  93.            long time = cursor.getLong(1);  
  94.   
  95.            String title = cursor.getColumnName(2);  
  96.   
  97.            builder.append(id).append(": ");  
  98.   
  99.            builder.append(time).append(": ");  
  100.   
  101.            builder.append(title).append("\n");  
  102.   
  103.        }  
  104.   
  105.        TextView myTextView = (TextView)findViewById(R.id.myText);  
  106.   
  107.        myTextView.setText(builder);  
  108.   
  109.     }  
  110.   
  111.     //此函數接受一個Cursor作爲輸入並格式化,以便用戶能夠理解輸出的內容   
  112.   
  113.     //查詢條目方法   
  114.   
  115.     private Cursor getItems() {  
  116.   
  117.        // TODO Auto-generated method stub   
  118.   
  119.        SQLiteDatabase db = mySqlite.getReadableDatabase();  
  120.   
  121.        Cursor cursor = db.query(TABLE_NAME, FROM, nullnullnullnull, ORDER_BY);  
  122.   
  123.        startManagingCursor(cursor);  
  124.   
  125.        return cursor;  
  126.   
  127.     }  
  128.   
  129.     //因爲查詢不用修改數據庫,因此利用只讀句柄,然後調用查詢的query方法來執行SQL語句,FROM是   
  130.   
  131.     //想要使用的列構成的數組,ORDER_BY告訴SQLite按照從新到舊的順序返回查詢結果。   
  132.   
  133.     //添加條目方法   
  134.   
  135.     private void addItem(String string) {  
  136.   
  137.        // TODO Auto-generated method stub   
  138.   
  139.        SQLiteDatabase db = mySqlite.getWritableDatabase();  
  140.   
  141.        ContentValues values = new ContentValues();  
  142.   
  143.        values.put(TIME, System.currentTimeMillis());  
  144.   
  145.        values.put(TITLE, string);  
  146.   
  147.        db.insert(TABLE_NAME, null, values);  
  148.   
  149.     }  
  150.   
  151.     //因爲要修改數據庫,所以調用getWritableDatabase()方法來獲取數據庫的一個寫句柄,當然也可以獲取   
  152.   
  153.     //到讀句柄。   
  154.   
  155. }  


完成上述文件-》運行,即可看到結果顯示出來。這樣就完成了第一個Android數據庫程序。

當然,這裏面需要注意的問題:

1.       新建數據庫的問題

新建數據庫的時候,會遇到我們用:

db.execSQL("CREATE TABLE "+ TABLE_NAME+"("+_ID+"INTEGER PRIMARY KEY AUTOINCREMENT,"+TIME+"INTEGER,"+TITLE+"TEXT NOT NULL);");

這樣一條語句。有時候SQL語句書寫錯誤,比如少一個空格就會引起程序異常退出。這個時候建議在db.execSQL()函數中填寫一個字符串變量,然後把自己寫的SQL語句賦給字符串變量,在做賦值操作的時候,先打印出來看看,是不是少空格,多+號什麼的小錯誤。

2.       版本號的問題

關於構造函數中的版本號,是有明確的說明的。

    public MySQLite(Context context, String name, CursorFactory factory,

           int version) {

       super(context, DATABASE_NAME, factory, DATABASE_VERSION);

這個版本號version當你有新版本的時候,則做更新操作,新版本的版本號將要比老版本高,如果此時修改版本號,不是向高修改,而是向低修改的話,程序就會發生異常了。

 

3.       數據庫文件的問題

對於onCreate()方法,在程序運行的時候,只運行一次,運行的這一次就是去創建數據庫。將數據庫的文件存儲在SD卡中。路徑是data/data/包名/databases裏。可以在Eclipse裏通過DDMS裏的File Explorer來查看。當數據庫文件存在了以後,則不會再次運行。

 

完成了上述例子,就算完成了一個數據庫的應用。但是如果列表中有數千個或者上百萬個事件。程序將運行的非常慢。甚至於耗盡內存。解決辦法就是數據綁定。

有了數據綁定,只需要幾行代碼,就可以將數據連接到視圖,從而實現需求。爲了演示,我們修改上面的實例。

將主程序HelloSQLite.java繼承ListActivity而不是Activity。

將代碼修改爲如下所示:

 

  1. package com.blueeagle;  
  2.   
  3. /* 
  4.  
  5.  *  Android開發之數據保存技術 
  6.  
  7.  *  HelloSQLite.java 
  8.  
  9.  *  Created on: 2011-8-16 
  10.  
  11.  *  Author: blueeagle 
  12.  
  13.  *  Email: [email protected] 
  14.  
  15.  */  
  16.   
  17. import android.app.ListActivity;  
  18.   
  19. import android.content.ContentValues;  
  20.   
  21. import android.database.Cursor;  
  22.   
  23. import android.database.sqlite.SQLiteDatabase;  
  24.   
  25. import android.os.Bundle;  
  26.   
  27. import android.widget.SimpleCursorAdapter;  
  28.   
  29. import static com.blueeagle.Constants.TABLE_NAME;  
  30.   
  31. import static com.blueeagle.Constants.TIME;  
  32.   
  33. import static com.blueeagle.Constants.TITLE;  
  34.   
  35. import static android.provider.BaseColumns._ID;  
  36.   
  37.    
  38.   
  39. public class HelloSQLite extends ListActivity {  
  40.   
  41.     /** Called when the activity is first created. */  
  42.   
  43.     private MySQLite mySqlite;  
  44.   
  45.     private static String[] FROM={_ID,TIME,TITLE};  
  46.   
  47.     private static String ORDER_BY = TIME+" DESC";  
  48.   
  49.     //@Override   
  50.   
  51.     public void onCreate(Bundle savedInstanceState) {  
  52.   
  53.         super.onCreate(savedInstanceState);  
  54.   
  55.         setContentView(R.layout.main);  
  56.   
  57.           
  58.   
  59.         mySqlite = new MySQLite(this);  
  60.   
  61.         try{  
  62.   
  63.         addItem("Hello,Android!");  
  64.   
  65.         Cursor cursor = getItems();  
  66.   
  67.         showItems(cursor);  
  68.   
  69.         }  
  70.   
  71.         finally{  
  72.   
  73.         mySqlite.close();  
  74.   
  75.         }  
  76.   
  77.     }  
  78.   
  79.     private static int[] TO = {R.id.rowid,R.id.time,R.id.title};  
  80.   
  81.     private void showItems(Cursor cursor) {  
  82.   
  83.     SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,R.layout.item,cursor,FROM,TO);  
  84.   
  85.     //這裏有必要說明一下SimpleCursorAdapter構造函數的5個參數。1.對應於當前Activity的引用,2.一個資源,它定義一個列表條目的視圖,   
  86.   
  87.     //3.數據集光標,4.一組列名稱,數據來源於這些列。5.視圖列表,這是數據的目的地。   
  88.   
  89.     setListAdapter(adapter);  
  90.   
  91.       
  92.   
  93.     }  
  94.   
  95.     private Cursor getItems() {  
  96.   
  97.        SQLiteDatabase db = mySqlite.getReadableDatabase();  
  98.   
  99.        Cursor cursor = db.query(TABLE_NAME, FROM, nullnullnullnull, ORDER_BY);  
  100.   
  101.        startManagingCursor(cursor);  
  102.   
  103.        return cursor;  
  104.   
  105.     }  
  106.   
  107.     private void addItem(String string) {  
  108.   
  109.        // TODO Auto-generated method stub   
  110.   
  111.        SQLiteDatabase db = mySqlite.getWritableDatabase();  
  112.   
  113.        ContentValues values = new ContentValues();  
  114.   
  115.        values.put(TIME, System.currentTimeMillis());  
  116.   
  117.        values.put(TITLE, string);  
  118.   
  119.        db.insert(TABLE_NAME, null, values);  
  120.   
  121.     }  
  122.   
  123. }  


將main.xml修改成如下形式:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2.   
  3. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  4.   
  5.     android:layout_width="fill_parent"  
  6.   
  7.     android:layout_height="fill_parent"  
  8.   
  9.     >  
  10.   
  11. <ListView  
  12.   
  13. android:id="@android:id/list"  
  14.   
  15. android:layout_height="wrap_content"  
  16.   
  17. android:layout_width="wrap_content">  
  18.   
  19. </ListView>  
  20.   
  21. <TextView  
  22.   
  23. android:id="@android:id/empty"  
  24.   
  25. android:layout_height="wrap_content"  
  26.   
  27. android:layout_width="wrap_content"  
  28.   
  29. android:text="empty!">  
  30.   
  31. </TextView>  
  32.   
  33. </LinearLayout>  


 

增加item.xml爲如下:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2.   
  3. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  4.   
  5.     android:orientation="horizontal"  
  6.   
  7.     android:layout_width="fill_parent"  
  8.   
  9.     android:layout_height="fill_parent"  
  10.   
  11.     android:padding="10sp"  
  12.   
  13.     >  
  14.   
  15. <TextView  
  16.   
  17. android:id="@+id/rowid"  
  18.   
  19. android:layout_height="wrap_content"  
  20.   
  21. android:layout_width="wrap_content">  
  22.   
  23. </TextView>  
  24.   
  25. <TextView  
  26.   
  27. android:id="@+id/rowidcolon"  
  28.   
  29. android:layout_height="wrap_content"  
  30.   
  31. android:layout_width="wrap_content"  
  32.   
  33. android:text=":"  
  34.   
  35. android:layout_toRightOf="@id/rowid">  
  36.   
  37. </TextView>  
  38.   
  39. <TextView  
  40.   
  41. android:id="@+id/time"  
  42.   
  43. android:layout_height="wrap_content"  
  44.   
  45. android:layout_width="wrap_content"  
  46.   
  47. android:layout_toRightOf="@id/rowidcolon">  
  48.   
  49. </TextView>  
  50.   
  51. <TextView  
  52.   
  53. android:id="@+id/timecolon"  
  54.   
  55. android:layout_height="wrap_content"  
  56.   
  57. android:layout_width="wrap_content"  
  58.   
  59. android:text=":"  
  60.   
  61. android:layout_toRightOf="@id/time">  
  62.   
  63. </TextView>  
  64.   
  65. <TextView  
  66.   
  67. android:id="@+id/title"  
  68.   
  69. android:layout_height="fill_parent"  
  70.   
  71. android:layout_width="wrap_content"  
  72.   
  73. android:textStyle="italic"  
  74.   
  75. android:ellipsize="end"  
  76.   
  77. android:singleLine="true"  
  78.   
  79. android:layout_toRightOf="@id/timecolon">  
  80.   
  81. </TextView>  
  82.   
  83. </RelativeLayout>  


運行結果如圖所示。

 

當然,這只是一個DEMO,用來展示如何使用SQLite,這個DEMO存在一些問題。其他應用程序都不能向事件數據庫添加內容,甚至於無法看這些事件。那麼針對這一點,引出了Android裏的內容提供器,即ContentProvider。

內容提供器

       在Android安全模型中,一個應用程序編寫的文件無法被其他任何應用程序所讀寫。每個應用程序都有自己的Linux用戶ID和數據目錄,以及其受保護的內存空間。Android程序可以通過下面兩種方式進行彼此間的通信。

       第一種是IPC(Inter-Process Communication,進程間通信):一個進程使用AIDL(Android Interface Definition Language,接口定義語言)和IBinder接口聲明一個任意的API。調用該API時,將在進程間安全且有效地隊參數進行編組。這項先進技術用於對後臺Service線程進行遠程過程調用。

       第二種就是ContentProvider:進程在系統中將他們本身註冊爲某些數據類型的提供者。請求該信息時,Android就會通過一個固定的API調用這些進程,以它們認爲合適的方式查詢或者修改內容。

       ContentProvider管理的任何信息部分都通過一個URI來尋址,這個URI類似於以下形式:content://authority/path/id

其中content://是標準要求的前綴。authority是提供者的名稱,建議使用完全限定包名稱,避免出現名稱衝突。Path是提供者內部的一個虛擬目錄,用於標識被請求的數據類型。Id是被請求的特定記錄的主鍵,要請求獲得具有特定類型的所有記錄,可以省略此參數以及後面的斜槓。

       Android已經內置提供了幾個提供者,包括:

content://browser;

content://contacts;

content://media;

content://settings。

下面我們就來演示如何使用內容提供器。

將HelloSQLite程序改爲使用內容提供器的。對於HelloSQLite的提供者,下面都是有效的URI:

content://com.blueeagle.HelloSQLite/3 ——表示取id爲3的數據

content://com.blueeagle.HelloSQLite/   ——表示取所有數據

 

首先需要向Contants.java添加兩個常量:

    public static final String AUTHORITY = "com.blueeagle.HelloSQLite";

    public static final Uri CONTENT_URI = Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);

 

接下來可以對HelloSQLite類做一些修改:

代碼如下:

  1. package com.blueeagle;  
  2.   
  3. /* 
  4.  
  5.  *  Android開發之數據保存技術(一) 
  6.  
  7.  *  HelloSQLite.java 
  8.  
  9.  *  Created on: 2011-8-16 
  10.  
  11.  *  Author: blueeagle 
  12.  
  13.  *  Email: [email protected] 
  14.  
  15.  */  
  16.   
  17.    
  18.   
  19. import android.app.ListActivity;  
  20.   
  21. import android.content.ContentValues;  
  22.   
  23. import android.database.Cursor;  
  24.   
  25. import android.os.Bundle;  
  26.   
  27. import android.widget.SimpleCursorAdapter;  
  28.   
  29. import static com.blueeagle.Constants.TIME;  
  30.   
  31. import static com.blueeagle.Constants.TITLE;  
  32.   
  33. import static com.blueeagle.Constants.CONTENT_URI;  
  34.   
  35. import static android.provider.BaseColumns._ID;  
  36.   
  37.    
  38.   
  39. public class HelloSQLite extends ListActivity {  
  40.   
  41.     /** Called when the activity is first created. */  
  42.   
  43.     //private MySQLite mySqlite;   
  44.   
  45.     private static String[] FROM = {_ID,TIME,TITLE};  
  46.   
  47.     private static String ORDER_BY = TIME+" DESC";  
  48.   
  49.     //@Override   
  50.   
  51.     public void onCreate(Bundle savedInstanceState) {  
  52.   
  53.         super.onCreate(savedInstanceState);  
  54.   
  55.         setContentView(R.layout.main);  
  56.   
  57.           
  58.   
  59.         addItem("Hello,Android!");  
  60.   
  61.         Cursor cursor = getItems();  
  62.   
  63.         showItems(cursor);  
  64.   
  65.     }  
  66.   
  67.     private static int[] TO = {R.id.rowid,R.id.time,R.id.title};  
  68.   
  69.       
  70.   
  71. //顯示查詢結果   
  72.   
  73.     private void showItems(Cursor cursor) {  
  74.   
  75.        // TODO Auto-generated method stub   
  76.   
  77.     SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,R.layout.item,cursor,FROM,TO);  
  78.   
  79.     setListAdapter(adapter);  
  80.   
  81.     }  
  82.   
  83.     //使用ContentProvider時,geiItems方法也化簡了   
  84.   
  85.     private Cursor getItems() {  
  86.   
  87.        return managedQuery(CONTENT_URI,FROM,null,null,ORDER_BY);  
  88.   
  89.     }  
  90.   
  91.     //此處使用Activity.managedQuery()方法,將內容URI,感興趣的列表和應該使用的排序順序傳遞給該方法。   
  92.   
  93.     private void addItem(String string) {  
  94.   
  95.        ContentValues values = new ContentValues();  
  96.   
  97.        values.put(TIME, System.currentTimeMillis());  
  98.   
  99.        values.put(TITLE, string);  
  100.   
  101.        getContentResolver().insert(CONTENT_URI, values);  
  102.   
  103.     }  
  104.   
  105.     //沒有了對getWriteableDatabase()的調用,對insertOrThrow的調用替換成了getContentResolver().insert(CONTENT_URI, values)   
  106.   
  107.     //我們使用了一個URI而不是用數據庫句柄。   
  108.   
  109. }  


 

下面就是實現ContentProvider

       ContentProvider是一個類似於Activity的高級對象,需要向系統進行聲明。因此,實現ContentProvider的第一步是將其添加到AndroidManifest.xml文件中的<activity>標籤之前。

其中android:name是類名,android:authorities是在內容URI中使用的字符串。接下來創建我們自己的ContentProvider類,爲繼承類。ContentProvider創建後就會被調用:public boolean onCreate()ContentProvider類中有6個繼承而來的方法,需要實現:

具體來說需要實現ContentProvider 類中的6個抽象方法。
       Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):將查詢的數據以Cursor 對象的形式返回。
        Uri insert(Uri uri, ContentValues values):向 Content Provider中插入新數據記錄,ContentValues 爲數據記錄的列名和列值映射。
        int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):更新Content Provider中已存在的數據記錄。

int delete(Uri uri, String selection, String[] selectionArgs):從Content Provider中刪除數據記錄。

        String getType(Uri uri):返回Content Provider中的數據( MIME )類型。

boolean onCreate():當 Content Provider 啓動時被調用。

定義一個 URI 類型的靜態常量,命名爲CONTENT_URI。 必須爲該常量對象定義一個唯一的URI字符串,一般的做法是將 ContentProvider子類的全稱類名作爲URI字符串。

定義每個字段的列名,如果採用的數據庫存儲系統爲SQLite 數據庫,數據表列名可以採用數據庫中表的列名。不管數據表中有沒有其他的唯一標識一個記錄的字段,都應該定義一個"_id"字段 來唯一標識一個記錄。模式使用 "INTEGER PRIMARY KEY AUTOINCREMENT" 自動更新 一般將這些列名字符串定義爲靜態常量, 如"_id"字段名定義爲一個名爲"_ID"  值爲 "_id" 的靜態字符串對象。

創建好的一個Content Provider必須在AndroidManifest.xml中聲明。

        <provider android:name=".ItemsProvider"

              android:authorities="com.blueeagle" />

其中name屬性爲ContentProvider 子類的全稱類名,authorities 屬性唯一標識了一個ContentProvider。還可以通過 setReadPermission() 和 setWritePermission() 來設置其操作權限。當然也可以再上面的 xml中加入 android:readPermission 或者 android: writePermission屬性來控制其權限。
 注意:因爲ContentProvider可能被不同的進程和線程調用,所以這些方法必須是線程安全的。

然後需要使用UriMatcher,用於匹配Uri。
用法如下: 
首先把需要匹配Uri路徑全部給註冊上:

對於Uri:

什麼是URI?將其分爲A,B,C,D 4個部分:

A:標準前綴,用來說明一個Content Provider控制這些數據,無法改變的;"content://"

B:URI的標識,它定義了是哪個Content Provider提供這些數據。對於第三方應用程序,爲了保證URI標識的唯一性,它必須是一個完整的、小寫的 類名。這個標識在 元素的 authorities屬性中說明:一般是定義該ContentProvider的包.類的名稱 ;"content://hx.android.text.myprovider"

C:路徑,不知道是不是路徑,通俗的講就是你要操作的數據庫中表的名字,或者你也可以自己定義,記得在使用的時候保持一致就ok了;"content://hx.android.text.myprovider/tablename"

D:如果URI中包含表示需要獲取的記錄的ID;則就返回該id對應的數據,如果沒有ID,就表示返回全部; "content://hx.android.text.myprovider/tablename/#" #表示數據id

註冊完需要匹配的Uri後,就可以使用sMatcher.match(uri)方法對輸入的Uri進行匹配,如果匹配就返回匹配碼,匹配碼是調用addURI()方法傳入的第三個參數,例如匹配content://com.blueeagle路徑,返回的匹配碼爲1。

//常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼 
UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

//添加需要匹配的uri,如果匹配就會返回匹配碼 
//如果match()方法匹配content://com.blueeagle路徑,返回匹配碼爲1 
sUriMatcher.addURI(“content://com.blueeagle”, “HelloSQLite”, 1);

//如果match()方法匹配content://com.blueeagle/ ***路徑,返回匹配碼爲2
//#號爲通配符 
sUriMatcher.addURI(“content://com.blueeagle”, “HelloSQLite/#”, 2);

switch(sUriMatcher.match(Uri.parse("content://com.blueeagle /***"))) {
 case 1 break;
 case 2 break; 
default:
//不匹配 break; 
}

自定義的contentprovider如下:

ItemsProvider.java

  1. package com.blueeagle;  
  2.   
  3. /* 
  4.  
  5.  *  Android開發之數據保存技術(一) 
  6.  
  7. *  ItemsProvider.java 
  8.  
  9.  *  Created on: 2011-8-16 
  10.  
  11.  *  Author: blueeagle 
  12.  
  13.  *  Email: [email protected] 
  14.  
  15.  */  
  16.   
  17.    
  18.   
  19. import android.content.ContentProvider;  
  20.   
  21. import android.content.ContentUris;  
  22.   
  23. import android.content.ContentValues;  
  24.   
  25. import android.content.UriMatcher;  
  26.   
  27. import android.database.Cursor;  
  28.   
  29. import android.database.sqlite.SQLiteDatabase;  
  30.   
  31. import android.net.Uri;  
  32.   
  33. import android.text.TextUtils;  
  34.   
  35. import static com.blueeagle.Constants.CONTENT_URI;  
  36.   
  37. import static com.blueeagle.Constants.TABLE_NAME;  
  38.   
  39. import static com.blueeagle.Constants.AUTHORITY;  
  40.   
  41. import static android.provider.BaseColumns._ID;  
  42.   
  43.    
  44.   
  45. public class ItemsProvider extends ContentProvider {  
  46.   
  47.       
  48.   
  49.     private static final int ITEMS = 1;  
  50.   
  51.     private static final int ITEMS_ID = 2;  
  52.   
  53.        /** The MIME type of a directory of items */  
  54.   
  55.        private static final String CONTENT_TYPE  
  56.   
  57.           = "vnd.android.cursor.dir/vnd.com.blueeagle";  
  58.   
  59.        /** The MIME type of a single item */  
  60.   
  61.        private static final String CONTENT_ITEM_TYPE  
  62.   
  63.           = "vnd.android.cursor.item/vnd.com.blueeagle";  
  64.   
  65.          
  66.   
  67.        private MySQLite myDataBase ;  
  68.   
  69.        private UriMatcher myUriMatcher;  
  70.   
  71.          
  72.   
  73.        @Override  
  74.   
  75.        public boolean onCreate() {  
  76.   
  77.           myUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);  
  78.   
  79.           myUriMatcher.addURI(AUTHORITY, "HelloSQLite", ITEMS);  
  80.   
  81.           myUriMatcher.addURI(AUTHORITY, "HelloSQLite/#", ITEMS_ID);  
  82.   
  83.           myDataBase = new MySQLite(getContext());  
  84.   
  85.           return true;  
  86.   
  87.        }  
  88.   
  89.     @Override  
  90.   
  91.     public int delete(Uri uri, String selection,  
  92.   
  93.        // TODO Auto-generated method stub   
  94.   
  95.         String[] selectionArgs) {  
  96.   
  97.             SQLiteDatabase db = myDataBase.getWritableDatabase();  
  98.   
  99.             int count;  
  100.   
  101.             switch (myUriMatcher.match(uri)) {  
  102.   
  103.             case ITEMS:  
  104.   
  105.                count = db.delete(TABLE_NAME, selection, selectionArgs);  
  106.   
  107.                break;  
  108.   
  109.             case ITEMS_ID:  
  110.   
  111.                long id = Long.parseLong(uri.getPathSegments().get(1));  
  112.   
  113.                count = db.delete(TABLE_NAME, appendRowId(selection, id),  
  114.   
  115.                      selectionArgs);  
  116.   
  117.                break;  
  118.   
  119.             default:  
  120.   
  121.                throw new IllegalArgumentException("Unknown URI " + uri);  
  122.   
  123.             }  
  124.   
  125.             // Notify any watchers of the change   
  126.   
  127.             getContext().getContentResolver().notifyChange(uri, null);  
  128.   
  129.             return count;  
  130.   
  131.     }  
  132.   
  133.     @Override  
  134.   
  135.     public String getType(Uri uri) {  
  136.   
  137.        // TODO Auto-generated method stub   
  138.   
  139.           switch (myUriMatcher.match(uri)) {  
  140.   
  141.           case ITEMS:  
  142.   
  143.              return CONTENT_TYPE;  
  144.   
  145.           case ITEMS_ID:  
  146.   
  147.              return CONTENT_ITEM_TYPE;  
  148.   
  149.           default:  
  150.   
  151.              throw new IllegalArgumentException("Unknown URI " + uri);  
  152.   
  153.           }  
  154.   
  155.     }  
  156.   
  157.     @Override  
  158.   
  159.     public Uri insert(Uri uri, ContentValues values) {  
  160.   
  161.        // TODO Auto-generated method stub   
  162.   
  163.           SQLiteDatabase db = myDataBase.getWritableDatabase();  
  164.   
  165.           // Validate the requested uri   
  166.   
  167.           if (myUriMatcher.match(uri) != ITEMS) {  
  168.   
  169.              throw new IllegalArgumentException("Unknown URI " + uri);  
  170.   
  171.           }  
  172.   
  173.           // Insert into database   
  174.   
  175.           long id = db.insertOrThrow(TABLE_NAME, null, values);  
  176.   
  177.           // Notify any watchers of the change   
  178.   
  179.           Uri newUri = ContentUris.withAppendedId(CONTENT_URI, id);  
  180.   
  181.           getContext().getContentResolver().notifyChange(newUri, null);  
  182.   
  183.           return newUri;  
  184.   
  185.     }  
  186.   
  187.     @Override  
  188.   
  189.     public Cursor query(Uri uri, String[] projection,  
  190.   
  191.              String selection, String[] selectionArgs, String orderBy) {  
  192.   
  193.        // TODO Auto-generated method stub   
  194.   
  195.           if (myUriMatcher.match(uri) == ITEMS_ID) {  
  196.   
  197.               long id = Long.parseLong(uri.getPathSegments().get(1));  
  198.   
  199.               selection = appendRowId(selection, id);  
  200.   
  201.           }  
  202.   
  203.               // Get the database and run the query   
  204.   
  205.               SQLiteDatabase db = myDataBase.getReadableDatabase();  
  206.   
  207.               Cursor cursor = db.query(TABLE_NAME, projection, selection,  
  208.   
  209.                     selectionArgs, nullnull, orderBy);  
  210.   
  211.               // Tell the cursor what uri to watch, so it knows when its   
  212.   
  213.               // source data changes   
  214.   
  215.               cursor.setNotificationUri(getContext().getContentResolver(),  
  216.   
  217.                     uri);  
  218.   
  219.               return cursor;  
  220.   
  221.           }  
  222.   
  223.     private String appendRowId(String selection, long id) {  
  224.   
  225.        // TODO Auto-generated method stub   
  226.   
  227.           return _ID + "=" + id  
  228.   
  229.           + (!TextUtils.isEmpty(selection)  
  230.   
  231.                 ? " AND (" + selection + ')'  
  232.   
  233.                 : "");  
  234.   
  235.     }  
  236.   
  237.     @Override  
  238.   
  239.     public int update(Uri uri, ContentValues values, String selection,  
  240.   
  241.            String[] selectionArgs) {  
  242.   
  243.        // TODO Auto-generated method stub   
  244.   
  245.           SQLiteDatabase db = myDataBase.getWritableDatabase();  
  246.   
  247.           int count;  
  248.   
  249.           switch (myUriMatcher.match(uri)) {  
  250.   
  251.           case ITEMS:  
  252.   
  253.              count = db.update(TABLE_NAME, values, selection,  
  254.   
  255.                    selectionArgs);  
  256.   
  257.              break;  
  258.   
  259.           case ITEMS_ID:  
  260.   
  261.              long id = Long.parseLong(uri.getPathSegments().get(1));  
  262.   
  263.              count = db.update(TABLE_NAME, values, appendRowId(  
  264.   
  265.                    selection, id), selectionArgs);  
  266.   
  267.              break;  
  268.   
  269.           default:  
  270.   
  271.              throw new IllegalArgumentException("Unknown URI " + uri);  
  272.   
  273.           }  
  274.   
  275.           // Notify any watchers of the change   
  276.   
  277.           getContext().getContentResolver().notifyChange(uri, null);  
  278.   
  279.           return count;  
  280.   
  281.     }  
  282.   
  283. }  


總結一下,創建一個新的內容提供器。

1.通過擴展抽象類ContentProvider可以創建新的內容提供器。重寫onCreate方法來打開或者初始化將要使用這個新的提供器來提供底層數據源。

2.還應該提供那些用來返回指向這個提供器的完整的URI的公共靜態CONTENT_URI變量。提供器之間的內容URI應該是唯一的,所以最好的做法是使URI路徑以包名爲基礎。

定義一個內容提供器URI一般的形式爲:

content://com.pakagename/datapath

例如:content://com.blueeagle/items

或者:content://com.blueeagle./items/3

內容URI可以表示爲這兩種形式中的任意一種形式。前面的一種URI表示對那種類型中所有的值的請求,後面附加一個/3的表示對一條記錄的請求(這裏請求的是記錄3)。這兩種形式來訪問提供器都是可行的。

完成這項工作最簡單的方式是使用一個UriMatcher。當通過內容解析器來訪問內容提供器的時候,可以配置UriMathcer來解析URI以確定它們的形式。就像前文說的那樣。

出自:http://blog.csdn.net/redoffice/article/details/6695345

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章