Content Provider Basics

Android API Guides


Content Provider 负责管理访问结构化数据集,encapsulate 数据,提供安全访问数据机制。
访问数据的Client 与 提供数据的Provider 处于两个进程。

Client 使用ContentResolver, 负责与实现ContentProvider接口的实例进行进程间通信IPC。
一般来讲,无需开发provider,除非需要与其他应用共享数据,如提供自定义的搜索建议,复制或者粘贴复杂的数据或者文件到其他应用。

ContntResolver.query()会调用ContentProvider.query()获取数据。

// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
    mProjection,                        // The columns to return for each row
    mSelectionClause                    // Selection criteria
    mSelectionArgs,                     // Selection criteria
    mSortOrder);                        // The sort order for the returned rows

其中,

query argsselect keyword/parametersnotes
UriFROM table_nameUri maps to the table in the provider named table_name.
projectioncol,col,col,…projection is an array of columns that should be included for each row retrieved.
selectionWHERE col = valueselection specifies the criteria for selecting rows.
selectionArgs(No exact equivalent. Selection arguments replace ? placeholders in the selection clause.)
sortOrderORDER BY col,col,…sortOrder specifies the order in which rows appear in the returned Cursor.

ContentURI 标示一个数据Provider。由sybolic name(authority) 和 table name(path)组成。
content://user_dictionary/words
其中content://是scheme, user_dictionary 是authority, words是table.
许多provider允许访问单行数据,附上一个_ID。
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);
通常是在获取一系列数据后,然后想要更新某条数据或者删除某条数据。

Retriving Data From the provider

For the sake of clarity, 为清晰起见,演示的代码都在UI线程中。但在实际的代码中,一般都在子线程中采用异步查询等操作。也可以使用CursorLoader。

从provider中获取数据,一般有两步:
1. 请求read access permission
2. 编写代码发送查询请求。

1. 请求Read Permission

不可在运行时请求,只能在Manifest中静态指定, 和 exact permission name defined by provider.
当用户安装应用时,用户就会 implicitly grant(隐式授权)这个权限请求。

如用户词典User Dictionary Provider在其Manifest中定义了android.permission.READ_USER_DICTIONARY 权限。

构造请求查询代码

// A "projection" defines the columns that will be returned for each row
String[] mProjection =
{
    UserDictionary.Words._ID,    // Contract class constant for the _ID column name
    UserDictionary.Words.WORD,   // Contract class constant for the word column name
    UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
};

// Defines a string to contain the selection clause
String mSelectionClause = null;

// Initializes an array to contain selection arguments
String[] mSelectionArgs = {""};

mSelectionClause 是查询条件
而mSelectionArgs则是查询条件的参数,比如下面的代码。

/*
 * This defines a one-element String array to contain the selection argument.
 */
String[] mSelectionArgs = {""};

// Gets a word from the UI
mSearchString = mSearchWord.getText().toString();

// Remember to insert code here to check for invalid or malicious input.

// If the word is the empty string, gets everything
if (TextUtils.isEmpty(mSearchString)) {
    // Setting the selection clause to null will return all words
    mSelectionClause = null;
    mSelectionArgs[0] = "";

} else {
    // Constructs a selection clause that matches the word that the user entered.
    mSelectionClause = UserDictionary.Words.WORD + " = ?";

    // Moves the user's input string to the selection arguments.
    mSelectionArgs[0] = mSearchString;

}

// Does a query against the table and returns a Cursor object
mCursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
    mProjection,                       // The columns to return for each row
    mSelectionClause                   // Either null, or the word the user entered
    mSelectionArgs,                    // Either empty, or the string the user entered
    mSortOrder);                       // The sort order for the returned rows

// Some providers return null if an error occurs, others throw an exception
if (null == mCursor) {
    /*
     * Insert code here to handle the error. Be sure not to use the cursor! You may want to
     * call android.util.Log.e() to log this error.
     *
     */
// If the Cursor is empty, the provider found no matches
} else if (mCursor.getCount() < 1) {

    /*
     * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
     * an error. You may want to offer the user the option to insert a new row, or re-type the
     * search term.
     */

} else {
    // Insert code here to do something with the results

}

等化为SQL语句:

SELECT_ID, word, localeFROM wordsWHERE word =ORDER BY word ASC;
.query()projectioncontent_uriselectionselectionArgsorderby

避免注入,推荐在selection使用?来代替。

// Constructs a selection clause with a replaceable parameter
String mSelectionClause =  "var = ?";
// Sets the selection argument to the user's input
selectionArgs[0] = mUserInput;

CAUTION:若要使用Cursor备份一个ListView,该Cursor必须包含_ID列,即使ListView并没有显示_ID列。
To back a ListView with a Cursor, the cursor must contain a column named _ID.

从Cursor中获取数据
newWord = mCursor.getString(index);
或者是其他的getInteger等等类型。

Content Provider Permissions

指定权限以便于控制数据的访问。
另外,若是一个Provider什么权限都不指定,则其它应用将不可访问,但是该Provider所在的应用具有完全的访问读写能力。

