# An
droid use Content Providers for abstracting data into services.
# Android has built-in content providers. These SQLite database typically have an extension of .db and are accessible only from the implementation package. Any access outside that package must go through the content-provider interface.
# 构筑content-provider
\ Content provider 会在设备上注册一个authority(类似于域名). This authority registration occurs in the AndroidManifest.xml file.
\ How to register providers in AndroidManifest.xml
方法一:
< provider android:name ="SomeProvider" android:authorities ="com.your-company.SomeProvider" />
方法二:
< provider android:name ="NotePadProvider" android:authorities ="com.google.provider.NotePad" />
如:content://com.your-company.SomeProvider/
和 content://com.google.provider.NotePad/
# Content Provider also retrieve(检索) or manipulate(操作) date.
The URI指向Notes目录或集合的是:content://com.google.provider.NotePad/Notes
The URI indentify a specify note is : content://com.google.provider.NotePad/Notes/#
# is a id of a particular note.
# Android 内置provider 不需要content 提供完整的authority
如:content://media/internal/images
content://contacts/people/23
相应地,第三方provider需要content提供完整的authority。
# The output of the URI of a content provider is not a typed data,caller must know the structure of the rows and columns that are returned.
# MIME
\ A content provider has a built-in mechanism that allows you to determine the Multipurpose Internet Mail Extensions(MIME)type of the data represented by this URI.
\ content provider 通过内置的机制让你可以确定URI所表示的MIME类型。
# URI calls against a content provider return a Cursor.
# Structure of Android Content URIs
\ To retrieve data from a content provider, must invoke a URI.(returned a Cursor object)
\ Cursor object is a set of rows and columns.
\ Content Provider的格式:
1/ content://*/*/*
2/ content://authority-name/path-segment1/path-segment2/ETC...
e.g. content://com.google.provider.NotePad/notes/23
content之后,URI提供了一个authority的唯一标识,用于定位已注册的provider, /notes/23 称为 path-segments.
# Structure of Android MIME Types
\ 通过MIME类型,可以指导系统使用何种程序打开数据。
\ A MIME type has two parts : a type and a subtype
type/subtype
e.g. text/css, text/html, text/xml, text/vnd.curl, application/pdf, application/vnd.ms-excel
\ 一些主要的TYPE包括:application, audio, example, image, message, model, multipart, text, video.
\ Each primary type has subtype.
\ Subtype 前缀 "vnd." 表示subtype是专有的数据格式; 前缀 "x-" 表示未注册的非标准subtype.
\ 在Android中,vnd在MIME里表示type和subtype是非标准的由厂商定制的格式.
\ Android MIME type 两种形式:
1/ 单条特定记录 vnd.android.cursor.item/vnd.yourcompanyname.ContentType
2/ 数据集合或行集合 vnd.android.cursor.dir/vnd.yourcompanyname.ContentType
e.g.
vnd.android.cursor.item/vnd.google.note //One single note
vnd.android.cursor.dir/vnd.google.note //A collection or a directory of notes
# MIME可用于intent, intent可以基于数据的MIME类型指定系统用哪个activity调用
# MIME types are invariably(总是) derived(派生) from their URIs through content provider.
\ Three things keep in mind:
1/ The type and subtype need to be unique for what they represent. Primary type主要包括dir和item.
2/ The type and subtype need to be preceded with vnd if they are not standard.
3/ They are typically name-spaced for your specific need.
\ MIME中type一般只有 vnd.android.cursor.item 或 vnd.android.cursor.dir
\ MIME中subtype中 "vnd."之后, you are free to subtype it with anything you like.
# Reading Data Using URI
\ Android SDK有一个helper中有以下一条URI(const)
ContactsContract.Contacts.CONTENT_URI 相当于 content://com.android.contacts/contacts/
以下代码检索此ContentProvider中单行的数据:
\ An Android Cursor :
A cursor is a collection of rows.
Use moveToFirst() before reading any data, because the cursor starts off positioned before the first row.
Must know the column names.
Must know the column types.
All field-access methods are based on column number, so we must convert the column name to a column number first.
The cursor is random ( U can move forward and backward,and U can jump).
Because the cursor is random, u can ask it for a row count.
\ Navigating Through a Cursor Using a while Loop
isBeforeFirst()
isAfterLast()
isClosed()
\ Navigating Through a Cursor Using a for Loop
\ To find the number of rows in a cursor, Android provides a method on the cursor object called getCount().
# Passing where Clause
\ Content Providers offer 2 ways of passing a where clause.
1/ Through the URI.
2/ Through the combination of a string clause and a set of replaceable string-array arguments.
\ Through a URI
\ Using explicit where Clause
Activity.managedQuery()的函数原型
passing " null " will return all rows for the given URI,
passing " ? " wiil be replaced by the values from selectionArgs, the values will be bound as Strings.
e.g.
# Inserting Records
\ Single record insert, use ContentValues.
ContentValues is a key/value pairs.
插入记录时,先将记录填充ContentValues,然后请求android.content.ContentResolver用一个URI插入该记录.
在抽象层上,我们不能直接向database插入数据,而必须通过URI来确定provider,然后插入。
ContentResolver正是这样一个角色,它通过URI指向正确的provider,然后将传给它的ContentValues插入特定的provider,最后返回指向新插入记录的URI。
e.g.
\ ContentResolver create a internal file and stors the reference to the file name to a column "_data".
\ An update is similar to an insert, changed columns values pass through ContentValues:
\ First, plan a database, URIs, column names, and so on, and create a metadata class(元数据类) that defines constants for all these metadata elements.
\ Second, extend androd.content.ContentProvider.
\ Then, implement key methods: query, insert, update, delete, getType.
\ Finally, register the provider in the manifest.xml.
# Plan a database
\ Define Metabase class for Database:
\ Android use UriMatcher class to identify the URI types.
# Projection Maps
\ 映射用来将用户提供查询的数据列名称转换成数据库中真实数据列的名称。如果定义了映射,那么所有的列无论用户名称是否有改变都要列出,没改变的可以直接照抄。
如: sBooksProjectionMap.put(BookTableMetaData.BOOK_NAME, BookTableMetaData.BOOK_NAME);
# Android has built-in content providers. These SQLite database typically have an extension of .db and are accessible only from the implementation package. Any access outside that package must go through the content-provider interface.
# 构筑content-provider
\ Content provider 会在设备上注册一个authority(类似于域名). This authority registration occurs in the AndroidManifest.xml file.
\ How to register providers in AndroidManifest.xml
方法一:
< provider android:name ="SomeProvider" android:authorities ="com.your-company.SomeProvider" />
方法二:
< provider android:name ="NotePadProvider" android:authorities ="com.google.provider.NotePad" />
如:content://com.your-company.SomeProvider/
和 content://com.google.provider.NotePad/
# Content Provider also retrieve(检索) or manipulate(操作) date.
The URI指向Notes目录或集合的是:content://com.google.provider.NotePad/Notes
The URI indentify a specify note is : content://com.google.provider.NotePad/Notes/#
# is a id of a particular note.
# Android 内置provider 不需要content 提供完整的authority
如:content://media/internal/images
content://contacts/people/23
相应地,第三方provider需要content提供完整的authority。
# The output of the URI of a content provider is not a typed data,caller must know the structure of the rows and columns that are returned.
# MIME
\ A content provider has a built-in mechanism that allows you to determine the Multipurpose Internet Mail Extensions(MIME)type of the data represented by this URI.
\ content provider 通过内置的机制让你可以确定URI所表示的MIME类型。
# URI calls against a content provider return a Cursor.
# Structure of Android Content URIs
\ To retrieve data from a content provider, must invoke a URI.(returned a Cursor object)
\ Cursor object is a set of rows and columns.
\ Content Provider的格式:
1/ content://*/*/*
2/ content://authority-name/path-segment1/path-segment2/ETC...
e.g. content://com.google.provider.NotePad/notes/23
content之后,URI提供了一个authority的唯一标识,用于定位已注册的provider, /notes/23 称为 path-segments.
# Structure of Android MIME Types
\ 通过MIME类型,可以指导系统使用何种程序打开数据。
\ A MIME type has two parts : a type and a subtype
type/subtype
e.g. text/css, text/html, text/xml, text/vnd.curl, application/pdf, application/vnd.ms-excel
\ 一些主要的TYPE包括:application, audio, example, image, message, model, multipart, text, video.
\ Each primary type has subtype.
\ Subtype 前缀 "vnd." 表示subtype是专有的数据格式; 前缀 "x-" 表示未注册的非标准subtype.
\ 在Android中,vnd在MIME里表示type和subtype是非标准的由厂商定制的格式.
\ Android MIME type 两种形式:
1/ 单条特定记录 vnd.android.cursor.item/vnd.yourcompanyname.ContentType
2/ 数据集合或行集合 vnd.android.cursor.dir/vnd.yourcompanyname.ContentType
e.g.
vnd.android.cursor.item/vnd.google.note //One single note
vnd.android.cursor.dir/vnd.google.note //A collection or a directory of notes
# MIME可用于intent, intent可以基于数据的MIME类型指定系统用哪个activity调用
# MIME types are invariably(总是) derived(派生) from their URIs through content provider.
\ Three things keep in mind:
1/ The type and subtype need to be unique for what they represent. Primary type主要包括dir和item.
2/ The type and subtype need to be preceded with vnd if they are not standard.
3/ They are typically name-spaced for your specific need.
\ MIME中type一般只有 vnd.android.cursor.item 或 vnd.android.cursor.dir
\ MIME中subtype中 "vnd."之后, you are free to subtype it with anything you like.
# Reading Data Using URI
\ Android SDK有一个helper中有以下一条URI(const)
ContactsContract.Contacts.CONTENT_URI 相当于 content://com.android.contacts/contacts/
以下代码检索此ContentProvider中单行的数据:
Uri myPersonUri = Uri.withAppendedPath(peopleBaseUri,"23");
// query for this record
// manageQuery is a method on Activity class
Cursor cur = manageQuery (myPersonUri, null , null , null );
# Retrieving a cursor from a content provider
// query for this record
// manageQuery is a method on Activity class
Cursor cur = manageQuery (myPersonUri, null , null , null );
//
use this interface to see the constants
import ContactsContract.Contacts;
// An array specifying which columns to return
string[] projection = new string[]{
Contacts.ID;
Contacts.DISPLAY_NAME_PRIMARY
};
Uri mContactsUri = ContactsContract.Contacts.CONTENT_URI
// Best way to retrieve a query;returns a managed query
Cursor managedCursor = managedQuery( mContactsUri,
projection, // Which columns to return.
null , // WHERE clause
Contacts.DISPLAY_NAME_PRIMARY + "ASC"); // Order-by clause.
# The Android Cursor
import ContactsContract.Contacts;
// An array specifying which columns to return
string[] projection = new string[]{
Contacts.ID;
Contacts.DISPLAY_NAME_PRIMARY
};
Uri mContactsUri = ContactsContract.Contacts.CONTENT_URI
// Best way to retrieve a query;returns a managed query
Cursor managedCursor = managedQuery( mContactsUri,
projection, // Which columns to return.
null , // WHERE clause
Contacts.DISPLAY_NAME_PRIMARY + "ASC"); // Order-by clause.
\ An Android Cursor :
A cursor is a collection of rows.
Use moveToFirst() before reading any data, because the cursor starts off positioned before the first row.
Must know the column names.
Must know the column types.
All field-access methods are based on column number, so we must convert the column name to a column number first.
The cursor is random ( U can move forward and backward,and U can jump).
Because the cursor is random, u can ask it for a row count.
\ Navigating Through a Cursor Using a while Loop
if
(cur.moveToFirst() ==
false
)
{
// no rows empty cursor
return ;
}
// The cursor is already pointing to the first row
// lets access a few columns
int nameColumnIndex = cur.getColumnIndex(Contacts.DISPLAY_NAME_PRIMARY);
String name = cur.getString(nameColumnIndex);
// lets new see how we can loop through a cursor
while ( cur.moveToNext())
{
// cursor moved successfully
// access fields
}
\ Android provides some methods to learn where the cursor is
{
// no rows empty cursor
return ;
}
// The cursor is already pointing to the first row
// lets access a few columns
int nameColumnIndex = cur.getColumnIndex(Contacts.DISPLAY_NAME_PRIMARY);
String name = cur.getString(nameColumnIndex);
// lets new see how we can loop through a cursor
while ( cur.moveToNext())
{
// cursor moved successfully
// access fields
}
isBeforeFirst()
isAfterLast()
isClosed()
\ Navigating Through a Cursor Using a for Loop
//
Get your indexs first outside the for loop
int nameColumn = cur.getColumnIndex(Contacts.DISPLAY_NAME_PRIMARY);
// Walk the cursor now based on column indexs
for ( cur.moveToFirst(); !cur.is AfterLast(); cur.moveToNext())
{
String name = cur.getString(nameColumn);
}
\ Get the indexes first from the cursor to avoid surprises.
int nameColumn = cur.getColumnIndex(Contacts.DISPLAY_NAME_PRIMARY);
// Walk the cursor now based on column indexs
for ( cur.moveToFirst(); !cur.is AfterLast(); cur.moveToNext())
{
String name = cur.getString(nameColumn);
}
\ To find the number of rows in a cursor, Android provides a method on the cursor object called getCount().
# Passing where Clause
\ Content Providers offer 2 ways of passing a where clause.
1/ Through the URI.
2/ Through the combination of a string clause and a set of replaceable string-array arguments.
\ Through a URI
Activity someActivity
// ..initialize someActivity
String noteUri = "content://com.google.provider.NotePad/notes/23";
Cursor managedCursor = someActivity.managedQuery( noteUri,
projection, // Which columns to return.
null , // WHERE clause
null ); // Order-by clause
The id we want is embedded in the URI itself.
// ..initialize someActivity
String noteUri = "content://com.google.provider.NotePad/notes/23";
Cursor managedCursor = someActivity.managedQuery( noteUri,
projection, // Which columns to return.
null , // WHERE clause
null ); // Order-by clause
\ Using explicit where Clause
Activity.managedQuery()的函数原型
public
final
Cursor managedQuery( Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
String sortOrder)
The selection string represents a where clause declaring which rows to return,
String[] projection,
String selection,
String[] selectionArgs,
String sortOrder)
passing " null " will return all rows for the given URI,
passing " ? " wiil be replaced by the values from selectionArgs, the values will be bound as Strings.
e.g.
//
URI method
managedQuery( "content://com.google.provider.NotePad/notes/23",
null ,
null ,
null ,
null );
// explicit where clause
managedQuery( "content://com.google.provider.NotePad/notes",
null ,
"_id=?",
new String[] {23},
null );
managedQuery( "content://com.google.provider.NotePad/notes/23",
null ,
null ,
null ,
null );
// explicit where clause
managedQuery( "content://com.google.provider.NotePad/notes",
null ,
"_id=?",
new String[] {23},
null );
# Inserting Records
\ Single record insert, use ContentValues.
ContentValues is a key/value pairs.
插入记录时,先将记录填充ContentValues,然后请求android.content.ContentResolver用一个URI插入该记录.
在抽象层上,我们不能直接向database插入数据,而必须通过URI来确定provider,然后插入。
ContentResolver正是这样一个角色,它通过URI指向正确的provider,然后将传给它的ContentValues插入特定的provider,最后返回指向新插入记录的URI。
e.g.
ContentValues values =
new
ContentValues();
values.put( "title", "New note");
values.put( "note", "This is a new note");
// values object is now ready to be inserted
// U can get a reference to a ContentResolver by asking the Activity class
ContentResolver contentResolver = activity.getContentResolver();
// Tell ContentResolver a URI to insert the row.
Uri uri = contentResolver.insert( Notepad.Notes.CONTENT_URI, values);
// This call returns a URI pointing to the newly inserted record.
// This returned URI match the structure: Notepad.Notes.CONTENT_URI/new_id
# Adding a File to a Content Provider
values.put( "title", "New note");
values.put( "note", "This is a new note");
// values object is now ready to be inserted
// U can get a reference to a ContentResolver by asking the Activity class
ContentResolver contentResolver = activity.getContentResolver();
// Tell ContentResolver a URI to insert the row.
Uri uri = contentResolver.insert( Notepad.Notes.CONTENT_URI, values);
// This call returns a URI pointing to the newly inserted record.
// This returned URI match the structure: Notepad.Notes.CONTENT_URI/new_id
\ ContentResolver create a internal file and stors the reference to the file name to a column "_data".
//
insert some record to return URI
ContentResolver contentResolver = activity.getContentResolver();
Uri newUri = contentResover.insert( Notepad.Notes.CONTENT_URI, values);
// Once we have the URI of the record, ask the ContentResolver to get a reference to the file output stream
// ContentResolver hides the access to the _data filed where it stores the real file reference
OutputStream outStream = activity.getContentResolver().openOutputStream( newUri);
someSourceBitmap.compress( Bitmap.CompressFormat.JGEG, 50, outStream);
outStream.close();
// The code then uses that output stream to write to
# Update and Delete
ContentResolver contentResolver = activity.getContentResolver();
Uri newUri = contentResover.insert( Notepad.Notes.CONTENT_URI, values);
// Once we have the URI of the record, ask the ContentResolver to get a reference to the file output stream
// ContentResolver hides the access to the _data filed where it stores the real file reference
OutputStream outStream = activity.getContentResolver().openOutputStream( newUri);
someSourceBitmap.compress( Bitmap.CompressFormat.JGEG, 50, outStream);
outStream.close();
// The code then uses that output stream to write to
\ An update is similar to an insert, changed columns values pass through ContentValues:
int
numberOfRowsUpdated = activity.getContentResolver().update(
Uri uri,
ContentValues values,
String whereClause,
String[] selectionArgs )
\ Delete method:
Uri uri,
ContentValues values,
String whereClause,
String[] selectionArgs )
int
numberOfRowsDeleted
=
activity.getContentResolver().delete(
Uri uri,
String whereClause,
String[] selectionArgs )
# Implementing Content Providers
Uri uri,
String whereClause,
String[] selectionArgs )
\ First, plan a database, URIs, column names, and so on, and create a metadata class(元数据类) that defines constants for all these metadata elements.
\ Second, extend androd.content.ContentProvider.
\ Then, implement key methods: query, insert, update, delete, getType.
\ Finally, register the provider in the manifest.xml.
# Plan a database
\ Define Metabase class for Database:
public
class BookProviderMetaData
{
public static final String AUTHORITY = "com.androidbook.provider.BookProvider"; // use this String to register in manifest.xml
public static final String DATABASE_NAME = "book.db";
public static final int DATABASE_VERSION = 1;
public static final String BOOKS_TABLE_NAME = "books";
private BookProviderMetaData() {} // 空构造函数
// inner class BookTable
public static final class BookTableMetaBata implements BaseColumns // The BaseColumns class provides the standard _id field,which represents the row ID.
{
private BookTableMetaData() {} // 空构造函数
public static final String TABLE_NAME = "books";
// uri and MIME type definitions
public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY + "/books");
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.androidbook.book";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.androidbook.book";
public static final String DEFAULT_SORT_ORDER = "modified DESC";
// Columns, string type
public static final String BOOK_NAME = "name";
public static final String BOOK_ISBN = "isbn";
public static final String BOOK_AUTHOR = "author";
// Integer from System.currentTimeMillis()
public static final String CREATED_DATE = "created";
public static final String MODIFIED_DATE = "modified";
}
}
# Implementing the BookProvider Content Provider{
public static final String AUTHORITY = "com.androidbook.provider.BookProvider"; // use this String to register in manifest.xml
public static final String DATABASE_NAME = "book.db";
public static final int DATABASE_VERSION = 1;
public static final String BOOKS_TABLE_NAME = "books";
private BookProviderMetaData() {} // 空构造函数
// inner class BookTable
public static final class BookTableMetaBata implements BaseColumns // The BaseColumns class provides the standard _id field,which represents the row ID.
{
private BookTableMetaData() {} // 空构造函数
public static final String TABLE_NAME = "books";
// uri and MIME type definitions
public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY + "/books");
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.androidbook.book";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.androidbook.book";
public static final String DEFAULT_SORT_ORDER = "modified DESC";
// Columns, string type
public static final String BOOK_NAME = "name";
public static final String BOOK_ISBN = "isbn";
public static final String BOOK_AUTHOR = "author";
// Integer from System.currentTimeMillis()
public static final String CREATED_DATE = "created";
public static final String MODIFIED_DATE = "modified";
}
}
public
class BookProvider
extends ContentProvider
{
// Setup projection Map
private static HashMap<String, String> sBooksProjectionMap; // not final
static
{
sBooksProjectionMap = new HashMap<String, String>();
sBooksProjectionMap.put(BookTableMetaData._ID, BookTableMetaData._ID); // use HashMap.put()
// name,isbn,author
sBooksProjectionMap.put(BookTableMetaData.BOOK_NAME, BookTableMetaData.BOOK_NAME);
sBooksProjectionMap.put(BookTableMetaData.BOOK_ISBN, BookTableMetaData.BOOK_ISBN);
sBooksProjectionMap.put(BookTableMetaData.BOOK_AUTHOR, BookTableMetaData.BOOK_AUTHOR);
// created date, modified date
sBooksProjectionMap.put(BookTableMetaData.CREATE_DATE, BookTableMetaData.CREATE_DATE);
sBooksprojectionMap.put(BookTableMetaData.MODIFIED_DATE, BookTableMetaData.MODIFIED_DATE);
}
// Setup URIs
// Provide a mechanism to indentify all the incoming uri patterns.
private static final UriMatcher sUriMatcher;
// define ids for each uri type
private static final int INCOMING_BOOK_COLLECTION_URI_INDICATOR = 1;
private static final int INCOMING_SINGLE_BOOK_URI_INDICATOR = 2;
static{
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); // UriMatcher returns NO_MATCH when don't match.
// Register pattern for the books
sUriMatcher.addURI( BookProviderMetaData.AUTHORITY, "books", INCOMING_BOOK_COLLECTION_URI_INDICATOR);
// Register pattern for a single book
sUriMatcher.addURI( BookProviderMetaData.AUTHORITY, "books/#", INCOMING_SINGLE_BOOK_URI_INDICATOR);
}
// Setup/Create Database
// This class helps open, create, and upgrade the database file.
private static class DatabaseHelper extends SQLiteOpenHelper
{
DatabaseHelper(Context context){
super(
context,
BookProviderMetaData.DATABASE_NAME,
null,
BookProviderMetaData.DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db)
{
Log.d(TAG,"inner oncreate called");
db.execSQL("CREATE TABLE " + BookTableMetaData.TABLE_NAME + " ("
+ BookTableMetaData._ID + " INTEGER PRIMARY KEY,"
+ BookTableMetaData.BOOK_NAME + " TEXT,"
+ BookTableMetaData.BOOK_ISBN + " TEXT,"
+ BookTableMetaData.CREATED_DATE + " INTEGER,"
+ BookTableMetaData.MODIFIED_DATE + " INTEGER"
+ ");");
}
@Override
public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion)
{
db.execSQL("DROP TABLE IF EXISTS " + BookTableMetaData.TABLE_NAME);
onCreate(db);
}
}
private DatabaseHelper mOpenHelper;
// Component creation callback
@Override
public boolean onCreate(){
mOpenHelper = new DatabaseHelper(getContext());
return ture;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{
SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); // The query() method use SQLiteQueryBuilder() to execute the query.
switch (sUriMatcher.match(uri)){
case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
qb.setTables(BookTableMetaData.TABLE_NAME);
qb.setProjectionMap(sBooksProjectionMap);
break;
case INCOMING_SINGLE_BOOK_URI_INDICATOR:
qb.setTables(BookTableMetaData.TABLE_NAME);
qb.setProjectionMap(sBooksProjectionMap);
qb.appendWhere(BookTableMetaData._ID + "=" + uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Unknow URI" + uri);
}
// If no sort order is specified use the default
String orderBy;
if (TextUtils.isEmpty(sortOrder)){
orderBy = BookTableMetaData.DEFAULT_SORT_ORDER;
} else {
orderBy = sortOrder;
}
// Get the database and run the query
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
// example of getting a count
int i = c.getCount();
// Tell the cursor what uri to watch, so it knows when its source data changes
c.setNotificationUri(getContext().getContentResolver(), uri);
return c; // query() method returns a cursor to caller.
}
@Override
public String getType(Uri uri){ // Content Provider must implement getType() method to return a MIME type for a given URI
switch (sUriMatcher.match(uri)){
case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
return BookTableMetaData.CONTENT_TYPE;
case INCOMING_SINGLE_BOOK_URI_INDICATOR:
return BookTableMetaData.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknow URI " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues initialValues){
// Validate(验证) the requested uri
if (sUriMatcher.match(uri) != INCOMING_BOOK_COLLECTION_URI_INDICATOR)
{
throw new IllegalArgumentException("Unknow URI" + uri);
}
ContentValues values;
if (initialValues != null){
values = new ContentValues(initialValues);
} else{
values = new ContentValues();
}
Long now = Long.valueOf(System.currentTimeMillis());
// Make sure that the fields are all set
if (values.containsKey(BookTableMetaData.CREATED_DATE) == false)
{
values.put(BookTableMetaData.CREATED_DATE, now);
}
if (values.containsKey(BookTableMetaData.MODIFIED_DATE) == false)
{
values.put(BookTableMetaData.MODIFIED_DATE, now);
}
if (values.containsKey(BookTableMetaData.BOOK_NAME) == false)
{
throw new SQLException("Failed to insert row because Book Name is needed " + uri);
}
if (values.containsKey(BookTableMetaData.BOOK_ISBN) == false)
{
values.put(BookTableMetaData.BOOk_ISBN, "Unknow ISBN");
}
if (values.containsKey(BookTableMetaData.BOOK_AUTHOR) == false)
{
values.put(BookTableMetaData.BOOK_AUTHOR, "Unknow Author");
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
// The insert() method use SQLiteDatabase object to insert new record and returns the newly inserted ID.
long rowId = db.insert(BookTableMetaData.TABLE_NAME, BookTableMetaData.BOOK_NAME, values);
if (rowId > 0){
Uri insertedBookUri = ContentUris.withAppendedId(BookTableMetaData.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(insertedBookUri, null);
return insertedBookUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
@Override
public int delete( Uri uri, String where, String[] whereArgs )
{
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)){
case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
count = db.delete(BookTableMetaData.TABLE_NAME, where, whereArgs);
break;
case INCOMING_SINGLE_BOOK_URI_INDICATOR:
String rowId = uri.getPathSegments().get(1);
count = db.delete(BookTableMetaData.TABLE_NAME, BookTableMetaData._ID + "=" + rowId + (!TextUtils.isEmpty(where)? " AND(" + where + ')' : ""), whereArgs);
break;
default:
throw new IllegalArgumentException("Unknow URI " + uri);
}
getContext().getContentResolver.().notifyChange(uri, null);
}
@Override
public int update( Uri uri, ContentValues values, String where, String[] whereArgs)
{
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)){
case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
count = db.update(BookTableMetaData.TABLE_NAME, values, where, whereArgs);
break;
case INCOMING_SINGLE_BOOK_URI_INDICATOR:
String rowId = uri.getPathSegments().get(1);
count = db.update(BookTableMetaData.TABLE_NAME, values, BookTableMetaData._ID + "=" + rowId + (!TextUtils.isEmpty(where)? " AND (" + where + ')' : ""), whereArgs);
break;
default:
throw new IllegalArgumentExpection("Unknow URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count; // The update() method returns the number of rows updated in the process.
}
}
# UriMatcher Class{
// Setup projection Map
private static HashMap<String, String> sBooksProjectionMap; // not final
static
{
sBooksProjectionMap = new HashMap<String, String>();
sBooksProjectionMap.put(BookTableMetaData._ID, BookTableMetaData._ID); // use HashMap.put()
// name,isbn,author
sBooksProjectionMap.put(BookTableMetaData.BOOK_NAME, BookTableMetaData.BOOK_NAME);
sBooksProjectionMap.put(BookTableMetaData.BOOK_ISBN, BookTableMetaData.BOOK_ISBN);
sBooksProjectionMap.put(BookTableMetaData.BOOK_AUTHOR, BookTableMetaData.BOOK_AUTHOR);
// created date, modified date
sBooksProjectionMap.put(BookTableMetaData.CREATE_DATE, BookTableMetaData.CREATE_DATE);
sBooksprojectionMap.put(BookTableMetaData.MODIFIED_DATE, BookTableMetaData.MODIFIED_DATE);
}
// Setup URIs
// Provide a mechanism to indentify all the incoming uri patterns.
private static final UriMatcher sUriMatcher;
// define ids for each uri type
private static final int INCOMING_BOOK_COLLECTION_URI_INDICATOR = 1;
private static final int INCOMING_SINGLE_BOOK_URI_INDICATOR = 2;
static{
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); // UriMatcher returns NO_MATCH when don't match.
// Register pattern for the books
sUriMatcher.addURI( BookProviderMetaData.AUTHORITY, "books", INCOMING_BOOK_COLLECTION_URI_INDICATOR);
// Register pattern for a single book
sUriMatcher.addURI( BookProviderMetaData.AUTHORITY, "books/#", INCOMING_SINGLE_BOOK_URI_INDICATOR);
}
// Setup/Create Database
// This class helps open, create, and upgrade the database file.
private static class DatabaseHelper extends SQLiteOpenHelper
{
DatabaseHelper(Context context){
super(
context,
BookProviderMetaData.DATABASE_NAME,
null,
BookProviderMetaData.DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db)
{
Log.d(TAG,"inner oncreate called");
db.execSQL("CREATE TABLE " + BookTableMetaData.TABLE_NAME + " ("
+ BookTableMetaData._ID + " INTEGER PRIMARY KEY,"
+ BookTableMetaData.BOOK_NAME + " TEXT,"
+ BookTableMetaData.BOOK_ISBN + " TEXT,"
+ BookTableMetaData.CREATED_DATE + " INTEGER,"
+ BookTableMetaData.MODIFIED_DATE + " INTEGER"
+ ");");
}
@Override
public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion)
{
db.execSQL("DROP TABLE IF EXISTS " + BookTableMetaData.TABLE_NAME);
onCreate(db);
}
}
private DatabaseHelper mOpenHelper;
// Component creation callback
@Override
public boolean onCreate(){
mOpenHelper = new DatabaseHelper(getContext());
return ture;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{
SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); // The query() method use SQLiteQueryBuilder() to execute the query.
switch (sUriMatcher.match(uri)){
case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
qb.setTables(BookTableMetaData.TABLE_NAME);
qb.setProjectionMap(sBooksProjectionMap);
break;
case INCOMING_SINGLE_BOOK_URI_INDICATOR:
qb.setTables(BookTableMetaData.TABLE_NAME);
qb.setProjectionMap(sBooksProjectionMap);
qb.appendWhere(BookTableMetaData._ID + "=" + uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Unknow URI" + uri);
}
// If no sort order is specified use the default
String orderBy;
if (TextUtils.isEmpty(sortOrder)){
orderBy = BookTableMetaData.DEFAULT_SORT_ORDER;
} else {
orderBy = sortOrder;
}
// Get the database and run the query
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
// example of getting a count
int i = c.getCount();
// Tell the cursor what uri to watch, so it knows when its source data changes
c.setNotificationUri(getContext().getContentResolver(), uri);
return c; // query() method returns a cursor to caller.
}
@Override
public String getType(Uri uri){ // Content Provider must implement getType() method to return a MIME type for a given URI
switch (sUriMatcher.match(uri)){
case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
return BookTableMetaData.CONTENT_TYPE;
case INCOMING_SINGLE_BOOK_URI_INDICATOR:
return BookTableMetaData.CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknow URI " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues initialValues){
// Validate(验证) the requested uri
if (sUriMatcher.match(uri) != INCOMING_BOOK_COLLECTION_URI_INDICATOR)
{
throw new IllegalArgumentException("Unknow URI" + uri);
}
ContentValues values;
if (initialValues != null){
values = new ContentValues(initialValues);
} else{
values = new ContentValues();
}
Long now = Long.valueOf(System.currentTimeMillis());
// Make sure that the fields are all set
if (values.containsKey(BookTableMetaData.CREATED_DATE) == false)
{
values.put(BookTableMetaData.CREATED_DATE, now);
}
if (values.containsKey(BookTableMetaData.MODIFIED_DATE) == false)
{
values.put(BookTableMetaData.MODIFIED_DATE, now);
}
if (values.containsKey(BookTableMetaData.BOOK_NAME) == false)
{
throw new SQLException("Failed to insert row because Book Name is needed " + uri);
}
if (values.containsKey(BookTableMetaData.BOOK_ISBN) == false)
{
values.put(BookTableMetaData.BOOk_ISBN, "Unknow ISBN");
}
if (values.containsKey(BookTableMetaData.BOOK_AUTHOR) == false)
{
values.put(BookTableMetaData.BOOK_AUTHOR, "Unknow Author");
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
// The insert() method use SQLiteDatabase object to insert new record and returns the newly inserted ID.
long rowId = db.insert(BookTableMetaData.TABLE_NAME, BookTableMetaData.BOOK_NAME, values);
if (rowId > 0){
Uri insertedBookUri = ContentUris.withAppendedId(BookTableMetaData.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(insertedBookUri, null);
return insertedBookUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
@Override
public int delete( Uri uri, String where, String[] whereArgs )
{
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)){
case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
count = db.delete(BookTableMetaData.TABLE_NAME, where, whereArgs);
break;
case INCOMING_SINGLE_BOOK_URI_INDICATOR:
String rowId = uri.getPathSegments().get(1);
count = db.delete(BookTableMetaData.TABLE_NAME, BookTableMetaData._ID + "=" + rowId + (!TextUtils.isEmpty(where)? " AND(" + where + ')' : ""), whereArgs);
break;
default:
throw new IllegalArgumentException("Unknow URI " + uri);
}
getContext().getContentResolver.().notifyChange(uri, null);
}
@Override
public int update( Uri uri, ContentValues values, String where, String[] whereArgs)
{
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)){
case INCOMING_BOOK_COLLECTION_URI_INDICATOR:
count = db.update(BookTableMetaData.TABLE_NAME, values, where, whereArgs);
break;
case INCOMING_SINGLE_BOOK_URI_INDICATOR:
String rowId = uri.getPathSegments().get(1);
count = db.update(BookTableMetaData.TABLE_NAME, values, BookTableMetaData._ID + "=" + rowId + (!TextUtils.isEmpty(where)? " AND (" + where + ')' : ""), whereArgs);
break;
default:
throw new IllegalArgumentExpection("Unknow URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count; // The update() method returns the number of rows updated in the process.
}
}
\ Android use UriMatcher class to identify the URI types.
# Projection Maps
\ 映射用来将用户提供查询的数据列名称转换成数据库中真实数据列的名称。如果定义了映射,那么所有的列无论用户名称是否有改变都要列出,没改变的可以直接照抄。
如: sBooksProjectionMap.put(BookTableMetaData.BOOK_NAME, BookTableMetaData.BOOK_NAME);
sBooksProjectionMap.put(BookTableMetaData.BOOK_ISBN, BookTableMetaData.BOOK_ISBN);
sBooksProjectionMap.put(BookTableMetaData.BOOK_AUTHOR, BookTableMetaData.BOOK_AUTHOR);