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

本文档详述了在Android环境中创建内容提供者的过程,包括设计数据存储、实现ContentProvider类、设计内容URI、处理权限及实现契约类等关键步骤。

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

创建一个内容提供者

内容提供商管理对中央数据存储库的访问。您的内容提供者作为一个或多个类存在于Android应用程序中,并且属于清单文件的一个元素。您的一个类实现了一个ContentProvider子类,它是您的提供者和其他应用程序之间的接口。虽然内容提供商旨在将数据提供给其他应用程序,但您当然可以在应用程序中使用允许用户查询和修改提供商管理的数据的Activity。

本章节的其余部分是构建内容提供程序的基本步骤列表以及要使用的API列表。

在开始构建之前


在开始构建提供程序之前,请执行以下操作:

1.确定您是否需要内容提供商。如果要提供以下一项或多项功能,则需要构建内容提供程序:

  • 您希望向其他应用程序提供复杂的数据或文件。
  • 您希望允许用户将应用中的复杂数据复制到其他应用中。
  • 您希望使用搜索框架提供自定义搜索建议。

如果完全在您自己的应用程序中,则不需要提供程序来使用SQLite数据库。

2.如果您尚未这样做,请阅读内容提供商基础知识主题以了解有关提供商的更多信息。

接下来,按照以下步骤构建您的提供程序:

1.设计数据的原始存储。内容提供商以两种方式提供数据:

文件数据

通常存入文件的数据,例如照片,音频或视频。将文件存储在应用程序的私有空间中。为响应来自其他应用程序的文件请求,您的提供程序可以提供该文件的句柄。

“结构化”数据

通常存入数据库,数组或类似结构的数据。以与行和列表兼容的形式存储数据。行表示实体,例如库存中的人员或项目。列表示实体的某些数据,例如此人的姓名或项目的价格。存储此类数据的常用方法是在SQLite数据库中,但您可以使用任何类型的持久存储。要了解有关Android系统中可用存储类型的更多信息,请参阅设计数据存储一节。

2.定义ContentProvider类及其所需方法的具体实现。此类是您的数据与Android系统其余部分之间的接口。有关此类的更多信息,请参阅实现ContentProvider类一节。

3.定义提供程序的权限字符串,其内容URI和列名称。如果您希望提供者的应用程序处理意图,还要定义意图操作,附加数据和标志。还要为要访问数据的应用程序定义所需的权限。您应该考虑将所有这些值定义为单独的合同类中的常量;稍后,您可以将此类公开给其他开发人员。有关内容URI的更多信息,请参阅设计内容URI部分。有关意图的更多信息,请参阅意图和数据访问部分。

4.添加其他可选部分,例如示例数据或AbstractThreadedSyncAdapter的实现,可以在提供程序和基于云的数据之间同步数据。

设计数据存储


内容提供者是以结构化格式保存的数据的接口。在创建界接口之前,您必须决定如何存储数据。您可以以任何您喜欢的形式存储数据,然后设计接口以根据需要读取和写入数据。

这些是Android中可用的一些数据存储技术:

  • Android系统包括一个SQLite数据库API,Android自己的提供程序使用它来存储面向表的数据。 SQLiteOpenHelper类可以帮助您创建数据库,SQLiteDatabase类是访问数据库的基类。请记住,您不必使用数据库来实现存储库。提供程序在外部显示为一组表,类似于关系数据库,但这不是提供程序内部实现的要求。
  • 为了存储文件数据,Android有各种面向文件的API。要了解有关文件存储的更多信息,请阅读数据存储主题。如果您正在设计提供媒体相关数据(如音乐或视频)的提供商,则可以拥有一个将表数据和文件组合在一起的提供商。
  • 要使用基于网络的数据,请使用java.net和android.net中的类。您还可以将基于网络的数据同步到本地数据存储(如数据库),然后将数据作为表或文件提供。 Sample Sync Adapter示例应用程序演示了此类同步。

数据设计考虑因素

