Content Provider Basics

来源:互联网 发布:windows最佳分辨率 编辑:程序博客网 时间:2024/06/05 18:54

内容提供器管理是通过一个结构化的数据集合。他们封装数据,并且提供一个机制来定义数据安全。内容提供器标准一个接口,将一个进程中的数据运行在另外一个进程中。

         当你想获取某些数据通过内容提供者,你可以用ContentResolver对象在你应用Context中来和提供者进行交互。这个ContentResolver对象和提供者对象进行交流。这个提供者接受从各个客户端来的请求数据,执行请求动作,并且返回结果。

         你不需要开发你自己的提供者假如你不打算分享你的数据给另外的应用。但是你需要你自己的提供者来提供定制的搜索建议在你的应用中。你也需要你自己的提供者假如你想复制并粘贴复杂的数据或者文件从你的应用到另外的应用。

         Android自己包含了内容提供者管理数据,比如音乐,视频,图片,个人联系人信息。你可以看到他们列举在android的文档中。这些限制,这些提供者可以被访问让任何的android应用。

接下来详细介绍一下内容提供者:

Content Provider Basics:怎么样在内容提供者去访问被组织在表里的数据。

创建一个内容提供者:怎么样创建一个自己的内容提供者。

日历提供者:怎么样访问日历提供者android的一部分。

联系人提供者:怎么样访问一个联系人提供者。

 

内容提供者基础

一个内容提供者通过一个数据仓库。一个提供者是anroid应用的一部分,经常提供他自己的UI线程工作。但是内容提供者的最主要用途是提供给另外的应用程序使用的,通过提供者提供一个客户端对象。同时提供者和提供的客户端使用相同的标准的接口用于进程间交互数据和数据的安全。

这个主要描述在下面:

1.      内容提供者怎么样工作。

2.      使用API检索数据从内容提供者。

3.      使用API你插入,修改,删除数据在内容提供者。

4.      另外的API特性使更好的使用提供者。

 

概括

一个内容提供者显示数据给外部的数据有一个或多个表这些都可以在表中找到在一个相关的数据库里。一行代表一个实例多少个数据提供者,并且每列在行里代表一个单独的实例。

比如:一个已经创建完的提供者在andorid平台上是用户目录,这个存储在非标准的拼写中用户需要保存。表1说明了数据看起来在这样的提供者表中:

Table 1: Sample userdictionary table.

word

app id

frequency

locale

_ID

mapreduce

user1

100

en_US

1

precompiler

user14

200

fr_FR

2

applet

user2

225

fr_CA

3

const

user1

255

pt_BR

4

int

user5

100

en_UK

5

表1,每行代表一个单词实例这个也行不能被找到在标准的字典。没列代表单词数据,比如像本地曾经遇到过。这个列的都是列的名字存储的提供者的名字。你指代一个语言栏。对于这个提供者_ID列作为一个关键字列提供者自动分配。

 

注意:一个提供者不需要有一个主键,并且不需要使用_ID列作为主键。但是你想要绑定数据从内容提供者到一个ListView,这个就是一列名字叫_ID。这个需要查看更多在展示部分。

 

访问提供者

一个应用访问数据从一个内容提供者通过一个ContentResolver客户端对象。这个对象有调用那些名字和提供者相同的的方法,一个实例创建继承ContentProvider。这个ContentResolver方法提供基本的“CRUD”持久存储。

这个ContentResolver对象在客户端应用的进程中并且ContentProvider对象在应用中拥有提供者自动会执行进程间通信。ContentProvider也扮演的一个抽象层的概念。在存储的数据和外观数据表。

注意:去访问一个提供者,你的应用通常有请求权限在它的清单文件中。这个描述更多在下面Content Provider Permissions部分中。

比如,获取一个单词的列表和他们本地的用户提供者,你可以调用ContentResolver.query(),query方法调用ContentProvider.query()方法定义在字典提供者。下面是展示一个ContentResolver.query()调用

// Queries theuser 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,                    // Selectioncriteria
    mSortOrder);                       // The sort order for the returned rows

表2展示了query参数的意义(略)

 

内容URIs

