Android官方文档—APP组件(Content Providers)(Content Provider Basics)

本文深入讲解Android内容提供商的基础知识,涵盖其工作原理、API使用、数据访问控制及安全性,同时提供了批量访问、异步查询和通过意图访问数据的实践指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内容提供商基础知识

内容提供商管理对中央数据存储库的访问。提供程序是Android应用程序的一部分,该应用程序通常提供自己的用于处理数据的UI。但是,内容提供程序主要供其他应用程序使用,这些应用程序使用提供程序客户端对象访问提供程序。提供商和提供商客户一起为数据提供一致的标准接口,该接口还处理进程间通信和安全数据访问。

本主题介绍以下基础知识:

  • 内容提供商如何工作。
  • 您使用的API从内容提供商处检索数据。
  • 用于在内容提供程序中插入,更新或删除数据的API。
  • 有关提供商其他API功能。

概述


内容提供程序将数据作为一个或多个表格呈现给外部应用程序,这些表格与关系数据库中的表格类似。行表示提供程序收集的某种类型数据的实例,行中的每个列表示为实例收集的单个数据。

例如,Android平台中的一个内置提供者是用户词典,其存储用户想要保留的非标准词的拼写。表1说明了此提供程序表中数据的外观:

表1:示例用户字典表。

在表1中,每行表示可能在标准字典中找不到的单词的实例。每列代表该单词的一些数据,例如首次遇到它的语言环境。列标题是存储在提供程序中的列名。要引用行的语言环境,请参阅其语言环境列。对于此提供程序,_ID列用作提供程序自动维护的“主键”列。

注意:提供程序不需要具有主键,并且如果存在主键,则不需要使用_ID作为主键的列名。但是,如果要将数据从提供程序绑定到ListView,则其中一个列名称必须为_ID。显示查询结果部分将更详细地说明此要求。

访问提供商

应用程序使用ContentResolver客户端对象从内容提供程序访问数据。此对象具有在提供程序对象中调用具有相同名称的方法的方法,该对象是ContentProvider的一个具体子类的实例。 ContentResolver方法提供持久存储的基本“CRUD”(创建,检索,更新和删除)功能。

客户端应用程序进程中的ContentResolver对象和拥有提供程序的应用程序中的ContentProvider对象自动处理进程间通信。 ContentProvider还充当其数据存储库与作为表的数据外部外观之间的抽象层。

注意:要访问提供程序,您的应用程序通常必须在其清单文件中请求特定权限。内容提供程序权限一节中对此进行了更详细的描述。

例如,要从User Dictionary Provider获取单词及其语言环境的列表,请调用ContentResolver.query()。 query()方法调用User Dictionary Provider定义的ContentProvider.query()方法。以下代码行显示ContentResolver.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

表2显示了查询的参数(Uri,projection,selection,selectionArgs,sortOrder)如何与SQL SELECT语句匹配:

表2:Query()与SQL查询进行比较。

内容URI

 内容URI是标识提供者中的数据的URI。内容URI包括整个提供程序的符号名称(其权限)和指向表(路径)的名称。当您调用客户端方法来访问提供程序中的表时,该表的内容URI是其中一个参数。

在前面的代码行中,常量CONTENT_URI包含用户词典的“单词”表的内容URI。 ContentResolver对象解析URI的权限,并通过将权限与已知提供程序的系统表进行比较来使用它来“解析”提供程序。然后,ContentResolver可以将查询参数分派给正确的提供程序。

ContentProvider使用内容URI的路径部分来选择要访问的表。提供程序通常为其公开的每个表都有一个路径。

 在前面的代码行中,“words”表的完整URI是:

content://user_dictionary/words

user_dictionary字符串是提供者的作者,而words是表的路径。字符串content://(方案)始终存在,并将其标识为内容URI。

许多提供程序允许您通过将ID值附加到URI的末尾来访问表中的单个行。例如,要从用户词典中检索_ID为4的行,可以使用此内容URI:

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

当您检索到一组行,然后想要更新或删除其中一行时,通常会使用id值。