以下是设计提供商数据结构的一些提示:

  • 表数据应始终具有“主键”列,提供程序将其维护为每行的唯一数值。您可以使用此值将行链接到其他表中的相关行(将其用作“外键”)。虽然您可以为此列使用任何名称,但使用BaseColumns._ID是最佳选择,因为将提供程序查询的结果链接到ListView需要其中一个检索到的列具有名称_ID。
  • 如果要提供位图图像或其他非常大的面向文件的数据,请将数据存储在文件中,然后间接提供,而不是直接将其存储在表中。如果执行此操作,则需要告知提供程序的用户他们需要使用ContentResolver文件方法来访问数据。
  • 使用二进制大对象(BLOB)数据类型来存储大小不同或结构不同的数据。例如,您可以使用BLOB列来存储协议缓冲区或JSON结构。

您还可以使用BLOB实现与模式无关的表。在此类表中,您将主键列,MIME类型列和一个或多个通用列定义为BLOB。 BLOB列中数据的含义由MIME类型列中的值指示。这允许您在同一个表中存储不同的行类型。 Contacts Provider的“数据”表ContactsContract.Data是与模式无关的表的示例。

设计内容URI


内容URI是标识提供者中的数据的URI。内容URI包括整个提供程序的符号名称(其权限)以及指向表或文件(路径)的名称。可选的id部分指向表中的单个行。 ContentProvider的每个数据访问方法都有一个内容URI作为参数;这允许您确定要访问的表,行或文件。

内容URI的基础知识在主题内容提供程序基础知识中进行了描述。

设计authority

提供者通常具有单个权限,该权限用作其Android内部名称。为避免与其他提供程序冲突,您应使用Internet域所有权(反向)作为提供程序权限的基础。由于此建议对于Android程序包名称也是如此,因此您可以将提供程序权限定义为包含提供程序的程序包名称的扩展名。例如,如果您的Android软件包名称为com.example<appname>,则您的提供商authority为 com.example.<appname>.provider。

设计路径结构

开发人员通常通过附加指向各个表的路径来从权限创建内容URI。例如,如果您有两个表table1和table2,则组合上一个示例中的权限以生成内容URI com.example.<appname>.provider / table1和com.example<appname>.provider / table2 。路径不限于单个段,并且不必为路径的每个级别都有表。

处理内容URI ID

按照惯例,提供程序通过接受URI末尾的行ID值的内容URI来提供对表中单行的访问。此外,按照惯例,提供程序将ID值与表的_ID列匹配,并对匹配的行执行请求的访问。

此约定有助于访问提供者的应用程序的通用设计模式。该应用程序对提供程序执行查询,并使用CursorAdapter在ListView中显示生成的Cursor。 CursorAdapter的定义要求Cursor中的一列为_ID

然后,用户从UI中选择一个显示的行以查看或修改数据。应用程序从支持ListView的Cursor获取相应的行,获取此行的_ID值,将其附加到内容URI,并将访问请求发送给提供程序。然后,提供程序可以针对用户选择的确切行执行查询或修改。

内容URI模板

为了帮助您选择对传入内容URI采取哪种操作,提供者API包括便利类UriMatcher,它将内容URI“模式”映射到整数值。您可以在switch语句中使用整数值,该语句为内容URI或与特定模式匹配的URI选择所需的操作。

内容URI模式使用通配符匹配内容URI:

  • *:匹配任意长度的任何有效字符的字符串。
  • #:匹配任意长度的数字字符串。

作为设计和编码内容URI处理的示例,请考虑具有权限com.example.app.provider的提供程序,该提供程序可识别指向表的以下内容URI:

  • content://com.example.app.provider/table1:一个名为table1的表。
  • content://com.example.app.provider/table2/dataset1:一个名为dataset1的表。
  • content://com.example.app.provider/table2/dataset2:一个名为dataset2的表。
  • content://com.example.app.provider/table3:一个名为table3的表。