一个内容的URI是一个URI指向数据在一个提供者中。内容URI包含了一个内容提供者象征性的名字(这个是它的证书)并这个名字指向一个表(一个路径)。当你调用一个客户端的方法通过一个表用一个提供者,这个内容URI这个表也是一个参数。

在之前的代码中,这个参数CONTENT_URI包含了用户字典单词表的内容URI。这个ContentResolver对象解析这个URI的职责,并使用它去比较系统中已知的职责表在搜索这个提供者时。这个ContentResolver可以查询参数正确的提供者。

这个Content使用内容的URI路径部分去选择一个表。一个提供者通常有个路径对于表暴露。

在前面的代码可以用完整的URI表示只个“words”表

content://user_dictionary/words

 当这个user_dictionary字符串在提供者职责,并且words字符串是表的路径。这个字符串content://(scheme)总是存在的,并且指向一个内容的URI。

 

许多提供者允许你通过单行在表中根据一个ID值在URI的末尾。比如,去查询一行_ID是4在你的字典里,你可以用内容URI:

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

你经常使用id的值是查询一行的数据或者希望更新或删除他们中的一个。

注意:Uri和Uri。Builder类都包含了很方便的方法构建结构很好的Uri对象字符串。这个ContentUris包含了构建方法为增加一个id值在一个URI里。这个前面片段里使用了weithAppendedId()来增加一个id给UserDictionary内容URI。

 

查询数据从提供者

这部分主要描述这样查询数据从提供者,使用这个UserDictionary提供者作为例子。

 

为了清晰,代码片段部分在选择调用Contentesolver.query()在UI线程中。这个具体的代码,但是你应该查询在不同的线程异步。一个方式时你是用CursorLoader类。接下来的片段;他们不能展示一个完整的应用。

为了查询数据从一个提供者,有接下来几步:

1.      向提供者请求读取权限。

2.      定义代码发送一个查询给提供者。

 

请求读取权限

查询数据从提供者,你的应用需要提供者的“读取权限”。你不能请求这个权限在运行时,你要指定你的权限在你的清单中,使用<user-permission>标签和额外的提供者定义的权限。当你指定元素在你的清单中,你影响“请求”的权限在你的应用中。当你安装应用时,他们会授予你请求。

这个额外的读取权限对这个你使用的提供者,以及其他名字为另外的权限为这个提供者,看提供者的文档。

这个权限的角色在访问提供者更多的描述在ContentProvidrer Permissions部分中。

User DictionaryProvider 定义权限android.prermission.READ_USER_DICTIONARY在清单文件中,所以一个应用希望从提供者读取数据必须请求这个权限。

 

构建一个查询

这个下一步在查询数据需要构造一个查询。这个首先是一个片段访问User Dictionary Provider。

// A "projection" defines the columns that will be returned for each rowString[] 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 clauseString mSelectionClause = null;// Initializes an array to contain selection argumentsString[] mSelectionArgs = {""};

这个下一个片段展示了使用ContentResolver.query(),使用UserDictionary Provider 举个例子。一个提供客户端查询和SQL 查询一样并且它也包含了一组返回集合,一组选择条件和一个排序。

这组集合是显现返回叫做投射(可见mProjection)

这个表达式指定查询行分为选择条件和选择参数。这个选择条件是一个逻辑组合和布尔表达式,行的名字,和其他的值。假如你指定可替换参数为“?”替换一个值,这个查询方法查询这个值从选择参数数组。

这个接下来的片段,假如用户不输入一个单词,这个选择条件设置为null,并且查询返回所有的单词。假如用户输入一个单词,这个选择条件就变为UserDictionary.Words.WORD +”?”并且第一个元素选择参数数组在设置这个用户输入的词

 

/* * This defines a one-element String array to contain the selection argument. */String[] mSelectionArgs = {""};// Gets a word from the UImSearchString = mSearchWord.getText().toString();// Remember to insert code here to check for invalid or malicious input.// If the word is the empty string, gets everythingif (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 objectmCursor = 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 exceptionif (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, locale FROM words WHERE word = <userinput> ORDER BY word ASC;

