Android ContentProvider组件
2015-10-25 14:28
513 查看
摘要:Android四大组件,Activity,Service,Broadcast,ContentProvider,这是Android入门的基础课程,也是Android面试中常考的题目,很基础,借此机会和大家分享。
如果要复制并从应用程序到其它应用程序粘贴复杂的数据或文件,也需要自定义ContentProvider。
创建自定义Provider时,需要在Android manifest文件中进行声明,同其他四大组件
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()方法由系统主线程调用,必须避免执行冗长操作
注:访问Provider需要在应用程序manifest文件中声明特定的权限。
例如,获取用户字典中的数据,通过User Dictionary Provider
例如User Dictionary中的words表的URI
content:// 代表这是一个content uri
user_dictionary 表示权限
words 表示这个表的路径
通过_ID查找指定记录:
注:URI和Uri.Builder类包含方便的方法将字符串构建为格式化的URI对象。ContentUris包含方便的方法将ID值构建进URI对象中。
1. 请求获取provider的读取访问权限
2. 定义发送给provider查询数据的代码
选择条件由逻辑表达式,布尔表达式,列名和值等组合。
对应的SQL语句为:
正确的使用方式是:直接将用户的输入绑定到查询,作为一个参数,而不是作为一条SQL语句执行,这样就不会产生恶意的SQL脚本注入。
用户参数查询参数定义
根据占位符匹配用户参数
注:为了支持ListView中使用Cursor,返回的结果集中必须包含_ID列,这也就需要在设计数据表示,将索引列设计为 _ID。
插入后返回插入数据的URI,通过ContentUris.parseId()可以解析插入记录所在的ID值。
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 );
相关文章推荐
- OnTouchListener的用法
- Android中使用Serializable和Parcelable实现序列化详解(含实例)
- Android 多线程-----AsyncTask详解
- Android 开发手记二 C可执行程序编译实例
- 源码解析Android中View的measure量算过程
- android handler 和Looper 的理解
- Android开发 布局
- Android NDK 官方下载地址
- Android 学习笔记之Volley(六)实现获取服务器的字符串响应...
- Android Studio如何发布APK
- Android第一次打开应用程序,实现向导界面
- 遇到一个代码混乱不堪的Android项目该怎么办?
- Android中使用Makefile编译程序和库的方法
- Android:View绘制流程
- S3C6410板子移植 Android2.2
- Java中的多线程Thread Runnable及android的handler
- Android SDK is missing, out of date, or is missing templates. Please ensure you are using SDK versio
- Android Service进程间双向通信之Messenger(系列4)
- Android Studio快捷键
- Android开发工具类集锦