注意:Uri和Uri.Builder类包含从字符串构造格式良好的URI对象的便捷方法。 ContentUris类包含将id值附加到URI的便捷方法。上一个代码段使用withAppendedId()将id附加到UserDictionary内容URI。

从提供者检索数据


本节介绍如何使用User Dictionary Provider作为示例从提供程序检索数据。

为了清楚起见,本节中的代码片段在“UI线程”上调用ContentResolver.query()。但是,在实际代码中,您应该在单独的线程上异步执行查询。一种方法是使用CursorLoader类,在Loaders指南中有更详细的描述。此外,代码行只是片段;它们不显示完整的应用程序。

要从提供程序检索数据,请执行以下基本步骤:

  1. 请求提供者的读访问权限。
  2. 定义向提供程序发送查询的代码。

请求读访问权限

要从提供程序检索数据,您的应用程序需要提供程序的“读访问权限”。您无法在运行时请求此权限;相反,您必须使用<uses-permission>在清单中指定您需要此权限。元素和提供者定义的确切权限名称。在清单中指定此元素时,您实际上正在为您的应用程序“请求”此权限。当用户安装您的应用程序时,他们会隐式授予此请求。

要查找您正在使用的提供程序的读访问权限的确切名称,以及提供程序使用的其他访问权限的名称,请查看提供程序的文档。

“内容提供程序权限”一节中详细介绍了权限在访问提供程序中的作用。

用户词典提供程序在其清单文件中定义权限android.permission.READ_USER_DICTIONARY,因此想要从提供程序读取的应用程序必须请求此权限。

构造查询

从提供程序检索数据的下一步是构造查询。第一个片段定义了一些用于访问User Dictionary Provider的变量:

// 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 = {""};

下一个代码段显示了如何使用ContentResolver.query(),以User Dictionary Provider为例。提供者客户端查询类似于SQL查询,它包含一组要返回的列,一组选择条件和一个排序顺序。

查询应返回的列集称为投影(变量mProjection)。

指定要检索的行的表达式将拆分为选择子句和选择参数。 selection子句是逻辑和布尔表达式,列名和值(变量mSelectionClause)的组合。如果指定可替换参数?而不是某个值,查询方法从选择参数数组(变量mSelectionArgs)中检索值。

在下一个代码段中,如果用户未输入单词,则选择子句将设置为null,并且查询将返回提供程序中的所有单词。如果用户输入单词,则选择子句设置为UserDictionary.Words.WORD +“=?”并且选择参数数组的第一个元素设置为用户输入的单词。

/*
 * 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, 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 name
String mSelectionClause =  "var = " + mUserInput;

如果这样做,则允许用户将恶意SQL连接到SQL语句。例如,用户可以输入“nothing; DROP TABLE *;”对于mUserInput,这将导致选择子句var = nothing; DROP TABLE *;。由于selection子句被视为SQL语句,这可能会导致提供程序擦除底层SQLite数据库中的所有表(除非将提供程序设置为捕获SQL注入尝试)。

要避免此问题,请使用使用?的选择子句?作为可替换参数和单独的选择参数数组。执行此操作时,用户输入直接绑定到查询,而不是被解释为SQL语句的一部分。因为它不被视为SQL,所以用户输入不能注入恶意SQL。使用此选择子句,而不是使用串联来包含用户输入:

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

设置选择参数数组,如下所示:

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

在选择参数数组中放置一个值,如下所示:

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

使用的选择条件?作为可替换参数和选择参数数组数组是指定选择的首选方法,即使提供程序不是基于SQL数据库也是如此。

显示查询结果

ContentResolver.query()客户端方法始终返回一个Cursor,其中包含查询为与查询的选择条件匹配的行的投影指定的列。 Cursor对象提供对其包含的行和列的随机读取访问。使用Cursor方法,您可以迭代结果中的行,确定每列的数据类型,从列中获取数据,以及检查结果的其他属性。某些Cursor实现在提供程序的数据更改时自动更新对象,或在Cursor更改时触发观察器对象中的方法,或两者​​都触发。

注意:提供程序可以根据进行查询的对象的性质来限制对列的访问。例如,Contacts Provider限制某些列对同步适配器的访问,因此它不会将它们返回给activity或服务。

如果没有符合选择条件的行,则提供程序返回Cursor对象,其Cursor.getCount()为0(空游标)。

如果发生内部错误,查询结果将取决于特定提供程序。它可能会选择返回null,或者它可能会抛出异常。

由于Cursor是行的“列表”,因此显示Cursor内容的一种好方法是通过SimpleCursorAdapter将其链接到ListView。

以下代码段继续前一代码段中的代码。它创建一个SimpleCursorAdapter对象,其中包含查询检索到的Cursor,并将此对象设置为ListView的适配器:

// Defines a list of columns to retrieve from the Cursor and load into an output row
String[] 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 row
int[] mWordListItems = { R.id.dictWord, R.id.locale};

// Creates a new SimpleCursorAdapter
mCursorAdapter = 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 ListView
mWordList.setAdapter(mCursorAdapter);

注意:要使用Cursor来展示ListView,游标必须包含名为_ID的列。因此,前面显示的查询检索“words”表的_ID列,即使ListView不显示它。此限制还解释了为什么大多数提供程序的每个表都有一个_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.
}

游标实现包含几个“get”方法,用于从对象中检索不同类型的数据。例如,前一个代码段使用getString()。它们还有一个getType()方法,该方法返回一个指示列数据类型的值。

内容提供商权限


提供者的应用程序可以指定其他应用程序必须具有的权限才能访问提供者的数据。这些权限可确保用户知道应用程序将尝试访问哪些数据。根据提供商的要求,其他应用程序请求访问提供商所需的权限。最终用户在安装应用程序时会看到所请求的权限。

如果提供者的应用程序未指定任何权限,则其他应用程序无权访问提供者的数据。但是,无论指定的权限如何,提供程序的应用程序中的组件始终具有完全的读写访问权限。

如前所述,User Dictionary Provider需要android.permission.READ_USER_DICTIONARY权限才能从中检索数据。提供程序具有单独的android.permission.WRITE_USER_DICTIONARY权限,用于插入,更新或删除数据。

要获得访问提供程序所需的权限,应用程序会使用<uses-permission>来请求它们。清单文件中的元素。当Android Package Manager安装应用程序时,用户必须批准应用程序请求的所有权限。如果用户批准所有这些,则包管理器继续安装;如果用户不批准它们,则包管理器将中止安装。

以下<uses-permission>元素请求对User Dictionary Provider的读访问权:

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

“安全性和权限”指南中详细介绍了权限对提供程序访问的影响。

插入,更新和删除数据


与从提供程序检索数据的方式相同,您还可以使用提供程序客户端和提供程序的ContentProvider之间的交互来修改数据。您使用传递给ContentProvider的相应方法的参数调用ContentResolver的方法。提供者和提供者客户端自动处理安全性和进程间通信。

插入数据

要将数据插入提供程序,请调用ContentResolver.insert()方法。此方法将新行插入提供程序并返回该行的内容URI。此代码段显示如何将新单词插入用户词典提供程序:

// 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
);

新行的数据进入单个ContentValues对象,该对象在形式上类似于单行游标。此对象中的列不需要具有相同的数据类型,如果您根本不想指定值,则可以使用ContentValues.putNull()将列设置为null。

该代码段不会添加_ID列,因为此列会自动维护。提供程序为添加的每一行分配_ID的唯一值。提供者通常使用此值作为表的主键。

newUri中返回的内容URI标识新添加的行,格式如下:

content://user_dictionary/words/<id_value>

<id_value>是新行的_ID的内容。大多数提供程序可以自动检测此形式的内容URI,然后在该特定行上执行请求的操作。

要从返回的Uri获取_ID的值,请调用ContentUris.parseId()。

更新数据

要更新行,您可以像使用插入一样使用带有更新值的ContentValues对象,并像使用查询一样使用选择条件。您使用的客户端方法是ContentResolver.update()。您只需要为要更新的列向ContentValues对象添加值。如果要清除列的内容,请将值设置为null。

以下代码段将其语言环境具有语言“en”的所有行更改为具有null的语言环境。返回值是已更新的行数:

// 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
);

您还应该在调用ContentResolver.update()时清理用户输入。要了解有关此内容的更多信息,请阅读“防止恶意输入”一节。

删除数据

删除行与检索行数据类似:您为要删除的行指定选择条件,客户端方法返回已删除行的数量。以下代码段删除appid与“user”匹配的行。该方法返回已删除行的数量。


// 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
);

您还应该在调用ContentResolver.delete()时清理用户输入。要了解有关此内容的更多信息,请阅读“防止恶意输入”一节。

提供者数据类型


内容提供商可以提供许多不同的数据类型用户词典提供程序仅提供文本,但提供程序还可以提供以下格式:

  • integer
  • long integer (long)
  • floating point
  • long floating point (double)

提供者经常使用的另一种数据类型是二进制大对象(BLOB),实现为64KB字节数组。您可以通过查看Cursor类“get”方法来查看可用的数据类型。

提供程序中每列的数据类型通常在其文档中列出。用户词典提供程序的数据类型列在其Contract类UserDictionary.Words的参考文档中(Contract类在“Contract类”一节中描述)。您还可以通过调用Cursor.getType()来确定数据类型。

提供者还为他们定义的每个内容URI维护MIME数据类型信息。您可以使用MIME类型信息来确定您的应用程序是否可以处理提供程序提供的数据,或者根据MIME类型选择处理类型。在使用包含复杂数据结构或文件的提供程序时,通常需要MIME类型。例如,Contacts Provider中的ContactsContract.Data表使用MIME类型标记存储在每行中的联系人数据的类型。要获取与内容URI对应的MIME类型,请调用ContentResolver.getType()。

MIME Type Reference部分描述了标准和自定义MIME类型的语法。

提供者访问的替代形式


提供者访问的三种替代形式在应用程序开发中很重要:

  • 批量访问:您可以使用ContentProviderOperation类中的方法创建一批访问调用,然后将它们与ContentResolver.applyBatch()一起应用。
  • 异步查询:您应该在单独的线程中执行查询。一种方法是使用CursorLoader对象。 Loaders指南中的示例演示了如何执行此操作。
  • 通过意图进行数据访问:虽然您无法直接向提供者发送意图,但您可以向提供者的应用程序发送意图,该应用程序通常是修改提供者数据的最佳选择。

以下各节介绍了通过意图进行批量访问和修改。

批量访问

批量访问提供程序对于插入大量行或在同一方法调用中的多个表中插入行很有用,或者通常用于跨进程边界执行一组操作作为事务(原子操作)。

要以“批处理模式”访问提供程序,需要创建一个ContentProviderOperation对象数组,然后使用ContentResolver.applyBatch()将它们分发给内容提供程序。您将内容提供程序的权限传递给此方法,而不是特定的内容URI。这允许数组中的每个ContentProviderOperation对象针对不同的表进行操作。对ContentResolver.applyBatch()的调用返回结果数组。

ContactsContract.RawContacts Contract类的描述包括演示批量插入的代码片段。 Contact Manager示例应用程序在其ContactAdder.java源文件中包含批量访问的示例。

使用帮助应用程序显示数据

如果您的应用程序确实具有访问权限,您仍可能希望使用意图在另一个应用程序中显示数据。例如,Calendar应用程序接受ACTION_VIEW意图,该意图显示特定日期或事件。这使您可以显示日历信息,而无需创建自己的UI。要了解有关此功能的更多信息,请参阅日历提供程序指南。

发送意图的应用程序不必是与提供程序关联的应用程序。例如,您可以从联系提供程序检索联系人,然后将包含联系人图像的内容URI的ACTION_VIEW意图发送给图像查看器。

通过意图访问数据

意图可以提供对内容提供者的间接访问。即使您的应用程序没有访问权限,也允许用户访问提供程序中的数据,方法是从具有权限的应用程序获取结果意图,或者通过激活具有权限的应用程序并让用户在其中工作它。

获得临时权限的访问权限

即使您没有适当的访问权限,也可以通过向具有权限的应用程序发送意图并接收包含“URI”权限的结果意图来访问内容提供程序中的数据。这些是特定内容URI的权限,这些内容URI将持续到接收它们的活动完成为止。具有永久权限的应用程序通过在结果意图中设置标志来授予临时权限:

  • Read permission: FLAG_GRANT_READ_URI_PERMISSION
  • Write permission: FLAG_GRANT_WRITE_URI_PERMISSION

注意:这些标志不提供对其权限包含在内容URI中的提供程序的一般读取或写入访问权限。访问仅适用于URI本身。

提供者使用<provider>的android:grantUriPermission属性或使用<provider>的<grant-uri-permission>子元素为其清单中的内容URI定义URI权限。 URI权限机制在“安全性和权限”指南的“URI权限”部分中有更详细的说明。

例如,即使您没有READ_CONTACTS权限,也可以在Contacts Provider中检索联系人的数据。您可能希望在他或她的生日时向联系人发送电子问候语的应用程序中执行此操作。您不希望请求READ_CONTACTS(允许您访问所有用户的联系人及其所有信息),而是让用户控制应用程序使用的联系人。为此,请使用以下过程:

  1. 您的应用程序使用startActivityForResult()方法发送包含操作ACTION_PICK和“contacts”MIME类型CONTENT_ITEM_TYPE的intent。
  2. 因为此intent与People应用程序的“选择”活动的intent过滤器匹配,所以Activity将出现在前台。
  3. 在选择activity中,用户选择要更新的联系人。发生这种情况时,选择活动会调用setResult(resultcode,intent)来设置回复应用程序的意图。 intent包含用户选择的联系人的内容URI,以及“extras”标志FLAG_GRANT_READ_URI_PERMISSION。这些标志为您的应用授予URI权限,以读取内容URI指向的联系人的数据。然后,选择活动调用finish()将控制权返回给您的应用程序。
  4. 您的activity返回到前台,系统调用您的活动的onActivityResult()方法。此方法接收People应用程序中的选择活动创建的结果意图。
  5. 使用结果意图中的内容URI,您可以从联系人提供程序中读取联系人的数据,即使您未向清单中的提供程序请求永久读取访问权限。然后,您可以获取联系人的生日信息或他或她的电子邮件地址,然后发送电子贺卡。

使用其他应用程序

允许用户修改您没有访问权限的数据的简单方法是激活具有权限的应用程序,并让用户在那里进行工作。

例如,Calendar应用程序接受ACTION_INSERT intent,它允许您激活应用程序的插入UI。您可以在此intent中传递“extras”数据,应用程序使用该数据预先填充UI。由于重复发生的事件具有复杂的语法,因此将事件插入日历提供程序的首选方法是使用ACTION_INSERT激活日历应用程序,然后让用户在其中插入事件。

Contract 类


Contract类定义了有助于应用程序使用内容URI,列名,意图操作和内容提供程序的其他功能的常量。Contract类不会自动包含在提供者中;提供者的开发者必须定义它们,然后将它们提供给其他开发人员。 Android平台中包含的许多提供程序在android.provider包中都有相应的Contract类。

例如,User Dictionary Provider有一个包含内容URI和列名常量的Contract类UserDictionary。 “words”表的内容URI在常量UserDictionary.Words.CONTENT_URI中定义。 UserDictionary.Words类还包含列名常量,这些常量在本指南的示例代码段中使用。例如,查询投影可以定义为:

另一个Contacts 类是Contacts Provider的ContactsContract。此类的参考文档包括示例代码片段。其子类之一ContactsContract.Intents.Insert是一个Contacts 类,它包含意图和意图数据的常量。

MIME类型参考


内容提供商可以返回标准MIME媒体类型,或自定义MIME类型字符串,或两者兼有。

MIME类型具有格式

type/subtype

例如,众所周知的MIME类型text / html具有文本类型和html子类型。如果提供程序为URI返回此类型,则表示使用该URI的查询将返回包含HTML标记的文本。

自定义MIME类型字符串(也称为“特定于供应商”的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

对于表Line2中的第5行,提供程序返回MIME类型

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

大多数内容提供程序为它们使用的MIME类型定义Contract 类常量。例如,Contacts Provider合约类ContactsContract.RawContacts为单个原始联系人行的MIME类型定义常量CONTENT_ITEM_TYPE。

单个行的内容URI在Content URIs中描述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值