如果提供者附加了行ID,则提供者也会识别这些内容URI,例如content3 //com.example.app.provider/table3/1,表3中标识为1的行。

可能是以下的形式:

content://com.example.app.provider/*

匹配提供程序中的任何内容URI。

content://com.example.app.provider/table2/*:

匹配表dataset1和dataset2的内容URI,但不匹配table1或table3的内容URI。

content://com.example.app.provider/table3/#:匹配table3中单行的内容URI,例如content://com.example.app.provider/table3/6,用于由6标识的行。

以下代码段显示了UriMatcher中的方法是如何工作的。此代码通过使用内容URI模式content://<authority> /<path>来处理整个表的URI,与单行的URI不同。表格和内容://<authority> /<path><id>对于单行。

方法addURI()将权限和路径映射到整数值。方法match()返回URI的整数值。 switch语句在查询整个表和查询单个记录之间进行选择:

public class ExampleProvider extends ContentProvider {
...
    // Creates a UriMatcher object.
    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        /*
         * The calls to addURI() go here, for all of the content URI patterns that the provider
         * should recognize. For this snippet, only the calls for table 3 are shown.
         */

        /*
         * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used
         * in the path
         */
        sUriMatcher.addURI("com.example.app.provider", "table3", 1);

        /*
         * Sets the code for a single row to 2. In this case, the "#" wildcard is
         * used. "content://com.example.app.provider/table3/3" matches, but
         * "content://com.example.app.provider/table3 doesn't.
         */
        sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
    }
...
    // Implements ContentProvider.query()
    public Cursor query(
        Uri uri,
        String[] projection,
        String selection,
        String[] selectionArgs,
        String sortOrder) {
...
        /*
         * Choose the table to query and a sort order based on the code returned for the incoming
         * URI. Here, too, only the statements for table 3 are shown.
         */
        switch (sUriMatcher.match(uri)) {


            // If the incoming URI was for all of table3
            case 1:

                if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
                break;

            // If the incoming URI was for a single row
            case 2:

                /*
                 * Because this URI was for a single row, the _ID value part is
                 * present. Get the last path segment from the URI; this is the _ID value.
                 * Then, append the value to the WHERE clause for the query
                 */
                selection = selection + "_ID = " uri.getLastPathSegment();
                break;

            default:
            ...
                // If the URI is not recognized, you should do some error handling here.
        }
        // call the code to actually do the query
    }

另一个类ContentUris提供了处理内容URI的id部分的便捷方法。 Uri和Uri.Builder类包括用于解析现有Uri对象和构建新对象的便捷方法。

实现ContentProvider类


ContentProvider实例通过处理来自其他应用程序的请求来管理对结构化数据集的访问。所有形式的访问最终都会调用ContentResolver,然后ContentResolver调用ContentProvider的具体方法来获取访问权限。

必需的方法

抽象类ContentProvider定义了六个抽象方法,您必须将它们作为自己的具体子类的一部分来实现。除了onCreate()之外的所有这些方法都由尝试访问内容提供者的客户端应用程序调用:

query()

从您的提供商处检索数据。使用参数选择要查询的表,要返回的行和列以及结果的排序顺序。将数据作为Cursor对象返回。

insert()

在您的提供程序中插入一个新行。使用参数选择目标表并获取要使用的列值。返回新插入行的内容URI。

update()

更新提供程序中的现有行。使用参数选择要更新的表和行以及获取更新的列值。返回更新的行数

delete()

从提供商中删除行。使用参数选择表和要删除的行。返回已删除的行数。

getType()

返回与内容URI对应的MIME类型。在实现内容提供程序MIME类型一节中更详细地描述了此方法。

onCreate()

初始化您的提供商。 Android系统在创建提供程序后立即调用此方法。请注意,在ContentResolver对象尝试访问它之前,不会创建提供程序。

请注意,这些方法与具有相同名称的ContentResolver方法具有相同的签名。

