本文主要講解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]
*/
- package com.blueeagle;
- import android.provider.BaseColumns;
- public interface Constants extends BaseColumns {
- /** Called when the activity is first created. */
- public static final String TABLE_NAME = "HelloSQLite";
- public static final String TIME = "time";
- public static final String TITLE = "title";
- }
每個事件都將作爲HeloSQLite表中的一行進行存儲。每行都包含一個_id、time和title列。_id是主鍵,在擴展的BaseColums接口中聲明。time和title分別作爲時間和事件標記。
使用SQliteOpenHelper。接下來,創建一個名爲SQLiteData的幫助器來表示數據庫本身。這個類擴展自Android的SQLiteOpenHelper類,它負責管理數據庫的創建和版本。需要做的就是提供一個構造方法並且覆寫兩個方法。
代碼如下:
- /*
- * Android開發之數據保存技術(一)
- * MySQLite.java
- * Created on: 2011-8-16
- * Author: blueeagle
- * Email: [email protected]
- */
- package com.blueeagle;
- import android.content.Context;
- import android.database.sqlite.SQLiteDatabase;
- import android.database.sqlite.SQLiteDatabase.CursorFactory;
- import android.database.sqlite.SQLiteOpenHelper;
- import static com.blueeagle.Constants.TABLE_NAME;
- import static com.blueeagle.Constants.TIME;
- import static com.blueeagle.Constants.TITLE;
- import static android.provider.BaseColumns._ID;
- public class MySQLite extends SQLiteOpenHelper {
- private static final String DATABASE_NAME = "MySQLite.db";
- private static final int DATABASE_VERSION = 1;
- public MySQLite(Context context, String name, CursorFactory factory,
- int version) {
- super(context, DATABASE_NAME, factory, DATABASE_VERSION);
- // TODO Auto-generated constructor stub
- }
- @Override
- public void onCreate(SQLiteDatabase db) {
- // TODO Auto-generated method stub
- db.execSQL("CREATE TABLE "+ TABLE_NAME+"("+_ID+"INTEGER PRIMARY KEY AUTOINCREMENT,"+TIME+"INTEGER,"+TITLE+"TEXT NOT NULL);");
- }
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- // TODO Auto-generated method stub
- db.execSQL("DROP TABLE IF EXISTS" + TABLE_NAME);
- onCreate(db);
- }
- }
首次訪問數據庫時,SQLiteOpenHelper將注意到該數據庫不存在,並調用onCreate()方法來創建它。
定義Activity主程序。
在HelloSQLite程序中做的第一次嘗試是使用本地的SQLite數據庫來存儲事件,並將這些事件顯示爲TextView中的一個字符串。
佈局xml文件如下:
- main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TextView
- android:id="@+id/myText"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- />
- </ScrollView>
這段代碼使用一個ScrollView,以防太多的時間沾滿了屏幕。
對於HelloSQLite.java的實現如下所示:相關代碼有註釋說明。
- package com.blueeagle;
- /*
- * Android開發之數據保存技術(一)
- * HelloSQLite.java
- * Created on: 2011-8-16
- * Author: blueeagle
- * Email: [email protected]
- */
- import android.app.Activity;
- import android.content.ContentValues;
- import android.database.Cursor;
- import android.database.sqlite.SQLiteDatabase;
- import android.os.Bundle;
- import android.widget.TextView;
- import static com.blueeagle.Constants.TABLE_NAME;
- import static com.blueeagle.Constants.TIME;
- import static com.blueeagle.Constants.TITLE;
- import static android.provider.BaseColumns._ID;
- public class HelloSQLite extends Activity {
- /** Called when the activity is first created. */
- private MySQLite mySqlite;
- private static String[] FROM={_ID,TIME,TITLE};
- private static String ORDER_BY = TIME+" DESC";
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mySqlite = new MySQLite(this);
- try{
- addItem("Hello,Android!");
- Cursor cursor = getItems();
- showItems(cursor);
- }
- finally{
- mySqlite.close();
- }
- }
- //顯示查詢結果
- private void showItems(Cursor cursor) {
- // TODO Auto-generated method stub
- StringBuilder builder = new StringBuilder("Saved items:\n");
- while(cursor.moveToNext()){
- long id = cursor.getLong(0);
- long time = cursor.getLong(1);
- String title = cursor.getColumnName(2);
- builder.append(id).append(": ");
- builder.append(time).append(": ");
- builder.append(title).append("\n");
- }
- TextView myTextView = (TextView)findViewById(R.id.myText);
- myTextView.setText(builder);
- }
- //此函數接受一個Cursor作爲輸入並格式化,以便用戶能夠理解輸出的內容
- //查詢條目方法
- private Cursor getItems() {
- // TODO Auto-generated method stub
- SQLiteDatabase db = mySqlite.getReadableDatabase();
- Cursor cursor = db.query(TABLE_NAME, FROM, null, null, null, null, ORDER_BY);
- startManagingCursor(cursor);
- return cursor;
- }
- //因爲查詢不用修改數據庫,因此利用只讀句柄,然後調用查詢的query方法來執行SQL語句,FROM是
- //想要使用的列構成的數組,ORDER_BY告訴SQLite按照從新到舊的順序返回查詢結果。
- //添加條目方法
- private void addItem(String string) {
- // TODO Auto-generated method stub
- SQLiteDatabase db = mySqlite.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(TIME, System.currentTimeMillis());
- values.put(TITLE, string);
- db.insert(TABLE_NAME, null, values);
- }
- //因爲要修改數據庫,所以調用getWritableDatabase()方法來獲取數據庫的一個寫句柄,當然也可以獲取
- //到讀句柄。
- }
完成上述文件-》運行,即可看到結果顯示出來。這樣就完成了第一個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。
將代碼修改爲如下所示:
- package com.blueeagle;
- /*
- * Android開發之數據保存技術
- * HelloSQLite.java
- * Created on: 2011-8-16
- * Author: blueeagle
- * Email: [email protected]
- */
- import android.app.ListActivity;
- import android.content.ContentValues;
- import android.database.Cursor;
- import android.database.sqlite.SQLiteDatabase;
- import android.os.Bundle;
- import android.widget.SimpleCursorAdapter;
- import static com.blueeagle.Constants.TABLE_NAME;
- import static com.blueeagle.Constants.TIME;
- import static com.blueeagle.Constants.TITLE;
- import static android.provider.BaseColumns._ID;
- public class HelloSQLite extends ListActivity {
- /** Called when the activity is first created. */
- private MySQLite mySqlite;
- private static String[] FROM={_ID,TIME,TITLE};
- private static String ORDER_BY = TIME+" DESC";
- //@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mySqlite = new MySQLite(this);
- try{
- addItem("Hello,Android!");
- Cursor cursor = getItems();
- showItems(cursor);
- }
- finally{
- mySqlite.close();
- }
- }
- private static int[] TO = {R.id.rowid,R.id.time,R.id.title};
- private void showItems(Cursor cursor) {
- SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,R.layout.item,cursor,FROM,TO);
- //這裏有必要說明一下SimpleCursorAdapter構造函數的5個參數。1.對應於當前Activity的引用,2.一個資源,它定義一個列表條目的視圖,
- //3.數據集光標,4.一組列名稱,數據來源於這些列。5.視圖列表,這是數據的目的地。
- setListAdapter(adapter);
- }
- private Cursor getItems() {
- SQLiteDatabase db = mySqlite.getReadableDatabase();
- Cursor cursor = db.query(TABLE_NAME, FROM, null, null, null, null, ORDER_BY);
- startManagingCursor(cursor);
- return cursor;
- }
- private void addItem(String string) {
- // TODO Auto-generated method stub
- SQLiteDatabase db = mySqlite.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(TIME, System.currentTimeMillis());
- values.put(TITLE, string);
- db.insert(TABLE_NAME, null, values);
- }
- }
將main.xml修改成如下形式:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <ListView
- android:id="@android:id/list"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content">
- </ListView>
- <TextView
- android:id="@android:id/empty"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="empty!">
- </TextView>
- </LinearLayout>
增加item.xml爲如下:
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:padding="10sp"
- >
- <TextView
- android:id="@+id/rowid"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content">
- </TextView>
- <TextView
- android:id="@+id/rowidcolon"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text=":"
- android:layout_toRightOf="@id/rowid">
- </TextView>
- <TextView
- android:id="@+id/time"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_toRightOf="@id/rowidcolon">
- </TextView>
- <TextView
- android:id="@+id/timecolon"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text=":"
- android:layout_toRightOf="@id/time">
- </TextView>
- <TextView
- android:id="@+id/title"
- android:layout_height="fill_parent"
- android:layout_width="wrap_content"
- android:textStyle="italic"
- android:ellipsize="end"
- android:singleLine="true"
- android:layout_toRightOf="@id/timecolon">
- </TextView>
- </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類做一些修改:
代碼如下:
- package com.blueeagle;
- /*
- * Android開發之數據保存技術(一)
- * HelloSQLite.java
- * Created on: 2011-8-16
- * Author: blueeagle
- * Email: [email protected]
- */
- import android.app.ListActivity;
- import android.content.ContentValues;
- import android.database.Cursor;
- import android.os.Bundle;
- import android.widget.SimpleCursorAdapter;
- import static com.blueeagle.Constants.TIME;
- import static com.blueeagle.Constants.TITLE;
- import static com.blueeagle.Constants.CONTENT_URI;
- import static android.provider.BaseColumns._ID;
- public class HelloSQLite extends ListActivity {
- /** Called when the activity is first created. */
- //private MySQLite mySqlite;
- private static String[] FROM = {_ID,TIME,TITLE};
- private static String ORDER_BY = TIME+" DESC";
- //@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- addItem("Hello,Android!");
- Cursor cursor = getItems();
- showItems(cursor);
- }
- private static int[] TO = {R.id.rowid,R.id.time,R.id.title};
- //顯示查詢結果
- private void showItems(Cursor cursor) {
- // TODO Auto-generated method stub
- SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,R.layout.item,cursor,FROM,TO);
- setListAdapter(adapter);
- }
- //使用ContentProvider時,geiItems方法也化簡了
- private Cursor getItems() {
- return managedQuery(CONTENT_URI,FROM,null,null,ORDER_BY);
- }
- //此處使用Activity.managedQuery()方法,將內容URI,感興趣的列表和應該使用的排序順序傳遞給該方法。
- private void addItem(String string) {
- ContentValues values = new ContentValues();
- values.put(TIME, System.currentTimeMillis());
- values.put(TITLE, string);
- getContentResolver().insert(CONTENT_URI, values);
- }
- //沒有了對getWriteableDatabase()的調用,對insertOrThrow的調用替換成了getContentResolver().insert(CONTENT_URI, values)
- //我們使用了一個URI而不是用數據庫句柄。
- }
下面就是實現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
- package com.blueeagle;
- /*
- * Android開發之數據保存技術(一)
- * ItemsProvider.java
- * Created on: 2011-8-16
- * Author: blueeagle
- * Email: [email protected]
- */
- import android.content.ContentProvider;
- import android.content.ContentUris;
- import android.content.ContentValues;
- import android.content.UriMatcher;
- import android.database.Cursor;
- import android.database.sqlite.SQLiteDatabase;
- import android.net.Uri;
- import android.text.TextUtils;
- import static com.blueeagle.Constants.CONTENT_URI;
- import static com.blueeagle.Constants.TABLE_NAME;
- import static com.blueeagle.Constants.AUTHORITY;
- import static android.provider.BaseColumns._ID;
- public class ItemsProvider extends ContentProvider {
- private static final int ITEMS = 1;
- private static final int ITEMS_ID = 2;
- /** The MIME type of a directory of items */
- private static final String CONTENT_TYPE
- = "vnd.android.cursor.dir/vnd.com.blueeagle";
- /** The MIME type of a single item */
- private static final String CONTENT_ITEM_TYPE
- = "vnd.android.cursor.item/vnd.com.blueeagle";
- private MySQLite myDataBase ;
- private UriMatcher myUriMatcher;
- @Override
- public boolean onCreate() {
- myUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- myUriMatcher.addURI(AUTHORITY, "HelloSQLite", ITEMS);
- myUriMatcher.addURI(AUTHORITY, "HelloSQLite/#", ITEMS_ID);
- myDataBase = new MySQLite(getContext());
- return true;
- }
- @Override
- public int delete(Uri uri, String selection,
- // TODO Auto-generated method stub
- String[] selectionArgs) {
- SQLiteDatabase db = myDataBase.getWritableDatabase();
- int count;
- switch (myUriMatcher.match(uri)) {
- case ITEMS:
- count = db.delete(TABLE_NAME, selection, selectionArgs);
- break;
- case ITEMS_ID:
- long id = Long.parseLong(uri.getPathSegments().get(1));
- count = db.delete(TABLE_NAME, appendRowId(selection, id),
- selectionArgs);
- break;
- default:
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- // Notify any watchers of the change
- getContext().getContentResolver().notifyChange(uri, null);
- return count;
- }
- @Override
- public String getType(Uri uri) {
- // TODO Auto-generated method stub
- switch (myUriMatcher.match(uri)) {
- case ITEMS:
- return CONTENT_TYPE;
- case ITEMS_ID:
- return CONTENT_ITEM_TYPE;
- default:
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- }
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- // TODO Auto-generated method stub
- SQLiteDatabase db = myDataBase.getWritableDatabase();
- // Validate the requested uri
- if (myUriMatcher.match(uri) != ITEMS) {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- // Insert into database
- long id = db.insertOrThrow(TABLE_NAME, null, values);
- // Notify any watchers of the change
- Uri newUri = ContentUris.withAppendedId(CONTENT_URI, id);
- getContext().getContentResolver().notifyChange(newUri, null);
- return newUri;
- }
- @Override
- public Cursor query(Uri uri, String[] projection,
- String selection, String[] selectionArgs, String orderBy) {
- // TODO Auto-generated method stub
- if (myUriMatcher.match(uri) == ITEMS_ID) {
- long id = Long.parseLong(uri.getPathSegments().get(1));
- selection = appendRowId(selection, id);
- }
- // Get the database and run the query
- SQLiteDatabase db = myDataBase.getReadableDatabase();
- Cursor cursor = db.query(TABLE_NAME, projection, selection,
- selectionArgs, null, null, orderBy);
- // Tell the cursor what uri to watch, so it knows when its
- // source data changes
- cursor.setNotificationUri(getContext().getContentResolver(),
- uri);
- return cursor;
- }
- private String appendRowId(String selection, long id) {
- // TODO Auto-generated method stub
- return _ID + "=" + id
- + (!TextUtils.isEmpty(selection)
- ? " AND (" + selection + ')'
- : "");
- }
- @Override
- public int update(Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
- // TODO Auto-generated method stub
- SQLiteDatabase db = myDataBase.getWritableDatabase();
- int count;
- switch (myUriMatcher.match(uri)) {
- case ITEMS:
- count = db.update(TABLE_NAME, values, selection,
- selectionArgs);
- break;
- case ITEMS_ID:
- long id = Long.parseLong(uri.getPathSegments().get(1));
- count = db.update(TABLE_NAME, values, appendRowId(
- selection, id), selectionArgs);
- break;
- default:
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- // Notify any watchers of the change
- getContext().getContentResolver().notifyChange(uri, null);
- return count;
- }
- }
總結一下,創建一個新的內容提供器。
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以確定它們的形式。就像前文說的那樣。