您的位置:首页 > 移动开发 > Android开发

Android ContentProvider组件

2015-10-25 14:28 513 查看
摘要:Android四大组件,Activity,Service,Broadcast,ContentProvider,这是Android入门的基础课程,也是Android面试中常考的题目,很基础,借此机会和大家分享。

ContentProvider

ContentProvider继承Object,实现了ComponentCallbacks2



概述

ContentProvider是Android组件之一,为应用程序提供数据,对数据进行封装,并通过ContentResolver接口为应用程序提供数据。只有当需要在多个应用程序间数据共享时,才使用该组件。例如联系人信息,需要在多个应用程序中共享,通过ContentProvider进行数据共享,其他情况下可以使用SQLiteDatabase进行数据存储。

如果要复制并从应用程序到其它应用程序粘贴复杂的数据或文件,也需要自定义ContentProvider。

创建自定义Provider时,需要在Android manifest文件中进行声明,同其他四大组件

主要需实现的方法

onCreate() 初始化provider时调用

query(Uri, String[], String, String[], String) 返回调用者需要查询的数据

insert(Uri, ContentValues) 将数据插入provider

update(Uri, ContentValues, String, String[]) 更新provider中的数据

delete(Uri, String, String[]) 删除provider中的数据

getType(Uri) 返回provider中数据的MIME type

注:数据访问方法(insert(),update())可能会被多个线程同时访问,必须保证线程安全,onCreate()方法由系统主线程调用,必须避免执行冗长操作

基础

访问ContentProvider

应用程序通过ContentResolver对象访问ContentProvider中的数据。ContentResolver对象中使用和provider中同名的方法,包括基本的CRUD(创建,检索,更新和删除,create, retrieve, update, and delete)访问持久化数据。

注:访问Provider需要在应用程序manifest文件中声明特定的权限。

例如,获取用户字典中的数据,通过User Dictionary Provider

// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI,   // 相当于from table
mProjection,                        // 要返回的数据列名
mSelectionClause                    // 选择的条件
mSelectionArgs,                     // 条件参数
mSortOrder);                        // 对返回数据排序


Content URIs

content uri 用来在provider中标记数据,content uri包含provider的符号名,一个指向数据表的路径名,uri作为provider方法的参数

例如User Dictionary中的words表的URI

content://user_dictionary/words


content:// 代表这是一个content uri

user_dictionary 表示权限

words 表示这个表的路径

通过_ID查找指定记录:

Uri singleUri = ContentUri.withAppendedId(UserDictionary.Words.CONTENT_URI,4);


注:URI和Uri.Builder类包含方便的方法将字符串构建为格式化的URI对象。ContentUris包含方便的方法将ID值构建进URI对象中。

检索数据

从provider中检索数据包括以下步骤:

1. 请求获取provider的读取访问权限

2. 定义发送给provider查询数据的代码

获取访问权限

通过< uses-permission >在manifest文件中指定所需要的权限,权限名称由provider定义。当应用程序被安装后,权限将自动被赋予相应的请求。

构建查询

// 定义要返回的列的映射
String[] mProjection =
{
UserDictionary.Words._ID,    // _ID
UserDictionary.Words.WORD,   // WORD
UserDictionary.Words.LOCALE  // LOCALE
};

// 定义选择的条件
String mSelectionClause = null;

// 选择条件所需的参数
String[] mSelectionArgs = {""};


选择条件由逻辑表达式,布尔表达式,列名和值等组合。

/*
* 定义一个只包含一个元素的字符串数组参数
*/
String[] mSelectionArgs = {""};

// 通过UI线程获取被查找的值
mSearchString = mSearchWord.getText().toString();

// 插入的代码检查是否有效或是否恶意输入

// 如果被查找的值为空,这取消查找限制条件
if (TextUtils.isEmpty(mSearchString)) {
// 返回所有值
mSelectionClause = null;
mSelectionArgs[0] = "";

} else {
// 构造一个用户输入的值的匹配选择条件。
mSelectionClause = UserDictionary.Words.WORD + " = ?";

// 将用户输入值放入字符串参数数组中,根据占位符匹配
mSelectionArgs[0] = mSearchString;

}

// 查询,并返回结果集游标
mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI,  // content uri
mProjection,                       // 返回结果集所包含的列的映射
mSelectionClause                   // 选择条件
mSelectionArgs,                    // 条件参数
mSortOrder);                       // 结果集排序