读使能权限
android.permission.READ_USER_DICTIONARY
插入删除更新使能权限
android.permission.WRITE_USER_DICTIONARY

Inserting, Updating, and Deleting Data

插入数据,调用ContentResolver.insert()
使用ContentValues 封装KV值,调用插入方法即可。

// Defines a new Uri object that receives the result of the insertion
Uri mNewUri;

...

// Defines an object to contain the new values to insert
ContentValues mNewValues = new ContentValues();

/*
 * Sets the values of each column and inserts the word. The arguments to the "put"
 * method are "column name" and "value"
 */
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,   // the user dictionary content URI
    mNewValues                          // the values to insert
);

更新
需要query的selectionClause, selectionArgs 和 insert的content values.

// Defines an object to contain the updated values
ContentValues mUpdateValues = new ContentValues();

// Defines selection criteria for the rows you want to update
String mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";
String[] mSelectionArgs = {"en_%"};

// Defines a variable to contain the number of updated rows
int mRowsUpdated = 0;

...

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

mRowsUpdated = getContentResolver().update(
    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    mUpdateValues                       // the columns to update
    mSelectionClause                    // the column to select on
    mSelectionArgs                      // the value to compare to
);

删除
需要SelectionClause, selectionArgs, 方法返回被删除的行数。

// Defines selection criteria for the rows you want to delete
String mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";
String[] mSelectionArgs = {"user"};

// Defines a variable to contain the number of rows deleted
int mRowsDeleted = 0;

...

// Deletes the words that match the selection criteria
mRowsDeleted = getContentResolver().delete(
    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI
    mSelectionClause                    // the column to select on
    mSelectionArgs                      // the value to compare to
);

Provider Data Types

  • String
  • integer
  • long integer (long)
  • floating point
  • long floating point (double)
  • BLOB, Binary Large OBject implemented as a 64KB byte array.

Provide也同样支持MIME类型,支持使用MIME指定一个类的格式,
MIME data type information for each content URI they define.

Alternative Forms of Provider Access

  • 批访问Batch Access: You can create a batch of access calls with methods in the ContentProviderOperation class, and then apply them with ContentResolver.applyBatch().

  • Asynchronous queries: 使用CursorLoader进行异步查询。

  • Data access via intents: 通过Intents访问数据,使用Intent发送请求到Provider所在Application,然后Application解析intent,返回相应的数据。best-equipped to modify the provider’s data.

    1. Batch Access
      create ContentProviderOperation 类的数组,然后调用 ContentResolver.applyBatch()。只需要传递authority,不需要特定的path.

      这允许数组中的每个ContentProviderOperation对象都能够工作在各自的table,并且Content.applyBatch()将返回一个结果数组。
      This allows each ContentProviderOperation object in the array to work against a different table. A call to ContentResolver.applyBatch() returns an array of results.

    2. Asynchrounous queries
      使用方法如CursorLoader中的Example.

    3. Data Access via intents

  • 从具有权限的应用获得result intent back
  • 激活一个具有权限的应用,并且用户操作它。

临时权限访问和写入
Read permission: FLAG_GRANT_READ_URI_PERMISSION
Write permission: FLAG_GRANT_WRITE_URI_PERMISSION

Provider在< provider>元素下定义android:grantUriPermission属性,以及 < grant-uri-permission> 子元素。

  • 发送ACTION_PICK并制定MIME type, 然后发送Intent。Your application sends an intent containing the action ACTION_PICK and the “contacts” MIME type CONTENT_ITEM_TYPE, using the method startActivityForResult().
  • Intent被匹配。Because this intent matches the intent filter for the People app’s “selection” activity, the activity will come to the foreground.
  • 返回结果并给予临时读取权限。In the selection activity, the user selects a contact to update. When this happens, the selection activity calls setResult(resultcode, intent) to set up a intent to give back to your application. The intent contains the content URI of the contact the user selected, and the “extras” flags FLAG_GRANT_READ_URI_PERMISSION. These flags grant URI permission to your app to read data for the contact pointed to by the content URI. The selection activity then calls finish() to return control to your application.
  • 调用onActivtyResult()。Your activity returns to the foreground, and the system calls your activity’s onActivityResult() method. This method receives the result intent created by the selection activity in the People app.
  • With the content URI from the result intent, you can read the contact’s data from the Contacts Provider, even though you didn’t request permanent read access permission to the provider in your manifest. You can then get the contact’s birthday information or his or her email address and then send the e-greeting.

使用另一个具有权限的应用
例如:Calendar应用允许其他应用发送ACTION_INSERT,启动Calendar的插入UI.
the Calendar application accepts an ACTION_INSERT intent, which allows you to activate the application’s insert UI.

Contract Classes

设定变量帮助应用与ContentProvider的content URIs, column names, intent actions, and other features 更好的交互。一般在android.provider下。

UserDictionary.Words.CONTENT_URI

MIME Type Reference

用法 type/subtype, 常见的有text/html
自定义的MIME类型,vendor-specific 具有更复杂的类型和子类型。
多行
vnd.android.cursor.dir
单行
vnd.android.cursor.item

再比如:子类型的值
vnd.android.cursor.item/phone_v2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值