以简单的备忘录应用程程序为例,实现ContentProvider,并测试
2012-02-10 16:27
369 查看
1. 如何为自己的应用程序自定义ContentProvider
首先,我们得有数据。所以,需要创建一个SQLite数据库来存储数据。而为了访问数据库,我们需要提供访问数据库的各种接口,如创建,打开,升级等
其次,创建一个类,继承ContentProvider类,并实现其中访问数据的所有方法,包括:1)query():查询 2) insert():插入 3)update():插入 4)delete():删除 5)getType():获得类型 6)onCreate():创建时调用。
最后,定义好的ContentProvider类必须在AndroidManifest.xml里声明后,才能使用。声明中必须添加的参数是授权属性“android:authorities”。
在实际的写代码过程中,目前只能好好研究developer.android.com上面的相关实例代码,学习Google提供的良好规范的代码:
http://developer.android.com/resources/samples/NotePad/src/com/example/android/notepad/index.html
在这个Note pad的例子中,Notepad.java和NotePadProvider.java两个文件是与创建ContentProvider相关的两个文件。其中Notepad.java类定义了用于可以访问的ContentProvider的常量,之所以创建这个文件,我觉得是优化代码架构而把常量写在同一个类中方便调用和访问。而NotePadProvider则主要涉及数据的相关操作,包括数据库的创建,连接以及继承实现ContenProvider类的6个方法。在阅读这部分代码时,对我来说最大的苦难是需要很好地理解URI的原理,只有这样才能看懂Google提供的代码的结构。关于URI的理解请参考官方文档:
http://developer.android.com/guide/topics/providers/content-providers.html#creating 中最后部分:Content URI Summary。
令人欣慰的是,Google提供的实例代码有详细的注释,这样方便了我们去理解和学习。
2. 现在以备忘录程序为例,来实现以上各个部分。
注:假设备忘录程序中只有一张数据表,叫做Memo, 表中有两个字段: _ID 和 MemoContent。
2.1 参考Notepad.java 创建 MemoContract.java 类,定义各种 主要的URI和数据表字段常量。之所以定义这个类,可以参考下面关于该类的注释
package com.memo;
import android.net.Uri;
import android.provider.BaseColumns;
/**
* Defines a contract between the Memo content provider and its clients.
* A contract defines the information that a client needs to access the provider as one or more data tables.
* A contract is a public, non-extendable (final) class that contains constants defining column names and URIs.
* A well-written client depends only on the constants in the contract.
*/
public
final class MemoContract {
/**
* identification of the content provider.
*/
public
static final String AUTHORITY
= "com.memo.MemoProvider";
/**
* This class can not be instantiated
*/
private MemoContract(){
}
/**
* Memo table contract
*/
public
static final
class Memo
implements BaseColumns{
/**
* This class can not be instantiated
*/
private Memo(){
}
/**
* The table name offered by this provider
*/
public
static
final String TABLE_NAME
= "Memo";
/* URI definition
*/
/**
* The content:// style URL for this table
*/
public
static
final Uri CONTENT_URI=Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);
/* MIME definitions
*/
/**
* The MIME type of {@link #CONTENT_URI} providing a directory of memo.
*/
public
static
final String CONTENT_TYPE=
"vnd.android.cursor.dir/vnd.companyName.memo";
/**
* The MIME type of a {@link #CONTENT_URI} sub-directory of a single memo.
*/
public
static
final String CONTENT_ITEM_TYPE=
"vnd.android.cursor.item/vnd.companyName.memo";
/* Default sort order and column definitions for this table
*/
/**
* The default sort order for this table
*/
public
static
final String DEFAULT_SORT_ORDER
= _ID+" DESC";
/**
* Column name for the content of the memo
* <P>Type: TEXT</P>
*/
public
static
final String COLUMN_NAME_CONTENT
= "MemoContent";
}
}
2.2 参考NotePadProvider.java创建了MemoProvider类,该类提供了访问数据的主要接口,并继承实现了ContenProvider的query()方法,其它方法类似。详细代码如下:
package com.memo;
import java.util.HashMap;
import com.memo.MemoContract.Memo;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
/**
* Provides the access to the data of Memo
*/
public
class MemoProvider
extends ContentProvider {
/**
* a new DbHelper
*/
private DbHelper dbHelper;
/**
* Create and initialize a UriMatcher instance
*/
private
static final UriMatcher sUriMatcher
= new UriMatcher(UriMatcher.NO_MATCH);;
// The incoming URI matches the Memo URI pattern
private
static
final int MEMOS
= 1;
// The incoming URI matches the Memo ID URI pattern
private
static
final int MEMO_ID
= 2;
static{
// Add a pattern that routes URIs terminated with "Memos" to a Memos operation
sUriMatcher.addURI(MemoContract.AUTHORITY,
"Memo", MEMOS);
// Add a pattern that routes URIs terminated with "Memos" plus an integer
// to a memo ID operation
sUriMatcher.addURI(MemoContract.AUTHORITY,
"Memo/#", MEMO_ID);
}
/**
* A projection map used to select columns from the database
*/
private
static HashMap<String, String> ProjectionMap;
/* constants for whole database
*/
private
static final String DATABASE_NAME=
"db";
private
static final
int DATABASE_VERSION=
2;
/* table creation SQL statements
*/
public
static final String CREATE_TABLE_MEMO=
"create table
"+Memo.TABLE_NAME+" ("
+Memo._ID+" integer primary key autoincrement,
"
+Memo.COLUMN_NAME_CONTENT+" text)";
/**
* The helper class which manage the database creation and database version upgrade
*/
private
class DbHelper
extends SQLiteOpenHelper{
public DbHelper(Context context) {
super(context, DATABASE_NAME,
null, DATABASE_VERSION);
}
/**
* Create the data table by executing the SQL statement
*/
@Override
public
void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_MEMO);
}
/**
*
* Demonstrates that the provider must consider what happens when the
* underlying database is changed. In this sample, the database is upgraded
* by destroying the existing data.
* A real application should upgrade the database in place.
*/
@Override
public
void onUpgrade(SQLiteDatabase db,
int oldVersion,
int newVersion) {
// Logs that the database is being upgraded
Log.w(DATABASE_NAME, "Upgrading database from version
" + oldVersion
+ " to
" + newVersion
+
", which will destroy all old data");
// Kills the table and existing data
db.execSQL("DROP TABLE IF EXISTS Memo");
// Recreates the database with a new version
onCreate(db);
}
}
/**
*
* Initializes the provider by creating a new DbHelper. onCreate() is called
* automatically when Android creates the provider in response to a resolver request from a
* client.
*/
@Override
public
boolean onCreate() {
// Creates a new helper object. Note that the database itself isn't opened until
// something tries to access it, and it's only created if it doesn't already exist.
dbHelper=new DbHelper(getContext());
// Assumes that any failures will be reported by a thrown exception.
return
true;
}
/**
* This method is called when a client calls
* {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)}.
* Queries the database and returns a cursor containing the results.
*
* @return A cursor containing the results of the query. The cursor exists but is empty if
* the query returns no results or an exception occurs.
* @throws IllegalArgumentException if the incoming URI pattern is invalid.
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Constructs a new query builder and sets its table name
SQLiteQueryBuilder qb =
new SQLiteQueryBuilder();
/**
* Choose the projection and adjust the "where" clause based on URI pattern-matching.
*/
switch (sUriMatcher.match(uri)) {
// If the incoming URI is for menos, chooses the Memos projection
case MEMOS:
qb.setTables(Memo.TABLE_NAME);
qb.setProjectionMap(ProjectionMap);
break;
/* If the incoming URI is for a single memo identified by its ID, chooses the
* memo ID projection, and appends "_ID = <MemoID>" to the where clause, so that
* it selects that single memo
*/
case MEMO_ID:
qb.setTables(Memo.TABLE_NAME);
qb.setProjectionMap(ProjectionMap);
qb.appendWhere(
Memo._ID +
// the name of the ID column
"="
+
// the position of the memo ID itself in the incoming URI
uri.getPathSegments().get(1));
break;
default:
// If the URI doesn't match any of the known patterns, throw an exception.
throw
new IllegalArgumentException("Unknown URI
" + uri);
}
String orderBy;
// If no sort order is specified, uses the default
if (TextUtils.isEmpty(sortOrder)) {
orderBy = Memo.DEFAULT_SORT_ORDER;
} else {
// otherwise, uses the incoming sort order
orderBy = sortOrder;
}
// Opens the database object in "read" mode, since no writes need to be done.
SQLiteDatabase db = dbHelper.getReadableDatabase();
/*
* Performs the query. If no problems occur trying to read the database, then a Cursor
* object is returned; otherwise, the cursor variable contains null. If no records were
* selected, then the Cursor object is empty, and Cursor.getCount() returns 0.
*/
Cursor c = qb.query(
db, // The database to query
projection, // The columns to return from the query
selection, // The columns for the where clause
selectionArgs, // The values for the where clause
null,
// don't group the rows
null,
// don't filter by row groups
orderBy // The sort order
);
// Tells the Cursor what URI to watch, so it knows when its source data changes
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public
int delete(Uri arg0, String arg1, String[] arg2) {
// TODO Auto-generated method stub
return
0;
}
@Override
public String getType(Uri arg0) {
// TODO Auto-generated method stub
return
null;
}
@Override
public Uri insert(Uri arg0, ContentValues arg1) {
// TODO Auto-generated method stub
return
null;
}
@Override
public
int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
// TODO Auto-generated method stub
return
0;
}
}
2.3 在AndroidMenifest.xml中声明我们定义的ContentProvider:
<provider
android:name="MemoProvider"
android:authorities="com.memo.MemoProvider">
</ provider>
2.4 为了测试我定义的ContentProvider,我们在创建的Main.activity中,利用ContentResolver读取数据,并用ListView显示:参考上一篇博文:
/article/5936031.html 代码如下:
package com.memo;
import java.util.ArrayList;
import com.memo.MemoContract.Memo;
import android.app.ListActivity;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public
class Main extends ListActivity {
/** Called when the activity is first created.
*/
@Override
public
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri memoUri= Memo.CONTENT_URI;
String[] proj1=new String[]{Memo.COLUMN_NAME_CONTENT};
Cursor cur=getContentResolver().query(memoUri,proj1,
null,
null, null);
//declare a ArrayList object to store the data that will present to the user
ArrayList<String> memoList=new
ArrayList<String>();
if(cur.getCount()>0){
while(cur.moveToNext()){
memoList.add(cur.getString(0));
}
}
// binding the data to ListView
setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,
memoList));
ListView lv=getListView();
lv.setTextFilterEnabled(true);
}
}
首先,我们得有数据。所以,需要创建一个SQLite数据库来存储数据。而为了访问数据库,我们需要提供访问数据库的各种接口,如创建,打开,升级等
其次,创建一个类,继承ContentProvider类,并实现其中访问数据的所有方法,包括:1)query():查询 2) insert():插入 3)update():插入 4)delete():删除 5)getType():获得类型 6)onCreate():创建时调用。
最后,定义好的ContentProvider类必须在AndroidManifest.xml里声明后,才能使用。声明中必须添加的参数是授权属性“android:authorities”。
在实际的写代码过程中,目前只能好好研究developer.android.com上面的相关实例代码,学习Google提供的良好规范的代码:
http://developer.android.com/resources/samples/NotePad/src/com/example/android/notepad/index.html
在这个Note pad的例子中,Notepad.java和NotePadProvider.java两个文件是与创建ContentProvider相关的两个文件。其中Notepad.java类定义了用于可以访问的ContentProvider的常量,之所以创建这个文件,我觉得是优化代码架构而把常量写在同一个类中方便调用和访问。而NotePadProvider则主要涉及数据的相关操作,包括数据库的创建,连接以及继承实现ContenProvider类的6个方法。在阅读这部分代码时,对我来说最大的苦难是需要很好地理解URI的原理,只有这样才能看懂Google提供的代码的结构。关于URI的理解请参考官方文档:
http://developer.android.com/guide/topics/providers/content-providers.html#creating 中最后部分:Content URI Summary。
令人欣慰的是,Google提供的实例代码有详细的注释,这样方便了我们去理解和学习。
2. 现在以备忘录程序为例,来实现以上各个部分。
注:假设备忘录程序中只有一张数据表,叫做Memo, 表中有两个字段: _ID 和 MemoContent。
2.1 参考Notepad.java 创建 MemoContract.java 类,定义各种 主要的URI和数据表字段常量。之所以定义这个类,可以参考下面关于该类的注释
package com.memo;
import android.net.Uri;
import android.provider.BaseColumns;
/**
* Defines a contract between the Memo content provider and its clients.
* A contract defines the information that a client needs to access the provider as one or more data tables.
* A contract is a public, non-extendable (final) class that contains constants defining column names and URIs.
* A well-written client depends only on the constants in the contract.
*/
public
final class MemoContract {
/**
* identification of the content provider.
*/
public
static final String AUTHORITY
= "com.memo.MemoProvider";
/**
* This class can not be instantiated
*/
private MemoContract(){
}
/**
* Memo table contract
*/
public
static final
class Memo
implements BaseColumns{
/**
* This class can not be instantiated
*/
private Memo(){
}
/**
* The table name offered by this provider
*/
public
static
final String TABLE_NAME
= "Memo";
/* URI definition
*/
/**
* The content:// style URL for this table
*/
public
static
final Uri CONTENT_URI=Uri.parse("content://"+AUTHORITY+"/"+TABLE_NAME);
/* MIME definitions
*/
/**
* The MIME type of {@link #CONTENT_URI} providing a directory of memo.
*/
public
static
final String CONTENT_TYPE=
"vnd.android.cursor.dir/vnd.companyName.memo";
/**
* The MIME type of a {@link #CONTENT_URI} sub-directory of a single memo.
*/
public
static
final String CONTENT_ITEM_TYPE=
"vnd.android.cursor.item/vnd.companyName.memo";
/* Default sort order and column definitions for this table
*/
/**
* The default sort order for this table
*/
public
static
final String DEFAULT_SORT_ORDER
= _ID+" DESC";
/**
* Column name for the content of the memo
* <P>Type: TEXT</P>
*/
public
static
final String COLUMN_NAME_CONTENT
= "MemoContent";
}
}
2.2 参考NotePadProvider.java创建了MemoProvider类,该类提供了访问数据的主要接口,并继承实现了ContenProvider的query()方法,其它方法类似。详细代码如下:
package com.memo;
import java.util.HashMap;
import com.memo.MemoContract.Memo;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
/**
* Provides the access to the data of Memo
*/
public
class MemoProvider
extends ContentProvider {
/**
* a new DbHelper
*/
private DbHelper dbHelper;
/**
* Create and initialize a UriMatcher instance
*/
private
static final UriMatcher sUriMatcher
= new UriMatcher(UriMatcher.NO_MATCH);;
// The incoming URI matches the Memo URI pattern
private
static
final int MEMOS
= 1;
// The incoming URI matches the Memo ID URI pattern
private
static
final int MEMO_ID
= 2;
static{
// Add a pattern that routes URIs terminated with "Memos" to a Memos operation
sUriMatcher.addURI(MemoContract.AUTHORITY,
"Memo", MEMOS);
// Add a pattern that routes URIs terminated with "Memos" plus an integer
// to a memo ID operation
sUriMatcher.addURI(MemoContract.AUTHORITY,
"Memo/#", MEMO_ID);
}
/**
* A projection map used to select columns from the database
*/
private
static HashMap<String, String> ProjectionMap;
/* constants for whole database
*/
private
static final String DATABASE_NAME=
"db";
private
static final
int DATABASE_VERSION=
2;
/* table creation SQL statements
*/
public
static final String CREATE_TABLE_MEMO=
"create table
"+Memo.TABLE_NAME+" ("
+Memo._ID+" integer primary key autoincrement,
"
+Memo.COLUMN_NAME_CONTENT+" text)";
/**
* The helper class which manage the database creation and database version upgrade
*/
private
class DbHelper
extends SQLiteOpenHelper{
public DbHelper(Context context) {
super(context, DATABASE_NAME,
null, DATABASE_VERSION);
}
/**
* Create the data table by executing the SQL statement
*/
@Override
public
void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_MEMO);
}
/**
*
* Demonstrates that the provider must consider what happens when the
* underlying database is changed. In this sample, the database is upgraded
* by destroying the existing data.
* A real application should upgrade the database in place.
*/
@Override
public
void onUpgrade(SQLiteDatabase db,
int oldVersion,
int newVersion) {
// Logs that the database is being upgraded
Log.w(DATABASE_NAME, "Upgrading database from version
" + oldVersion
+ " to
" + newVersion
+
", which will destroy all old data");
// Kills the table and existing data
db.execSQL("DROP TABLE IF EXISTS Memo");
// Recreates the database with a new version
onCreate(db);
}
}
/**
*
* Initializes the provider by creating a new DbHelper. onCreate() is called
* automatically when Android creates the provider in response to a resolver request from a
* client.
*/
@Override
public
boolean onCreate() {
// Creates a new helper object. Note that the database itself isn't opened until
// something tries to access it, and it's only created if it doesn't already exist.
dbHelper=new DbHelper(getContext());
// Assumes that any failures will be reported by a thrown exception.
return
true;
}
/**
* This method is called when a client calls
* {@link android.content.ContentResolver#query(Uri, String[], String, String[], String)}.
* Queries the database and returns a cursor containing the results.
*
* @return A cursor containing the results of the query. The cursor exists but is empty if
* the query returns no results or an exception occurs.
* @throws IllegalArgumentException if the incoming URI pattern is invalid.
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Constructs a new query builder and sets its table name
SQLiteQueryBuilder qb =
new SQLiteQueryBuilder();
/**
* Choose the projection and adjust the "where" clause based on URI pattern-matching.
*/
switch (sUriMatcher.match(uri)) {
// If the incoming URI is for menos, chooses the Memos projection
case MEMOS:
qb.setTables(Memo.TABLE_NAME);
qb.setProjectionMap(ProjectionMap);
break;
/* If the incoming URI is for a single memo identified by its ID, chooses the
* memo ID projection, and appends "_ID = <MemoID>" to the where clause, so that
* it selects that single memo
*/
case MEMO_ID:
qb.setTables(Memo.TABLE_NAME);
qb.setProjectionMap(ProjectionMap);
qb.appendWhere(
Memo._ID +
// the name of the ID column
"="
+
// the position of the memo ID itself in the incoming URI
uri.getPathSegments().get(1));
break;
default:
// If the URI doesn't match any of the known patterns, throw an exception.
throw
new IllegalArgumentException("Unknown URI
" + uri);
}
String orderBy;
// If no sort order is specified, uses the default
if (TextUtils.isEmpty(sortOrder)) {
orderBy = Memo.DEFAULT_SORT_ORDER;
} else {
// otherwise, uses the incoming sort order
orderBy = sortOrder;
}
// Opens the database object in "read" mode, since no writes need to be done.
SQLiteDatabase db = dbHelper.getReadableDatabase();
/*
* Performs the query. If no problems occur trying to read the database, then a Cursor
* object is returned; otherwise, the cursor variable contains null. If no records were
* selected, then the Cursor object is empty, and Cursor.getCount() returns 0.
*/
Cursor c = qb.query(
db, // The database to query
projection, // The columns to return from the query
selection, // The columns for the where clause
selectionArgs, // The values for the where clause
null,
// don't group the rows
null,
// don't filter by row groups
orderBy // The sort order
);
// Tells the Cursor what URI to watch, so it knows when its source data changes
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@Override
public
int delete(Uri arg0, String arg1, String[] arg2) {
// TODO Auto-generated method stub
return
0;
}
@Override
public String getType(Uri arg0) {
// TODO Auto-generated method stub
return
null;
}
@Override
public Uri insert(Uri arg0, ContentValues arg1) {
// TODO Auto-generated method stub
return
null;
}
@Override
public
int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
// TODO Auto-generated method stub
return
0;
}
}
2.3 在AndroidMenifest.xml中声明我们定义的ContentProvider:
<provider
android:name="MemoProvider"
android:authorities="com.memo.MemoProvider">
</ provider>
2.4 为了测试我定义的ContentProvider,我们在创建的Main.activity中,利用ContentResolver读取数据,并用ListView显示:参考上一篇博文:
/article/5936031.html 代码如下:
package com.memo;
import java.util.ArrayList;
import com.memo.MemoContract.Memo;
import android.app.ListActivity;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public
class Main extends ListActivity {
/** Called when the activity is first created.
*/
@Override
public
void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri memoUri= Memo.CONTENT_URI;
String[] proj1=new String[]{Memo.COLUMN_NAME_CONTENT};
Cursor cur=getContentResolver().query(memoUri,proj1,
null,
null, null);
//declare a ArrayList object to store the data that will present to the user
ArrayList<String> memoList=new
ArrayList<String>();
if(cur.getCount()>0){
while(cur.moveToNext()){
memoList.add(cur.getString(0));
}
}
// binding the data to ListView
setListAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,
memoList));
ListView lv=getListView();
lv.setTextFilterEnabled(true);
}
}
相关文章推荐
- 一个简单的小测试程序实现(已做优化)
- java简单实现测试程序执行时间及日期转换字符串
- 一个Java实现的简单的多个客户端聊天程序(未测试)
- Java实训——创建一个窗体程序,实现简单的数学加、减法测试。
- 应用:Python实现简单聊天程序
- 基于mina实现一个简单数据采集中间件的多客户端在线测试程序
- TCPIP详解第1卷1.10标准化过程1.11RFC1.12标准的简单服务1.13互联网1.14实现1.15应用编程接口1.16测试网络
- android ContentProvider简单实现不同应用间的数据共享
- 通用型C/C++程序性能测试Benchmark的简单实现
- Ubuntu 16.04 MPICH应用2——简单程序测试
- c++程序中写测试log到文件的简单实现
- 用Java实现的简单人品测试程序
- linux网络编程十六:I/O复用的应用-poll简单实现聊天室程序
- win32汇编实现一个简单的TCP服务端程序(WinSock的简单认知应用)
- freeDiameter的简单client/server测试程序实现
- Appium+Robotframework实现Android应用的自动化测试-6:一个简单的例子
- (7)基于hadoop的简单网盘应用实现3
- Android实现获取本机中所有图片(Loader,CursorLoader,LoaderManager,SimpleCursorAdapter的简单应用)
- 简单实用的PHP程序实现文件上传
- 简单实现WEB程序在线安装