您对这些方法的实施应考虑以下因素:

  • 除了onCreate()之外的所有这些方法都可以被多个线程一次调用,因此它们必须是线程安全的。要了解有关多个线程的更多信息,请参阅主题进程和线程。
  • 避免在onCreate()中进行冗长的操作。推迟初始化任务直到实际需要它们。实现onCreate()方法部分更详细地讨论了这一点。
  • 虽然您必须实现这些方法,但除了返回预期的数据类型之外,您的代码不必执行任何操作。例如,您可能希望阻止其他应用程序将数据插入某些表。为此,您可以忽略对insert()的调用并返回0。

实现query()方法

ContentProvider.query()方法必须返回Cursor对象,如果失败,则抛出异常。如果您使用SQLite数据库作为数据存储,则只需返回SQLiteDatabase类的query()方法之一返回的Cursor。如果查询与任何行都不匹配,则应返回其getCount()方法返回0的Cursor实例。只有在查询过程中发生内部错误时才应返回null。

如果您没有使用SQLite数据库作为数据存储,请使用Cursor的一个具体子类。例如,MatrixCursor类实现一个游标,其中每一行都是Object数组。使用此类,使用addRow()添加新行。

请记住,Android系统必须能够跨进程边界传递Exception。 Android可以针对以下可能在处理查询错误时有用的异常执行此操作:

  • IllegalArgumentException(如果您的提供者收到无效的内容URI,您可以选择抛出此异常)
  • NullPointerException

实现insert()方法

insert()方法使用ContentValues参数中的值向适当的表添加新行。如果列名不在ContentValues参数中,您可能希望在提供者代码或数据库模式中为其提供默认值。

此方法应返回新行的内容URI。要构造它,请使用withAppendedId()将新行的_ID(或其他主键)值附加到表的内容URI。

实现delete()方法

delete()方法不必从数据存储中物理删除行。如果您正在使用提供程序的同步适配器,则应考虑使用“delete”标记标记已删除的行,而不是完全删除该行。同步适配器可以检查已删除的行并从服务器中删除它们,然后再从提供程序中删除它们。

实现update()方法

update()方法采用insert()使用的相同ContentValues参数,以及delete()和ContentProvider.query()使用的相同selection和selectionArgs参数。这可能允许您在这些方法之间重用代码。 

实现onCreate()方法

Android系统在启动提供程序时调用onCreate()。您应该只在此方法中执行快速运行的初始化任务,并推迟数据库创建和数据加载,直到提供程序实际收到数据请求。如果您在onCreate()中执行冗长的任务,则会降低提供程序的启动速度。反过来,这将减慢从提供程序到其他应用程序的响应。

例如,如果您使用的是SQLite数据库,则可以在ContentProvider.onCreate()中创建新的SQLiteOpenHelper对象,然后在第一次打开数据库时创建SQL表。为此,第一次调用getWritableDatabase()时,它会自动调用SQLiteOpenHelper.onCreate()方法。

以下两个片段演示了ContentProvider.onCreate()和SQLiteOpenHelper.onCreate()之间的交互。第一个片段是ContentProvider.onCreate()的实现:

public class ExampleProvider extends ContentProvider

    /*
     * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
     * in a following snippet.
     */
    private MainDatabaseHelper mOpenHelper;

    // Defines the database name
    private static final String DBNAME = "mydb";

    // Holds the database object
    private SQLiteDatabase db;

    public boolean onCreate() {

        /*
         * Creates a new helper object. This method always returns quickly.
         * Notice that the database itself isn't created or opened
         * until SQLiteOpenHelper.getWritableDatabase is called
         */
        mOpenHelper = new MainDatabaseHelper(
            getContext(),        // the application context
            DBNAME,              // the name of the database)
            null,                // uses the default SQLite cursor
            1                    // the version number
        );

        return true;
    }

    ...

    // Implements the provider's insert method
    public Cursor insert(Uri uri, ContentValues values) {
        // Insert code here to determine which table to open, handle error-checking, and so forth

        ...

        /*
         * Gets a writeable database. This will trigger its creation if it doesn't already exist.
         *
         */
        db = mOpenHelper.getWritableDatabase();
    }
}

