安卓:内容提供者(基础知识)

来源:互联网 发布:python输入ctrl c 编辑:程序博客网 时间:2024/05/22 15:49

Content Provider Basics

内容提供者基础知识

内容提供者(content provider)管理对于数据中央资料库的访问。提供者是安卓程序的一部分,它常常提供用于数据工作的用户接口。然而内容提供者的最主要作用还是给访问该提供者的其他应用程序使用。这些应用程序通过使用一个提供者客户端,从而访问内容提供者。总的来说,提供者和提供者客户端(这也是随后要介绍的content receiver)为数据提供了一种一致的,标准的接口。它也可以用于进程间通信和数据安全。

我们从下面几个方面进行描述:

  • 内容提供者如何工作
  • 如何使用API从内容提供者检索数据
  • 如何插入,更新,删除内容提供者的数据
  • 有利于你的工作的内容提供者其他特性

 预览 Overview

内容提供者以表的形式向外部程序展示数据,这和关系型数据库很相似。一行代表了提供者的数据类型的集合的一个实例,行中的每一列代表了一个数据。

举个例子,一种嵌入式的安卓平台的提供者是用户字典,它存储了一些用户想要保留的非标准单词。表1阐述了提供者的表大致的样子

表1:用户字典的例子

wordapp idfrequencylocale_IDmapreduceuser1100en_US1precompileruser14200fr_FR2appletuser2225fr_CA3constuser1255pt_BR4intuser5100en_UK5

在表1中,每一行代表了一个可能无法在标准字典中找到的实例。每一列代表了那个单词的一些数据,例如位置(国籍)信息。存储了列的名字的头部存储于提供者中。为了引用一行中的locale,你引用到的是它的列目locale。对于这个提供者而言,_ID列是主键,提供者可以自动维护。

注意:提供者不要求拥有主键,在存在其他主键时,也没有要求使用_ID 作为主键。然而,如果你想要从提供者 到ListView 中绑定数据,一列必须是 。你可以在 (Displaying query results)章节找到更多细节。


访问提供者   Accessing a provider 

从内容提供者中访问应用程序的数据是通过内容接收者这一客户端对象来完成的。这个对象拥有调用提供者对象的同名方法,这个实例实际上是提供者的子类对象。内容接收者提供了基本的CRUD(创建,检索,更新,删除)的持久化存储功能。

处于客户端应用程序进程的内容接收者 和 拥有提供者对象应用程序 能够自动处理进程间通信。内容提供者也可以做为处于数据存储和外部数据体现(例如表哥)中的抽象层。

注意:为了访问内容提供者,你需要在manifest文件中加入请求特定的权限。这个在内容提供者权限(Content Provider Permissions)一节可以看到。

举个例子,获得用户字典提供者的单词的清单和他们的位置,你可以调用ContentResolver.query() 。查询方法query()将会调用定义在用户字典内容提供者中的查询方法。接下来的代码展示了调用:ContentProvider.query()。接下来的代码展示了ContentResolver.query() 调用:


// 查询用户字典并返回结果mCursor = getContentResolver().query(    UserDictionary.Words.CONTENT_URI,   // words表的content URI    mProjection,                        // 返回的列    mSelectionClause                    // 选择标准    mSelectionArgs,                     // 选择标准参数    mSortOrder);                        // 返回行的排序


表2显示了query(Uri,projection,selection,selectionArgs,sortOrder)参数如何匹配SQL选择语句

Table 2: Query() compared to SQL query.

query() argumentSELECT keyword/parameterNotesUriFROM table_nameUri 映射到表名指定的提供者的一个表。projectioncol,col,col,...投射 是一簇列的集合,标示那些需要表示在检索结果中的列selectionWHERE col = value选择 指定了行选择的标准selectionArgs没有确切的等式。使用?来进行替换对应参数 sortOrderORDER BYcol,col,...sortOrder 指定了结果集中显示的排列顺序

内容统一资源标识符 Content URIs

content URI 是一种提供者标识数据。Content URIs 包括完整的提供者象征名称(官方的)以及一个指向表的名称(路径)。当你调用客户端方法来访问提供者的一个表,content URI也是表的一个参数。

在之前的代码行中,常量CONTENT_URI包含了用户字典的 “words”表。ContentResolver内容接收者对象分析URI的全文行,并用他来“解决”提供者与已知的系统提供者的比较。内容接收者能够船体query参数给正确的提供者。

内容提供者使用content URI的路径部分来选择访问的表。一个提供者对于其每一个暴露的表都有一个路径。

在之前的代码行中,关于 "words"表的URI完整形式如下:

content://user_dictionary/words

这儿,user_dictionary代表的是提供者的名称,而字符串words代表路径名,字符串content://总是存在的,它是content URI的标志符。

通过在URI最后附加ID值,许多的提供者允许你访问表中的单一行。举个例子,为了检索用户字典中_ID是4的行,你可以如下使用内容URI:

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

当你想要更新或者删除你所检索的行的集合中的一个时,你会经常使用id值。

注意: Uri和Uri.Builder 类中包含了从字符串构建较好形式Uri对象的便捷方法。

Retrieving Data from the Provider 从提供者检索数据


这一节表述了如何从提供者中检索数据,使用用户字典提供者作为例子。

为了清晰明了,代码片段在UI线程中调用了ContentResolver.query() 。在实际代码中,你应该在一步的线程中进行查询。一种办法是使用CursorLoader 类,在Loaders 向导中描述了其更多的细节。此外,这仅仅是片段,而不是完整的应用程序。

为了检索提供者中的数据,你应该完成这写基本步骤:

1. 请求读取提供者 的权限。
2. 定义向提供者发送query的代码。

Requesting read access permission 请求读取访问权限

为了从提供者检索数据,你的应用程序需要“读取访问权限”。你无法在运行时申请全想,所以,你需要在你的 manifest中指定你需要的权限,使用 元素来指定。当你在manifest中指定这样的元素,你就处于请求允许,所以用户安装你的应用程序,他们也默认了你的请求。

为了找到你的提供者读取确切的数据,你应该在提供者开发文档中寻找。

许可所扮演的角色,请看Content Provider Permissions.章节

用户字典在manifest文件中定义了许可android.permission.READ_USER_DICTIONARY。想要自提供者中读取的,必须请求这个许可。

Constructing the query 结构化查询

下面的检索数据步骤是结构化查询。第一个片段定义了一些访问用户字典提供者的变量:

// "投射" 定义了结果中返回的列String[] mProjection ={    UserDictionary.Words._ID,        UserDictionary.Words.WORD,       UserDictionary.Words.LOCALE  };// 定义一个包含选择语句的字符串String mSelectionClause = null;// 初始化一个包含选择参数的数组String[] mSelectionArgs = {""};

第二个片段显示了如何使用ContentResolver.query(),使用用户字典提供者作为例子。一个提供者客户端查询与SQL查询很相似,它包含了一组列的返回,选择规矩以及排序规则。

查询应该返回的列集合被称作投射(变量mProjection

指定行检索的表达式分为选择语句选择参数。选择语句由逻辑布尔表达式列名以及(变量mSelectionClause)结合而成。如果你指定了替代参数?来代替一个值,查询方法将从选择参数数组中查找一个值来代替他(变量mSelectionArgs)。

在下一个片段中,如果用户不输入一个词,选择语句将设为null,并且返回所有的单词。如果用户输入一个单词,选择语句将被设为 UserDictionary.Words.WORD + " = ?" 并且 选择参数数组中的第一个元素。

/* * 这定义了包含选择参数的单一元素字符串 */String[] mSelectionArgs = {""};// 从UI获取单词mSearchString = mSearchWord.getText().toString();// 不要忘了在这里加入检查代码和防止SQL恶意注入的代码// 如果单词是空的,则获得所有的if (TextUtils.isEmpty(mSearchString)) {    // 选择语句设为空将返回所有单词    mSelectionClause = null;    mSelectionArgs[0] = "";} else {    // 匹配用户输入的单词的结构化选择语句    mSelectionClause = UserDictionary.Words.WORD + " = ?";    // 将用户输入的参数移入选择参数之中    mSelectionArgs[0] = mSearchString;}// 查询表并返回 一个 Cursor 对象mCursor = getContentResolver().query(    UserDictionary.Words.CONTENT_URI,  // 单词表的 content URI     mProjection,                       // The columns to return for each row    mSelectionClause                   // null, 或者用户输入的内容    mSelectionArgs,                    // empty, 或者用户输入的字符串    mSortOrder);                       // 返回结果的排序// 有些提供者在出现错误时会返回空,或者抛出异常if (null == mCursor) {    /*     * 在这插入代码来处理错误。千万不要使用cursor。你可能想使用android.util.Log.e()来打印错误     *     */// 如果Cursor 内容为空, 提供者将找不到匹配项} else if (mCursor.getCount() < 1) {    /*     * 在这插入代码来告知用户查询没有成功。这不是必须的。你可能希望用户插入新行或者重新输入。     */} else {    // 在这插入代码来对结果做一些操作}

这可以与如下的SQL语句进行类比:

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

在这个SQL语句中,实际的列名(_ID, word, locale)被使用以代替简写的类常量

Protecting against malicious input 防止恶意输入

如果内容提供者管理的数据是SQL数据库,在SQL语句中包含外部不可信数据可导致SQL注入。

考虑下面的选择语句:

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

如果你这么做了,你就允许了用户把有害的SQL注入到你的SQL语句中。举个例子,如果用户输入"nothing; DROP TABLE *;" 结果就是var = nothing; DROP TABLE *;由于选择语句被当作SQL语句对待,这可能导致提供者擦除SQLite所有的表(除非提供者建立了防止SQL注入的catch尝试)

为了避免这个问题,使用 来代替参数以及隔离选择数组中的参数.当你这样使用时,用户输入将受到限制,而不是被翻译成SQL语句的一部分。由于不被当作SQL语句,用户输入无法注入恶心SQL。不应该使用直接连接字符串方式,而使用下面的选择语句:

// 使用可替换参数构造结构化查询String mSelectionClause =  "var = ?";

像下面这样建立选择语句数组:

// 定义包含查询参数的数组String[] selectionArgs = {""};

像下面这种方式来输入值:

// 将用户输入设为查询选择语句的参数selectionArgs[0] = mUserInput;

选择语句使用? 作为可替换参数,并且指定选择的选择参数数组是更好的方式,即使提供者不是基于SQL数据库。

Displaying query results 显示查询结果

 客户端方法ContentResolver.query() 总是返回包含了指定查询投射(projection)和匹配查询选择标准(selection criteria)的Cursor。其提供了随即访问其所包含了行与列。使用Cursor 方法,你可以迭代访问结果中的每一行,决定每一列的类型,从列中获取数据,并检查结果中其他属性。当提供者数据改变,或者当Cursor 改变而触发处于观察者的方法,或者两者都改变时,一些Cursor实现自动更新对象。   

注意:一个提供者可能限制基于查询对象性质的列访问。举个例子,联系人访问限制某些同步适配器的某些列,所以它不会在活动或者服务中返回。

如果没有选择标准匹配行,提供者返回的 Cursor对象是空游标(an empty cursor)。它的 Cursor.getCount()是0。

如果出现内部错误,查询结果将部分取决于提供者。它可能选择返回空null,也可能抛出异常Exception

由于Cursor 是行的“列表”,一种显示Cursor 好的方式是通过SimpleCursorAdapter将其与ListView连接。

下面的代码继续了之前的片段。它创建了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};// 创建一个 SimpleCursorAdaptermCursorAdapter = new SimpleCursorAdapter(    getApplicationContext(),               // 应用程序的 Context 对象    R.layout.wordlistrow,                  // ListView中一行的XML布局文件    mCursor,                               // 查询结果    mWordListColumns,                      // cursor中列名的字符串数组    mWordListItems,                        // 行布局中的Views ID的整数数组    0);                                    // 标识位 (通常不需要)// 为ListView设置适配器mWordList.setAdapter(mCursorAdapter);

注意:返回拥有 Cursor 的ListView ,cursor必须包含名为_ID的列。正因为这样,查询在"words" 表中先查询了_ID列,即使ListView 不显示它。这个限制也解释了为什么每个表中都拥有_ID列.

Getting data from query results 从查询集中获得数据

不仅是简单显示查询结果,你也可以将其用于其他任务。举个例子,你可以在用户字典中查询拼写然后在其他提供者上查找他们。为了这么做,你迭代访问Cursor 的每一行。

// 返回名称为"word"的编号int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);/* * 仅当cursor有效时才执行. 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()) {        // 从对应列中获取值        newWord = mCursor.getString(index);        // 在这插入代码来处理查询到的单词        ...        // 终止while循环    }} else {    // 在这插入代码,如果cursor为空或者提供者抛出了异常}

Cursor的实现包含了一些 方法来检索对象中的不同类型的数据。举个例子,之前的代码片段中使用了getString() 。他们也拥有getType()方法来返回一个值来标识列类型数据。

Content Provider Permissions  内容提供权限


一个提供者能够指定其他应用程序访问提供者数据的权限。这些权限确保了用户知道应用程序将要试图访问的数据。基于提供者的要求,为了访问提供者其他应用程序需要请求这个权限。当用户安装程序时,他们可以看到这些请求。

如果提供者不指定权限,其他应用程序就无法访问提供者的数据了。但提供者自身组建总是拥有完全的读写权限的,而不论指定权限是什么。

正如之前提到的那样,用户字典提供者请求android.permission.READ_USER_DICTIONARY 权限来检索从其访问数据。提供者自己拥有android.permission.WRITE_USER_DICTIONARY 权限来进行插入,更新,删除数据。

需要在manifest文件中添加<uses-permission>元素来确保访问提供者数据的权限。当安卓包管理器安装应用程序时,用户必须确认相应权限。如果用户同意,则继续安装,如果用户不同意,则中断安装。

下面的<uses-permission> 元素请求了读取用户字典提供者。

    <uses-permission android:name="android.permission.READ_USER_DICTIONARY">

The impact of permissions on provider access is explained in more detail in the Security and Permissionsguide.


Inserting, Updating, and Deleting Data  插入,更新,删除数据


与检索数据相同的是,你也使用处于提供者和提供者客户端之间的交互。你可以使用与ContentResolver方法已经与之相一致的参数。提供者和提供者客户端(接收者)自动的处理安全和进程间通信。

Inserting data 插入数据

为了像提供者插入数据,你可以调用ContentResolver.insert()方法。这个方法将往提供者插入一行并返回那行对应的content URI。 下面这个片段显示了如何向用户字典提供者中插入一行。

// 定义一个新的Uri 对象来接收插入结果Uri mNewUri;...// 定义一个包含插入的新值的对象ContentValues mNewValues = new ContentValues();
//ContentValues 用于存储ContentResolver能够处理的结果集
/* * 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.putNull() 来设置一个栏位为空(null)。

这个片段没有加入_ID列,是因为这一列可以被自动维护。提供者为添加的每一行指派了一个唯一的_ID值。提供者通常将其作为表的主键。

以下面的格式返回新增加行标志符newUri.

content://user_dictionary/words/<id_value>

<id_value>是新行中_ID的内容。大部分提供者能够自动侦测到这种形式的content URI,并以请求的操作处理特定行。

为了获得返回 Uri_ID值,调用方法 ContentUris.parseId()。 

Updating data 更新数据

为了更新一行,你使用ContentValues对象就如果插入操作来执行更新。客户端方法是。你只需要在ContentValues对象中加入你需要更新的列。如果你想要清楚列的内容,将值设为null。

下面的片段将位置包含"en"的位置设为空null。返回值是更新的行:

// Defines an object to contain the updated valuesContentValues mUpdateValues = new ContentValues();// Defines selection criteria for the rows you want to updateString 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,   // 用户字典内容URI    mUpdateValues                       // 更新的列    mSelectionClause                    // 选择    mSelectionArgs                      // 参数);

在调用ContentResolver.update()时,你需要审察用户的输入。请看前文章节Protecting against malicious input.

Deleting data  删除数据

删除数据与检索数据相似:对你需要删除的行指定选择规则,然后客户端返回这些被删除的行。下面的代码片段删除了app id匹配"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);

你同样需要检查用户输入。防止恶意输入。

Provider Data Types  提供者数据类型


提供者可以提供不同的数据类型。用户字典提供者仅仅提供了text(文本),但提供者可以提供下面的类型:

  • 整形
  • 长整形
  • 浮点
  • 长浮点

提供者另一个数据类型是BLOB(Binary Large OBject:二进制大对象)你可以查阅Cursor类的get方法来查阅所有可用的类型。

提供者中每一列的数据类型通常列举在它的文档中。用户字典提供者的数据类型列举在他的contract类中( contract 类的描述请参阅 Contract Classes)你可以通过调用Cursor.getType()来获得数据类型。

提供者也维护其定义的每一个content URI的MIME数据类型信息。倘若应用程序能够处理,你可以使用MIME类型信息来找到提供者提供的数据类型,或者处理基于MIME的类型。当你与包含了复杂数据结构或者文件的提供者协同工作时,你常常需要MIME类型。举个例子,在联系人提供者中的ContactsContract.Data 表使用MIME类型来储存联系人数据类型的每一行。为了获得与content URI相一致的MIME类型,调用ContentResolver.getType()方法.

MIME Type Reference章节 描述了标准和自定义的MIME类型。

Alternative Forms of Provider Access 访问提供者可选的形式


在应用程序开发中三种可选的提供者访问形式:

  • 批访问:使用ContentProviderOperation 类中的方法来创建批访问调用,使用ContentResolver.applyBatch() 来应用.
  • 异步查询:你应该在独立的线程中查询。一种方式是使用CursorLoader对象。例子在Loaders向导中阐述了怎么做。
  • 经由intents的数据访问:尽管你不能直接将intent发给提供者,你可以发送intent给提供者的应用程序,这通常是最佳修饰提供者数据的方法。

批访问和经由intent修饰可在后面章节中看到。

Batch access  批访问


对于插入大量行,或者在同一方法内插入多行到多表或者以穿越进程边界的一组操作的通用执行。

为了使提供者处于"batch mode",你创建 对象的数组,并使用 来将它们分配给内容提供者。你将内容提供者的权威名称给了这个方法,而不是特别的内容URI。这允许每个ContentProviderOperation对象与不同的表。调用 返回一组结果。

Data access via intents 经由intents的数据访问


Intents 能够提供直接对内容提供者的访问。你允许用户访问提供者的数据即使你的应用程序不具有相应的权限,而是intent返回的应用程序有相应权限,或者被激活的应用程序有权限并允许用户和它工作。


Getting access with temporary permissions  获得临时权限

You can access data in a content provider, even if you don't have the proper access permissions, by sending an intent to an application that does have the permissions and receiving back a result intent containing "URI" permissions. These are permissions for a specific content URI that last until the activity that receives them is finished. The application that has permanent permissions grants temporary permissions by setting a flag in the result intent:

你能够访问内容提供者的数据,即使你不具有合适的访问权限,通过发送intent给拥有权限和接收返回数据且包含"URI"权限的应用程序。这有些内容URI权限,他们持续到接收他们的activity结束。拥有永久权限的应用程序担保临时的权限是在intent结果中通过置标记位:

  • Read permission: FLAG_GRANT_READ_URI_PERMISSION
  • Write permission: FLAG_GRANT_WRITE_URI_PERMISSION

Note: These flags don't give general read or write access to the provider whose authority is contained in the content URI. The access is only for the URI itself.

A provider defines URI permissions for content URIs in its manifest, using the android:grantUriPermissionattribute of the <provider> element, as well as the <grant-uri-permission> child element of the <provider>element. The URI permissions mechanism is explained in more detail in the Security and Permissions guide, in the section "URI Permissions".

For example, you can retrieve data for a contact in the Contacts Provider, even if you don't have theREAD_CONTACTS permission. You might want to do this in an application that sends e-greetings to a contact on his or her birthday. Instead of requesting READ_CONTACTS, which gives you access to all of the user's contacts and all of their information, you prefer to let the user control which contacts are used by your application. To do this, you use the following process:

  1. Your application sends an intent containing the action ACTION_PICK and the "contacts" MIME typeCONTENT_ITEM_TYPE, using the method startActivityForResult().
  2. Because this intent matches the intent filter for the People app's "selection" activity, the activity will come to the foreground.
  3. In the selection activity, the user selects a contact to update. When this happens, the selection activity callssetResult(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.
  4. 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.
  5. 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.

Using another application

A simple way to allow the user to modify data to which you don't have access permissions is to activate an application that has permissions and let the user do the work there.

For example, the Calendar application accepts an ACTION_INSERT intent, which allows you to activate the application's insert UI. You can pass "extras" data in this intent, which the application uses to pre-populate the UI. Because recurring events have a complex syntax, the preferred way of inserting events into the Calendar Provider is to activate the Calendar app with an ACTION_INSERT and then let the user insert the event there.

Contract Classes   联系人 类


A contract class defines constants that help applications work with the content URIs, column names, intent actions, and other features of a content provider. Contract classes are not included automatically with a provider; the provider's developer has to define them and then make them available to other developers. Many of the providers included with the Android platform have corresponding contract classes in the packageandroid.provider.

For example, the User Dictionary Provider has a contract class UserDictionary containing content URI and column name constants. The content URI for the "words" table is defined in the constantUserDictionary.Words.CONTENT_URI. The UserDictionary.Words class also contains column name constants, which are used in the example snippets in this guide. For example, a query projection can be defined as:

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

Another contract class is ContactsContract for the Contacts Provider. The reference documentation for this class includes example code snippets. One of its subclasses, ContactsContract.Intents.Insert, is a contract class that contains constants for intents and intent data.

MIME Type Reference (Multipurpose Internet Mail Extensions" 多用途互联网邮件扩展)类型引用


内容提供者能够返回标准的MIME媒体类型,或者自定义的MIME字符串,或者两者。

MIME类型有如下格式:

type/subtype

举个例子,我们所熟知的MIME类型text/html 拥有text类型和html子类型。如果提供者以URI返回此类型,这意味着使用这个URI查询将返回包含HTML标记的文本。

用户自定义字符串,也叫“商家定制”MIME类型,拥有更加复杂的类型和字类型。类型总是下面这样的:

vnd.android.cursor.dir

以上是多行。

vnd.android.cursor.item

以上是单行。

字类型是提供者之地功能的。安卓内置的提供者通常拥有简单的子类型。举个例子,当联系人应用程序创建为电话号码创建一行,它设置如下的MIME行:

vnd.android.cursor.item/phone_v2

注意子类型值是phone_v2.

 其他提供者开发可能创建给予提供者名称及表名的自有子类型。举个例子,考虑一个提供者保护列车是可悲。提供者名称为com.example.trains, ,它包含了表 Line1, Line2, and Line3.

为了应答 content URI

content://com.example.trains/Line1

for 表 Line1,提供者返回 MIME 类型

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

为了应答 content URI

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

表 Line2 第5行, 提供者返回  MIME 类型

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

Most content providers define contract class constants for the MIME types they use. The Contacts Provider contract class ContactsContract.RawContacts, for example, defines the constant CONTENT_ITEM_TYPE for the MIME type of a single raw contact row.

Content URIs for single rows are described in the section Content URIs.

原创粉丝点击