在这个SQL表达式中,这个实际的列名被常量代替。

 

防止恶意输入

假如被内容提供者管理的数据在一个SQL数据库中,包含外部不可信任的数据在原始的Sql语句可以导致sql注入。

考虑下么的选择条件:

// Constructs a selection clause by concatenating the user's input to the column nameString mSelectionClause =  "var = " + mUserInput;

假如你做这些,你被允许用户连接一个恶意的SQL在你的SQL 语句中。比如这个用户可以输入“noting;DROP TABLE *;”为mUserInput,这个的结果是在选择条件中变为var=nothing; DROP TABLE *因此选择条件被当做一个可信任的SQL语句,这个可以引起这个提供者擦除所有的表在这个SQLite 库里(除非这个提供这设置可以抓取SQL注入)。

为了避免这个问题你用户使用一个条件用“?”代替参数并且分离数组的选择参数。当你

做完这些后,用可以输入在一个直接限制去查询而不是解释一部分语句。因为像SQL不是被信任,这个用户不能注入恶意的SQL。而不是使用连接的的方式包含用户输入,使用选择条件

// Constructs a selection clause with a replaceable parameterString mSelectionClause =  "var = ?";

设置选择数组参数像

// Defines an array to contain the selection argumentsString[] selectionArgs = {""};

输入一个选择参数组的值像这样:

// Sets the selection argument to the user's inputselectionArgs[0] = mUserInput;

一个使用“?”选择条件是一个可以代替参数并且一个选择参数组被优先知道一个选择,即使提供不是基于一个SQL数据库。

 

显示查询结果

这个ContenResolver.query()客户端方法总是会返回一个Cursor包含了这个行知道查询的映射为每一列并且所有的它包括的行。用Cursor方法,你可以重复的列的在返回结果里,确定每一行的数据类型。获取数据从行中,并且结果的另外的属性。一些Cursor实现了自动更新对象当提供者数据改变时,或触发一个观察者对象当Cursor改变时。

注意:一个提供者约束访问行基于查询对象的性质。比如,联系人提供者约束了一些行同步适配器,所以他不能返回使他们给一个activity或service。

 

假如没有列符合选择的条件,这个内容提供者就会返回一个Cursor对象提供Cursor.getCount()是0

 

         假如发送了一个内部错误,这个查询的结果可以依赖特别的提供者。这个可以选择返回null或者可以抛出异常。

 

         由于一个Cursor是一个list的列,一个很好的方式是显示内容用一个Cursor链接到一个ListView上通过SimpleCursorAdapter。

         接下来是一个片段接上面的片段。它创建了一个简单的SimpleCursorAdapter对象包含了Cursor恢复被查询,并且设置这些对象到一个适配器为一个ListView

// Defines a list of columns to retrieve from the Cursor and load into an output rowString[] mWordListColumns ={    UserDictionary.Words.WORD,   // Contract class constant containing the word column name    UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name};// Defines a list of View IDs that will receive the Cursor columns for each rowint[] mWordListItems = { R.id.dictWord, R.id.locale};// Creates a new SimpleCursorAdaptermCursorAdapter = new SimpleCursorAdapter(    getApplicationContext(),               // The application's Context object    R.layout.wordlistrow,                  // A layout in XML for one row in the ListView    mCursor,                               // The result from the query    mWordListColumns,                      // A string array of column names in the cursor    mWordListItems,                        // An integer array of view IDs in the row layout    0);                                    // Flags (usually none are needed)// Sets the adapter for the ListViewmWordList.setAdapter(mCursorAdapter);

注意:这个返回一个Listview带着一个Cursor,这个cursor必须包含列名_ID。正因为如此在查询是检索_ID栏的单词即表示视图不显示它。这种限制也解释为啥大多数提供者都会有_ID列在各自的表里。

 

获取数据从查询结果中

即使简单的展示查询结果,你可以使用它们为另外的任务。比如,你可以检索拼写从你的字典中并且查询它们在另外的提供者。做这些你需要让变数递归取得行在Cursor:

// Determine the column index of the column named "word"int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);/* * Only executes if the cursor is valid. The User Dictionary Provider returns null if * an internal error occurs. Other providers may throw an Exception instead of returning null. */if (mCursor != null) {    /*     * Moves to the next row in the cursor. Before the first movement in the cursor, the     * "row pointer" is -1, and if you try to retrieve data at that position you will get an     * exception.     */    while (mCursor.moveToNext()) {        // Gets the value from the column.        newWord = mCursor.getString(index);        // Insert code here to process the retrieved word.        ...        // end of while loop    }} else {    // Insert code here to report an error if the cursor is null or the provider threw an exception.}

Cursor实现了包含以下get的方法检索不同的类型数据从对象中。比如,前一个片段使用getString()。这个有一个getType()的方法返回一个值指向数据类型列。

 

内容提供者权限

         一个提供者应用可以知道权限另外的应用必须有为了可以访问提供者的数据。这些权限确定这些使用者知道什么数据应用会去访问。基于这个提供者的请求,另外的应用请求这个权限他们必须为了获得提供者。最后使用者可以看到请求的权限在安装应用的时候。

 

假如一个提供者应用没有指定任何和权限,其他的应用不能访问这个提供者的数据。但是,组件在提供者应用总是可以完全的读或写访问,无论是否指定指定权限。

         正如上述,UserDictionary Provider 需要android.permission.READ_USER_DICTIONARY权限为检索他的数据,这个提供者需要分离开andorid.permission.WRITE_USER_DICTIONARY权限做插入,更新,删除数据。

为了获取权限访问提供者,一个应用需要配置<uses-permission>元素在清单列表中。当andorid的包管理器安装应用,一个用户必须批准所有的权限应用需要的。假如用户批准他们,包管理器才会继续安装。假如用户不能显示他们,包安装将会被终止。

 

插入,修改,删除数据

在同一时间你可以检索数据从一个提供者,你也交互在提供客户端和提供ContenProvider去修改数据。你可以调用一个ContentResolver方法这个会解析为一个相同的方法ContentProvider。这个提供者和提供的客户端自动安全执行并进行进程间的通讯。

插入数据

插入数据在提供者,你可以调用ContentResolver.insert()方法。这个方法插入一个新的一行在提供者并且返回者行的内容URI。这个是一个片段展示怎么样插入一个新单词在UserDictionary Provider:

// Defines a new Uri object that receives the result of the insertionUri mNewUri;...// Defines an object to contain the new values to insertContentValues 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);

数据为一个新的行一个单独的ContentValues对象,这个类似于一个光标。这列在这个对象中不需要有相同的数据类型,并且假如你不想指定一个值,你可以设置一个null用ContentValues.putNull()。

这个片段不会增加这个_ID列,因为这列是自动提供的。这个提供者分配一个唯一的值_ID给增加的每一行。提供的通常用这些值作为主键。

这个内容URI返回给newUri指定新的增加行,以下面的格式

content://user_dictionary/words/<id_value>

这个<id_value>是内容的_ID为新的一行。很多的提供者可以自动的发现这种形式的内容URI并且执行一个请求操作在一个特定的行。

为了获取_ID的值从Uri中,调用ContentUris.parseId()。

 

更新数据

         更新一行,你使用ContentValues对象带着更新的值你需要插入的并且选择条件在你做查询的时候。这个客户端方法你使用ContentResolver.update()。你只需要增加值在ContentValues对象中为更新的列你需要更新的。假如你想清空一列的内容可以设置null。

         下面的片段替换所有的行区域语言是“en“的设置为null。这个返回一个修改的行数。

ContentValues mUpdateValues = new ContentValues();// Defines selection criteria for the rows you want to updateString mSelectionClause = UserDictionary.Words.LOCALE +  "LIKE ?";String[] mSelectionArgs = {"en_%"};// Defines a variable to contain the number of updated rowsint 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);

你应该用户输入当你调用ContentResolver.update()。这个学习更多在Protecting against malicious input.部分。

 

删除数据

删除一行像检索一行数据。你指定选择条件为一行你想删除的饼客户端方法返回删除的行数。下面的片段删除appid符合”user”。这个方法返回一个删除的行数。

// Defines selection criteria for the rows you want to deleteString mSelectionClause = UserDictionary.Words.APP_ID + " LIKE ?";String[] mSelectionArgs = {"user"};// Defines a variable to contain the number of rows deletedint mRowsDeleted = 0;...// Deletes the words that match the selection criteriamRowsDeleted = getContentResolver().delete(    UserDictionary.Words.CONTENT_URI,   // the user dictionary content URI    mSelectionClause                    // the column to select on    mSelectionArgs                      // the value to compare to);

你应该整理用户输入的当调用ContentResolver.delete()。学习更多关于这些,请阅读Protecting against malicious input.部分

 

提供数据类型

         内容提供可以影响不同的数据类型。User Dictionary Provider 提供只有文本,但是提供者也可以提供这些格式:

1.      integer

2.      long integer

3.      floating point

4.      long floating point

另外的数据类型提供者也经常使用是binary Large Object(blob)实现了一个64kb的数据。你可以看可用的数据类型查询Cursor类的get方法。

 

         这个数据每列的类型通常是列举在他的文档中。你可以用MIME类型的信息去寻找,假如你的应用执行数据这些提供者提供的,或者选择一个类型的复杂的数据结构或文件。比如,ContactsContract.Data表在联系人使用MIME类型的标签联系人数据类型存储在每一行里。为了可以获取MIME类型一致的内容RUI,可以调用ContentResolver.getType()。

 

选择提供者访问方式

三种选择方式提供方法在应用开发的时候:

1.      成批访问方式:你可以创建一个成批访问调用方法在ContentProviderOperation类,和使用它们用ContentResolver.applyBatch()。

2.      异步查询:你可以查询在一个单独的线程中。一个方式可以做这个用CursorLoader对象。这个在Loaders里有指南怎么做。

3.      数据通道意图:虽然你不能发送一个意图直接给提供者,你可以发送一个意图给提供者的应用,这个通常最好的方法修改数据。

批量访问和修改通道意图在解析来部分描述:

1.批量访问

         批量访问很有用对一个提供者插入大量的行,或者插入行在多个表中在同一个方法,或者一个执行一个操作在各个进程中当做一个事务。

访问提供者用“批量访问模式“,你可以创建一个ContentProviderOperation对象数组然后发送他们给内容提供者用ContentResolver.applyBatch()。你可以通过内容提供者证书使用这些方法,而不是一个特定的内容URI。这个允许每个ContentProviderOperation对象在这个数组里为一个不同的表工作。一个调用ContentResolver.applyBatch()返回一个数组结果。

ContactsContract.RawContacts合约类包含了代码片段演示了批量插入。这个Contact 管理简单的应用包含了一个批量访问在ContactAdder.java源文件中。

 

数据访问通道意图

意图可以直接访问提供一个内容提供者。你可以允许用户访问数据在一个提供者即使你的应用没有这个权限,或者获取一个结果意图从有权限的应用中,激活一个有权限的应用让用户可以在里面工作。

获取暂时的访问权限

你可以访问数据通过一个内容提供者,即使你不能完全获取访问的权限,用发送一个意图给一个应用这个有权限并且可以返回几个结果包含一个意图“URI“权限。这些权限可以指定内容的URI直到一个activity接收他们结束。这个应用可以拥有暂时的权限设置这个标志。

·        Read permission: FLAG_GRANT_READ_URI_PERMISSION

·        Write permission: FLAG_GRANT_WRITE_URI_PERMISSION

注意:这些标志不能给于一般的读写访问提供者的证书被包含在内容URI里。这个访问只能通过URI自己。

 

一个提供者定义一个URI权限为内容URI在清单文件中,使用android:grantUriPermission属性在<provider>元素,作为一个<grant-uri-permission>的子元素在<provider>元素中。这个URI权限机制被解释更多在Security and Permissions的URI Permissions中。

 

         比如,你可以检索数据在联系人在联系人提供者中,即使你没有READ_CONTACTS权限。你也许希望做一个应用发送一个问候给一个联系人当他生日的时候。不是请求READ_CONTACTS权限当你访问所有的用户联系人并说有的信息,你执行的是让用户操作被使用的联系人用你的应用。做这些使用下面的过程:

1.      你的应用发送一个意图包含了一个动作ACTION_PICK并用联系人类型CONTENT_ITEM_TYPE,使用这个方法startActivityRorResult()。

2.      因为这个意图会匹配过滤器过滤出来的联系人应用到前端。

3.      在选择了一个activity后,用户可以选择更新。当这个完成后,这个选择的activity调用setResult(resultcode,intent)设置一个返回的意图给你的应用。这个意图里包含了你选择的联系人内容URI,并且附加标志FLAG_GRANT_READ_URI_PERMISSION.这个标志同意URI权限给你的应用去读取数据接触的联系人内容URI.这个选择的activity然后调用finish()返回给你的应用。

4.      你的activity返回到前端,并且系统会调用你的onActivityResult()方法。这个方法会接收结果被选择的联系人应用创建的意图。

5.      用这个返回回来的内容URI,你可以读取联系人数据从联系人提供者,即使你没有请求永久的权限在你的清单中。你可以获取联系人生日信息,或者是他的邮件地址并发送祝福。

 

使用另外一个应用

一个简单的方式允许你的修改数据你不想通过访问权限去激活一个有访问权限的应摒弃让用户工作在那里。

比如,日历应用接收ACTION_INSERT意图,这个允许你激活这个应用插入UI。你可以通过外部数据用这个意图,这个应用使用预先的UI。因为循环事件有一个复杂的语法,执行插入一个事件到日程表提供者中去激活日程表应用用ACTION_INSERT并且可以让使用者插入事件在那里。

 

联系人类

一个联系人类定义了常量帮助应用使用内容URI,列名,意图动作,另外的内容特性。联系人不自动包含一个提供者;这个提供者开发者定义了它们并使他们可以使用给另外的开发者。许多提供者包含了android平台有一致的联系人类在android.provider。

比如,User Dictionary Provider有一个UserDictionary 包含了内容URI并且列名常量。这个内容URI用于单词的表定义在常量UserDictationary.Words.CONTENT_URI。里这个UserDictionary.Words类也包含了列名,这个是用来举个例子片段,在指导中。比如,查询映射被定义为

String[] mProjection ={    UserDictionary.Words._ID,    UserDictionary.Words.WORD,    UserDictionary.Words.LOCALE};

另外的联系人类是ContactsContract用于联系人提供者,这个参考文档为这个类包含了示例代码段。一个他们的子类是ContactsContract.Intents.Insert,是联系人类包含插入常量并且是意图数据。

 

Mini类型引用

内容提供者可以返回一个标准的mime媒体类型,或者自定义的MIME类型字符串。

MIME类型格式

type/subtype

比如,这个熟知的MIME类型text/html是一个text类型并且是html子类型。假如你提供者防护这个URI,这个意思一个查询URI将返回一个text包含HTML标签。

 

普通的MIME类型字符串,也叫“vendor-specific “MIME类型,有更复杂类型和子类型。这个类型的值总是

vnd.android.cursor.dir

对多行:

vnd.android.cursor.item

对单行,

这个子类型。Android内置提供通常有一个简单的子类型。比如,当联系人应用创建一个电话号的行,者设置成下面的MIME类型在行中:

vnd.android.cursor.item/phone_v2

注意这些子类型的值是一个简单的phone_v2。

另外的提供者开发者创建自己的子类模板在基于提供者证书并且表名。比如,可以用一个提供者包含火车时间表。这个提供者证书是com.example.trains.并且包含表Line1,Line2,Line3。响应一个内容URI:

content://com.example.trains/Line1

为表Line1,这个提供者返回MIME类型

vnd.android.cursor.dir/vnd.example.line1

返回的内容URI

content://com.example.trains/Line2/5

从第5行在表中提供者返回的MIME类型

vnd.android.cursor.item/vnd.example.line2

更多的内容提供者定义在常量类中为MIME类型中。这个提供者相关的类ContactsContract.RawContacts。比如定义常量CONTENT_ITEM_TYPE为MIME类型为单行联系人。

原创粉丝点击