下一个片段是SQLiteOpenHelper.onCreate()的实现,包括一个帮助器类:

...
// A string that defines the SQL statement for creating a table
private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
    "main " +                       // Table's name
    "(" +                           // The columns in the table
    " _ID INTEGER PRIMARY KEY, " +
    " WORD TEXT"
    " FREQUENCY INTEGER " +
    " LOCALE TEXT )";
...
/**
 * Helper class that actually creates and manages the provider's underlying data repository.
 */
protected static final class MainDatabaseHelper extends SQLiteOpenHelper {

    /*
     * Instantiates an open helper for the provider's SQLite data repository
     * Do not do database creation and upgrade here.
     */
    MainDatabaseHelper(Context context) {
        super(context, DBNAME, null, 1);
    }

    /*
     * Creates the data repository. This is called when the provider attempts to open the
     * repository and SQLite reports that it doesn't exist.
     */
    public void onCreate(SQLiteDatabase db) {

        // Creates the main table
        db.execSQL(SQL_CREATE_MAIN);
    }
}

实现ContentProvider MIME类型


ContentProvider类有两种返回MIME类型的方法:

getType()

您必须为任何提供程序实现的必需方法之一。

getStreamTypes()

如果您的提供商提供文件,您应该实施的方法。

表的MIME类型

getType()方法返回MIME格式的String,该格式描述内容URI参数返回的数据类型。 Uri参数可以是模式而不是特定的URI;在这种情况下,您应该返回与模式匹配的内容URI相关的数据类型。

对于常见类型的数据(如text,HTML或JPEG),getType()应返回该数据的标准MIME类型。 IANA MIME媒体类型网站上提供了这些标准类型的完整列表。

对于指向一行或多行表数据的内容URI,getType()应返回Android特定于供应商的MIME格式的MIME类型:

  • 类型部分:vnd
  • 子类型部分:

                        如果URI模式是针对单行:android.cursor.item /

                         如果URI模式是多行:android.cursor.dir /

  • 特定于提供者的部分:vnd.<name>.<type>

您提供<name>和<type>。 <name>值应该是全局唯一的,并且<type>值对于相应的URI模式应该是唯一的。 <name>的一个不错的选择是您公司的名称或应用程序的Android软件包名称的某些部分。 <type>的一个很好的选择是一个字符串,用于标识与URI关联的表。

例如,如果提供者的权限是com.example.app.provider,并且它公开了一个名为table1的表,则table1中多行的MIME类型为:

vnd.android.cursor.dir/vnd.com.example.provider.table1

对于table1的单行,MIME类型为:

vnd.android.cursor.item/vnd.com.example.provider.table1

文件的MIME类型

如果您的提供者提供文件,请实现getStreamTypes()。该方法为您的提供程序可以为给定内容URI返回的文件返回MIME类型的String数组。您应该过滤MIME类型过滤器参数提供的MIME类型,以便仅返回客户端要处理的MIME类型。

例如,考虑将照片图像作为.jpg,.png和.gif格式的文件提供的提供程序。如果应用程序使用过滤器字符串image / *(某些是“图像”)调用ContentResolver.getStreamTypes(),则ContentProvider.getStreamTypes()方法应返回该数组:

{ "image/jpeg", "image/png", "image/gif"}

如果应用程序只对.jpg文件感兴趣,那么它可以使用过滤器字符串* \ / jpeg调用ContentResolver.getStreamTypes(),并且ContentProvider.getStreamTypes()应该返回:

{"image/jpeg"}

如果您的提供程序未提供过滤器字符串中请求的任何MIME类型,则getStreamTypes()应返回null。

实现契约类


契约类是公共最终类,包含URI,列名,MIME类型以及与提供者相关的其他元数据的常量定义。该类通过确保即使URI,列名等的实际值发生更改也可以正确访问提供程序,在提供程序和其他应用程序之间建立合同。

契约类也可以帮助开发人员,因为它的常量通常具有助记符名称,因此开发人员不太可能对列名或URI使用不正确的值。因为它是一个类,所以它可以包含Javadoc文档。集成开发环境(如Android Studio)可以自动完成契约类中的常量名称,并显示常量的Javadoc。

开发人员无法从您的应用程序访问合同类的类文件,但他们可以从您提供的.jar文件中将其静态编译到其应用程序中。

ContactsContract类及其嵌套类是合同类的示例。

实现内容提供商权限


安全和权限主题中详细介绍了Android系统所有方面的权限和访问权限。数据存储主题还描述了对各种类型的存储有效的安全性和权限。简而言之,重点是:

  • 默认情况下,存储在设备内部存储上的数据文件对您的应用程序和提供程序是私有的。
  • 默认情况下,保存到外部存储的数据文件是公共的,并且是世界可读的。您无法使用内容提供程序来限制对外部存储中文件的访问,因为其他应用程序可以使用其他API调用来读取和写入它们。
  • 该方法要求在设备的内部存储上打开或创建文件或SQLite数据库,这可能会对所有其他应用程序提供读写访问权限。如果您使用内部文件或数据库作为提供者的存储库,并且您为其提供“世界可读”或“世界可写”访问权限,则您在其清单中为提供者设置的权限将不会保护您的数据。内部存储中文件和数据库的默认访问权限是“私有”,对于提供商的存储库,您不应更改此设置。

如果要使用内容提供程序权限来控制对数据的访问,则应将数据存储在内部文件,SQLite数据库或“云”中(例如,在远程服务器上),并且应保留文件和数据库私有的申请。

实现权限

所有应用程序都可以读取或写入您的提供程序,即使基础数据是私有的,因为默认情况下您的提供程序没有设置权限。要更改此设置,请使用<provider>元素的属性或子元素在清单文件中为提供程序设置权限。您可以设置适用于整个提供程序,某些表,甚至某些记录或全部三个的权限。

您可以使用清单文件中的一个或多个<permission>元素为提供程序定义权限。要使提供者具有唯一权限,请对android:name属性使用Java样式作用域。例如,将读取权限命名为com.example.app.provider.permission.READ_PROVIDER。

以下列表描述了提供程序权限的范围,从适用于整个提供程序的权限开始,然后变得更细粒度。更细粒度的权限优先于范围更大的权限:

单读写提供程序级别权限

        一个权限,控制对整个提供程序的读写访问权限,使用<provider>元素的android:permission属性指定。

单独读取和写入提供者级别的权限

      对整个提供程序的读取权限和写入权限。您可以使用<provider>元素的android:readPermission和android:writePermission属性指定它们。它们优先于android:permission所需的权限。

路径级权限

     对提供程序中的内容URI的读取,写入或读取/写入权限。您可以使用<provider>元素的<path-permission>子元素指定要控制的每个URI。对于您指定的每个内容URI,您可以指定读/写权限,读权限或写权限,或全部三个。读写权限优先于读/写权限。此外,路径级权限优先于提供者级权限。

临时许可

       即使应用程序不具有通常所需的权限,也授予对应用程序的临时访问权限的权限级别。临时访问功能可减少应用程序在其清单中请求的权限数。当您打开临时权限时,只有您的提供商需要“永久”权限的应用程序才能持续访问您的所有数据。

      当您希望允许外部图像查看器应用程序显示来自提供商的照片附件时,请考虑实施电子邮件提供商和应用程序所需的权限。要在不需要权限的情况下为图像查看器提供必要的访问权限,请为照片的内容URI设置临时权限。设计您的电子邮件应用程序,以便当用户想要显示照片时,应用程序会向图像查看器发送包含照片内容URI和权限标记的意图。然后,图像查看器可以查询您的电子邮件提供商以检索照片,即使查看者没有您的提供商的正常读取权限。

      要打开临时权限,请设置<provider>元素的android:grantUriPermissions属性,或者将一个或多个<grant-uri-permission>子元素添加到<provider>元素。如果使用临时权限,则只要从提供程序中删除对内容URI的支持,就必须调用Context.revokeUriPermission(),并且内容URI与临时权限相关联。

      属性的值确定您的提供者可以访问的数量。如果该属性设置为true,则系统将向整个提供程序授予临时权限,从而覆盖提供程序级别或路径级别权限所需的任何其他权限。

     如果此标志设置为false,则必须将<grant-uri-permission>子元素添加到<provider>元素。每个子元素指定为其授予临时访问权限的内容URI或URI。

    要委派对应用程序的临时访问权限,intent必须包含FLAG_GRANT_READ_URI_PERMISSION或FLAG_GRANT_WRITE_URI_PERMISSION标志,或两者都包含。这些是使用setFlags()方法设置的。

    如果不存在android:grantUriPermissions属性,则假定为false。

<provider>元素


与Activity和Service组件一样,必须使用<provider>元素在其应用程序的清单文件中定义ContentProvider的子类。 Android系统从元素中获取以下信息:

Authority (android:authorities)

       识系统内整个提供程序的符号名称。在设计内容URI部分中更详细地描述了此属性。

提供者类名(android:name)

      实现ContentProvider的类。在实现ContentProvider类一节中更详细地描述了该类。

Permissions

       指定其他应用程序访问提供程序数据时必须具有的权限的属性:

  • android:grantUriPermissions:临时权限标志。
  • android:permission:单一提供者范围的读/写权限。
  • android:readPermission:提供者范围的读取权限。
  • android:writePermission:提供者范围的写权限。

权限及其相应的属性在实现内容提供程序权限一节中有更详细的描述。

启动和控制属性

这些属性决定了Android系统启动提供程序的方式和时间,提供程序的进程特征以及其他运行时设置:

  • android:enabled:允许系统启动提供程序的标志。
  • android:exported:允许其他应用程序使用此提供程序的标记。
  • android:initOrder:相对于同一进程中的其他提供程序,应该启动此提供程序的顺序。
  • android:multiProcess:允许系统在与调用客户端相同的进程中启动提供程序的标志。
  • android:process:提供程序应运行的进程的名称。
  • android:syncable:表示提供者的数据与服务器上的数据同步的标志。

这些属性完全记录在<provider>元素的开发指南主题中。

Informational attributes

提供者的可选图标和标签:

  • android:icon:包含提供者图标的可绘制资源。该图标显示在“设置”>“应用”>“全部”中应用列表中的提供商标签旁边。
  • android:label:描述提供者或其数据或两者的信息标签。该标签会显示在“设置”>“应用”>“全部”中的应用列表中。

这些属性完全记录在<provider>元素的开发指南主题中。

意图和数据访问


应用程序可以使用Intent间接访问内容提供程序。该应用程序不会调用ContentResolver或ContentProvider的任何方法。相反,它发送一个启动Activity的intent,这通常是提供者自己的应用程序的一部分。目标Activity负责在其UI中检索和显示数据。根据意图中的操作,目标Activity还可以提示用户修改提供者的数据。意图还可以包含目标Activity在UI中显示的“额外”数据;然后,用户可以选择在使用它来修改提供程序中的数据之前更改此数据。

您可能希望使用intent访问来帮助确保数据完整性。您的提供商可能依赖于根据严格定义的业务逻辑插入,更新和删除数据。如果是这种情况,允许其他应用程序直接修改您的数据可能会导致数据无效。如果您希望开发人员使用intent访问权限,请务必彻底记录。向他们解释为什么使用您自己的应用程序的UI进行意图访问比尝试使用他们的代码修改数据更好。

处理希望修改提供者数据的传入意图与处理其他意图没有什么不同。您可以通过阅读Intents和Intent Filters主题来了解有关使用意图的更多信息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值