// 当发生异常,返回空或抛出异常
if (null == mCursor) {
/*
* 插入代码来处理错误
* 打印错误日志
*/
// 如果返回结果集为空,也可能是未检索到相关数据
} else if (mCursor.getCount() < 1) {

/*
* 插入代码通知用户查询不成功,但不是错误,提示用户插入数据
*/

} else {
// 插入处理结果的代码

}


对应的SQL语句为:

SELECT _ID, word, frequency, locale FROM words WHERE word = <userinput> ORDER BY word ASC;


防止恶意输入

如果用下面的方式构造选择条件,将会导致恶意的SQL脚本注入

// 错误的构造条件
String mSelectionClause =  "var = " + mUserInput;

// var = nothing; DROP TABLE *;


正确的使用方式是:直接将用户的输入绑定到查询,作为一个参数,而不是作为一条SQL语句执行,这样就不会产生恶意的SQL脚本注入。

// 通过占位符匹配用户输入参数
String mSelectionClause =  "var = ?";


用户参数查询参数定义

String[] selectionArgs = {""};


根据占位符匹配用户参数

selectionArgs[0] = mUserInput;


显示查询结果

当返回的结果不为空,没有抛出异常,并且返回的数据是一个list,则将数据游标通过SimpleCursorAdapter将数据填充到listview中显示。

// 定义list列表的列,用于显示数据
String[] mWordListColumns =
{
UserDictionary.Words.WORD,   // Contract class constant containing the word column name
UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
};

// 定义列表显示数据控件的ID
int[] mWordListItems = { R.id.dictWord, R.id.locale};

// 创建SimpleCursorAdapter
mCursorAdapter = new SimpleCursorAdapter(
getApplicationContext(),               // 应用程序上下文
R.layout.wordlistrow,                  // 自定义的列表项布局文件
mCursor,                               // 返回的结果集游标
mWordListColumns,                      // 结果集中数据列名称数组
mWordListItems,                        // 自定义列表项中的ID数组,与数据列名称数组对应
0);                                    // Flags(标志位,通常情况下不使用)

// 为ListView设置适配器
mWordList.setAdapter(mCursorAdapter);


注:为了支持ListView中使用Cursor,返回的结果集中必须包含_ID列,这也就需要在设计数据表示,将索引列设计为 _ID。

从结果集中获取数据

// 获取word列的索引值
int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);

/*
* 使用Cursor前判断是否为空或是否包含异常
*/

if (mCursor != null) {
/*
* 获取数据前,先将游标指向第一条数据,判断是否包含下一条数据
*/
while (mCursor.moveToNext()) {

// 获取指定列的数据
newWord = mCursor.getString(index);

// 处理检索到的数据

...

// 结束循环
}
} else {

// 插入代码通知用户Cursor为空或处理异常
}


Content Provider Permissions

插入,更新,删除数据

插入数据

// 创建一个Uri对象,用于插入数据
Uri mNewUri;

...

// 创建ContentValues对象用于保存要插入的数据
ContentValues mNewValues = new ContentValues();

/*
* 将插入的数据以键值对的形式保存,键为列名,值为要保存的数据
*/
mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
mNewValues.put(UserDictionary.Words.WORD, "insert");
mNewValues.put(UserDictionary.Words.FREQUENCY, "100");

mNewUri = getContentResolver().insert(
UserDictionary.Word.CONTENT_URI,   // content uri
mNewValues                          // 要插入的数据
);


插入后返回插入数据的URI,通过ContentUris.parseId()可以解析插入记录所在的ID值。

content://user_dictionary/words/<id_value>


更新数据

ContentValues mUpdateValues = new ContentValues();

// 选择要更新的列
String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
String[] mSelectionArgs = {"en_%"};

// 返回被更新的记录统计
int mRowsUpdated = 0;

...

/*
* Sets the updated value and updates the selected words.
*/
mUpdateValues.putNull(UserDictionary.Words.LOCALE);

mRowsUpdated = getContentResolver().update(
UserDictionary.Words.CONTENT_URI,   // content URI
mUpdateValues                       // 要更新的列
mSelectionClause                    // 选择更新条件
mSelectionArgs                      // 条件参数
);


删除数据

String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
String[] mSelectionArgs = {"user"};

int mRowsDeleted = 0;

...

mRowsDeleted = getContentResolver().delete(
UserDictionary.Words.CONTENT_URI,
mSelectionClause
mSelectionArgs
);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: