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

内容提供者(Content Provider)--跨程序共享数据 内容提供者简介 访问其他应用中的数据 ContentResolver 的基本用法 实例读取系统联系人 创建自己的内容提供器 创建内容提供器的步骤 创建新类继承ContentProvider并且重写其中6个方法 内容URI参数 使用UriMatcher实现匹配内容URI的功能 getType函数介绍 实现跨程序数据共享 内容提供者 访问自己创建的内容提供者数据 遇到的问题内容提供者简介 使用场景: 阅读详情

Content Provider Basics

内容提供者基础知识

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

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

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

 预览 Overview

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

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

表1:用户字典的例子

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中,每一行代表了一个可能无法在标准字典中找到的实例。每一列代表了那个单词的一些数据,例如位置(国籍)信息。存储了列的名字的头部存储于提供者中。为了引用一行中的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() argument SELECT keyword/parameter Notes
Uri FROM table_name Uri 映射到表名指定的提供者的一个表。
projection col,col,col,... 投射 是一簇列的集合,标示那些需要表示在检索结果中的列
selection WHERE col = value 选择 指定了行选择的标准
selectionArgs 没有确切的等式。使用?来进行替换对应参数  
sortOrder ORDER 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 name
String 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 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};

// 创建一个 SimpleCursorAdapter
mCursorAdapter = 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 values
ContentValues mUpdateValues = new ContentValues();

// Defines selection criteria for the rows you want to update
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,   // 用户字典内容URI
    mUpdateValues                       // 更新的列
    mSelectionClause                    // 选择
    mSelectionArgs                      // 参数
);

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

Deleting data  删除数据

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

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

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结果中通过置标记位:

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.

Android之内容提供者(ContentProvider) 内容提供者(ContentProvider)是Android四大组件之一,用于在不同应用之间共享数据。内容提供者通过统一资源标识符(URI)来标识数据,并使用一套标准的接口来访问和操作数据。它是Android系统中实现跨应用数据共享的主要方式。 阅读详情

相关推荐

RFSoC应用XCZU47DR八发八收FPGA设计方案

本文介绍了基于Xilinx XCZU47DR RFSoC芯片的八发八收FPGA设计方案。该方案采用集成射频数据转换器的SoC架构,支持8路14bit ADC(5GSPS)和8路14bit DAC(9.85GSPS),可直接处理5GHz以下射频信号。硬件设计包含高性能输入缓冲器、QMC校正和复杂混频器,适用于5G/6G通信、雷达、卫星通信等领域。文章详细阐述了ADC/DAC子系统参数,包括通道配置、采样率、带宽等特性,并提供了FPGA顶层模块的硬件接口设计,展示了多通道数据收发处理能力。该方案无需外置混频器,

toonyhe的博客 1782

第7章 使用内容提供者共享数据

第七章

qq_51580006的博客 1130

Cadence数模混合仿真实战:从Verilog到ADE L的完整流程解析

本文详细解析了Cadence数模混合仿真的完整流程,从Verilog代码编写到ADE L仿真环境配置。通过实战案例讲解如何创建Config文件、搭建混合信号原理图,并分享提高仿真效率的技巧和常见问题排查方法,帮助工程师掌握这一关键芯片设计验证技术。

weixin_29246195的博客 986

【Android】安卓四大组件之内容提供者

【Android】安卓四大组件之内容提供者 1、关于内容提供者 1.1 什么是内容提供者 内容提供者就是contentProvider,作用有如下: 给多个应用提供数据 类似一个接口 可以和多个应用分享数据 1.2 为什么要有内容提供者 作为一个APP,自己的数据会在某些条件下提供给其他APP,但是,APP的数据是私有的。 例如,APP A的数据库内容是不可以被APP B进行读取的 这个时候,我们就需要一个内容提供者,将APP A中的数据信息提供给APP B。 1.3 使用场景 就贴近生活一些吧,拿某宝

woodwhale的博客 8947

计算机视觉与深度学习 | 基于Matlab的多特征融合可视化指纹识别系统(附完整代码)

此系统展示了一个完整的指纹识别流程,实际应用中可以根据需求进一步优化算法,如改进方向场计算、增加脊线频率特征、使用更精确的匹配算法等。

尘世冰封的专栏 359

高速PCB设计中的一些常用规则

目前,高速PCB产品设计中,高速接口的应用日益普及,对于复杂的设计而言,高速走线就如同“蜘蛛网”一样密布在PCB各处,因此,在进行PCB检查及评审时,必要的高速PCB设计经验法则及规则是工程师开展工作必不可少的知识储备。

一只豌豆象的博客 3413

Android(4、ContentProvider内容提供者)

➢ContentProvider为App存取内部数据提供统- -的外部接口,让不同的应用之间得以共享数据。A应用。

yiyuanpeng2004的博客 1364

第1章第11节:创建可容纳多行滚动文字的输入框TextEditor [SwiftUI快速入门到实战]

TextField可以让用户输入单行的文字内容,若想输入多行的文字内容,可以使用能够容纳多行滚动文字的 TextEditor。 定义一个字符串属性,用来存储用户输入的文字内容。接着定义一个布尔属性,用来标识是否弹出警告窗口,以显示用户输入的内容。 TextEditor是由UIKit中的UITextView演变而来,因此可以在init方法中,通过UITextView修改TextEditor的外观属性。 添加一个VStack视图,作为子视图的容器。 创建一个TextEditor,Te..

互动教程网[www.hdjc8.com] 832

(原创)ContentProvider内容提供者入门

ContentProvider作为android四大组件之一,其实在实际用处中用的并不是很多 但这并不代表他的作用不大,今天就来带大家入个门。 介绍一下ContentProvider的用法。 ContentProvider为存储和获取数据提供统一的接口。她可以在不同的应用程序之间共享数据。 当然,比较常用的是调用系统的一些数据 Android已经为常见的一些数据提供了默认的Content

Android_xiong_st的博客 434

ESP8266开发之旅 基础篇① 走进ESP8266的世界

    欢迎大家来到ESP8266的世界,从现在开始,笔者将带领大家慢慢揭开ESP8266神秘的面纱。 1. 简介     从笔者的角度来说,ESP8266这个词可以理解为ESP8266EX芯片(当然,后面也有可能出现了其他芯片,这里首推ESP8266EX)以及依赖ESP8266EX芯片开发的系列模组的简称。     ESP8266EX是由乐鑫信息科技公司(Espressif)出品的一款应用于物联...

单片机菜鸟哥的博客 15万+

STM32 实现 4*4 矩阵键盘扫描(HAL库、标准库 都适用)

本文实现的代码是基于STM32HAL库的基础上的,不过标准库也可以用,只是调用的库函数不同,逻辑跟配置是一样的,按我这里的逻辑来配置即可。 1、键盘原理图: 原理举例:先把 F0-F7 内部拉高,这样这个8个引脚都是高电平,然后就进行列扫描。例如:假如按下3按钮,Y3 列扫描,把F4先拉低,然后读取F0-F3的状态,就会读出为1110,这就可 以知道是F3行拉低了,同时这时候是程序控制F4拉低的,这样就可以知道是F4列导致它转态变化了的,这样就可以定位出是F4列F3行的按键按下了;其他的列也是这样子扫描

lhh_qrsly的博客 7058

Android基础知识:内容提供者

Day10 内容提供者一、内容提供者1. 创建内容提供者 系统应用的数据库是不允许其他应用访问的 内容提供者的作用就是让别的应用访问到你的私有数据 自定义内容提供者,继承ContentProvider类,重写增删改查方法,在方法中写增删改查数据库的代码,例如:添加的方法@Override public Uri insert(Uri uri, ContentValues values) { d

Pinger的博客 781

【unity】【WebRTC】从0开始创建一个Unity远程媒体流app-构建可同步场景

本文介绍了如何利用Unity和WebRTC技术实现远程画面传输。通过配置Render Streaming组件、设置信令管理器和视频流发送器,构建了一个可同步的Unity场景。文中详细说明了从场景搭建到浏览器端测试的完整流程,并提供了常见问题排查方法。该方案适用于远程调试、云游戏、VR共享等场景,具有低延迟、高兼容性等特点。文章还包含项目源码下载链接,方便读者实践操作。最终实现了Unity端到浏览器端的实时画面传输,为后续实现双向互动奠定了基础。

每日出拳老爷子的博客 1840

Android 第5讲:内容提供者ContentProvider

Android内容提供者

weixin_47323733的博客 2939

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

android之内容提供者解析

该系统有两个应用,比较繁琐。但是内容提供者是android里非常非常重要的一个内容,我们得好好学习下哦。先看效果图,我们提供了四个按钮,点击按钮便会进行相应的操作。我们先看内容提供者所在的应用,代码结构:activity代码:package cn.com.contentProvider; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; public class Con

梦中一夜下江南 1万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Android开发学习(内容提供者)

安卓学习(内容提供者)

m0_55686284的博客 1407

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Android - 内容提供者

内容提供者组件根据请求将数据从一个应用程序提供给其他应用程序。 此类请求由 ContentResolver 类的方法处理。 内容提供者可以使用不同的方式来存储其数据,并且数据可以存储在数据库、文件甚至网络上。内容提供者有时需要跨应用程序共享数据。 这就是内容提供者变得非常有用的地方。内容提供商让您可以将内容集中在一个地方,...

小屁孩大帅-保密的博客 1077

Android内容提供者

是 Android 调试桥(ADB)中用于直接调用内容提供者(Content Provider)定义的方法的命令。在不确定有哪些特殊字符时,可以使用单引号将整个值括起来,这样可以避免大部分特殊字符的干扰。在不确定哪些特殊字符会出现的情况下,可以通过将值用双引号括起来,并对常见特殊字符(如反斜杠。:将值进行URL编码或Base64编码处理,传递时无需担心特殊字符的影响。:例如,使用正则表达式或脚本扫描并转义可能会引发问题的字符。指定的 URI 找到对应的内容提供者。参数指定的方法,并传递。

酌沧 2068

Android中的内容提供者

安卓中的内容提供者ContentProvider,案例:读取手机通信录

. 1330

Android——内容提供者

Android 基础中最重要的就四大组件,四大组件是Android学习的开始。 activity ——算作Android界面 service——服务 Broadcast Receiver——广播 Content Provider——内容提供者 这四大组件开始对我来说内容提供者是最难理解的,但是慢慢接触之后感觉也不是很难。相对于基础来说吧。 这两天我又看了这一章的

weixin_36545772的博客 4366

内容提供者

Android工程搭建一.ContentProvider介绍:二.原理图三.一系列权限四.动态获取权限五.自定义内容提供者自定义内容提供者思路创建提供者代码内容索要方代码 一.ContentProvider介绍: 1.ContentProvider内容提供者(四大组件之一)主要用于在不同的应用程序之间实现数据共享的功能。 2.技能get点: (1)访问系统自带的内容提供者(******) 手机联系...

XinHao_Sir的博客 1595

android 内容提供者

android中的四大组件之一----内容提供者 用于不同app之间的数据共享,比如我在A应用程序中有个数据库,而应用B需要用到这个数据库的某个表信息,我们就可以在A应用中用ContentProvider来提供这个表的查询接口,这样也就做到了app之间的数据共享,还能让用户自定义接口提高安全性,避免了进程间调用,权限和开发成本问题。 先来复习一下Uri Uri 通用资源标志符,Web上可用的每种资源 -HTML文档、图像、视频片段、程序等可由一个Uri进行定位 构由以下几个部分组成 scheme、autho

Eliza白的博客 1550

android内容提供者优点,Android 内容提供者简介

在Android应用中,我们可以使用显式意图(Explicit Intent)来直接访问其他应用的Activity,但是这仅限于Activity的范畴;如果需要使用其他应用的数据,还需要用到另外一种组件,这就是所谓的内容提供者(Content Provider)。1、内容提供者(Content Porviders)简介内容提供者主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制...

weixin_32516271的博客 532

Android学习笔记--内容提供者

学习内容提供者内容提供者1.利用现有的内容提供者,操作相应程序的数据(ContentResolver 类)2.自定义自己的内容提供者,供其他程序使用 内容提供者 内容提供者,一般是用于跨程序之间数据的共享。一种是利用现有的内容提供者,来读取和操作相应程序的数据。另一种是创建自己的内容提供者为其他程序使用。 1.利用现有的内容提供者,操作相应程序的数据(ContentResolver 类) 想要访问内容提供者中所共享的数据,就必须要使用到ContentResolver这个类。可以通过Context中的getC

qq_44248026的博客 1628
上一篇: 安卓:内容提供者(导论)
下一篇: 安卓:可选的存储方式
computer165
博客等级 码龄16年 7粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值