Creating a Content Provider

A content provider manages access to a central repository of data. You implement a provider as one or more classes in an Android application, along with elements in the manifest file. One of your classes implements a subclassContentProvider, which is the interface between your provider and other applications. Although content providers are meant to make data available to other applications, you may of course have activities in your application that allow the user to query and modify the data managed by your provider.

The rest of this topic is a basic list of steps for building a content provider and a list of APIs to use.

Before You Start Building


Before you start building a provider, do the following:

  1. Decide if you need a content provider. You need to build a content provider if you want to provide one or more of the following features:
    • You want to offer complex data or files to other applications.
    • You want to allow users to copy complex data from your app into other apps.
    • You want to provide custom search suggestions using the search framework.

    You don't need a provider to use an SQLite database if the use is entirely within your own application.

  2. If you haven't done so already, read the topic Content Provider Basics to learn more about providers.

Next, follow these steps to build your provider:

  1. Design the raw storage for your data. A content provider offers data in two ways:
    File data
    Data that normally goes into files, such as photos, audio, or videos. Store the files in your application's private space. In response to a request for a file from another application, your provider can offer a handle to the file.
    "Structured" data
    Data that normally goes into a database, array, or similar structure. Store the data in a form that's compatible with tables of rows and columns. A row represents an entity, such as a person or an item in inventory. A column represents some data for the entity, such a person's name or an item's price. A common way to store this type of data is in an SQLite database, but you can use any type of persistent storage. To learn more about the storage types available in the Android system, see the section Designing Data Storage.
  2. Define a concrete implementation of the ContentProvider class and its required methods. This class is the interface between your data and the rest of the Android system. For more information about this class, see the section Implementing the ContentProvider Class.
  3. Define the provider's authority string, its content URIs, and column names. If you want the provider's application to handle intents, also define intent actions, extras data, and flags. Also define the permissions that you will require for applications that want to access your data. You should consider defining all of these values as constants in a separate contract class; later, you can expose this class to other developers. For more information about content URIs, see the section Designing Content URIs. For more information about intents, see the section Intents and Data Access.
  4. Add other optional pieces, such as sample data or an implementation of AbstractThreadedSyncAdapterthat can synchronize data between the provider and cloud-based data.

Designing Data Storage


A content provider is the interface to data saved in a structured format. Before you create the interface, you must decide how to store the data. You can store the data in any form you like, and then design the interface to read and write the data as necessary.

These are some of the data storage technologies that are available in Android:

  • The Android system includes an SQLite database API that Android's own providers use to store table-oriented data. The SQLiteOpenHelper class helps you create databases, and the SQLiteDatabase class is the base class for accessing databases.

    Remember that you don't have to use a database to implement your repository. A provider appears externally as a set of tables, similar to a relational database, but this is not a requirement for the provider's internal implementation.

  • For storing file data, Android has a variety of file-oriented APIs. To learn more about file storage, read the topicData Storage. If you're designing a provider that offers media-related data such as music or videos, you can have a provider that combines table data and files.
  • For working with network-based data, use classes in java.net and android.net. You can also synchronize network-based data to a local data store such as a database, and then offer the data as tables or files. TheSample Sync Adapter sample application demonstrates this type of synchronization.

Data design considerations

Here are some tips for designing your provider's data structure:

  • Table data should always have a "primary key" column that the provider maintains as a unique numeric value for each row. You can use this value to link the row to related rows in other tables (using it as a "foreign key"). Although you can use any name for this column, using BaseColumns._ID is the best choice, because linking the results of a provider query to a ListView requires one of the retrieved columns to have the name _ID.
  • If you want to provide bitmap images or other very large pieces of file-oriented data, store the data in a file and then provide it indirectly rather than storing it directly in a table. If you do this, you need to tell users of your provider that they need to use a ContentResolver file method to access the data.
  • Use the Binary Large OBject (BLOB) data type to store data that varies in size or has a varying structure. For example, you can use a BLOB column to store a protocol buffer or JSON structure.

    You can also use a BLOB to implement a schema-independent table. In this type of table, you define a primary key column, a MIME type column, and one or more generic columns as BLOB. The meaning of the data in the BLOB columns is indicated by the value in the MIME type column. This allows you to store different row types in the same table. The Contacts Provider's "data" table ContactsContract.Data is an example of a schema-independent table.

Designing Content URIs


content URI is a URI that identifies data in a provider. Content URIs include the symbolic name of the entire provider (its authority) and a name that points to a table or file (a path). The optional id part points to an individual row in a table. Every data access method of ContentProvider has a content URI as an argument; this allows you to determine the table, row, or file to access.

The basics of content URIs are described in the topic Content Provider Basics.

Designing an authority

A provider usually has a single authority, which serves as its Android-internal name. To avoid conflicts with other providers, you should use Internet domain ownership (in reverse) as the basis of your provider authority. Because this recommendation is also true for Android package names, you can define your provider authority as an extension of the name of the package containing the provider. For example, if your Android package name iscom.example.<appname>, you should give your provider the authority com.example.<appname>.provider.

Designing a path structure

Developers usually create content URIs from the authority by appending paths that point to individual tables. For example, if you have two tables table1 and table2, you combine the authority from the previous example to yield the content URIs com.example.<appname>.provider/table1 and com.example.<appname>.provider/table2. Paths aren't limited to a single segment, and there doesn't have to be a table for each level of the path.

Handling content URI IDs

By convention, providers offer access to a single row in a table by accepting a content URI with an ID value for the row at the end of the URI. Also by convention, providers match the ID value to the table's _ID column, and perform the requested access against the row that matches.

This convention facilitates a common design pattern for apps accessing a provider. The app does a query against the provider and displays the resulting Cursor in a ListView using a CursorAdapter. The definition of CursorAdapter requires one of the columns in the Cursor to be _ID

The user then picks one of the displayed rows from the UI in order to look at or modify the data. The app gets the corresponding row from the Cursor backing the ListView, gets the _ID value for this row, appends it to the content URI, and sends the access request to the provider. The provider can then do the query or modification against the exact row the user picked.

Content URI patterns

To help you choose which action to take for an incoming content URI, the provider API includes the convenience class UriMatcher, which maps content URI "patterns" to integer values. You can use the integer values in aswitch statement that chooses the desired action for the content URI or URIs that match a particular pattern.

A content URI pattern matches content URIs using wildcard characters:

  • *: Matches a string of any valid characters of any length.
  • #: Matches a string of numeric characters of any length.

As an example of designing and coding content URI handling, consider a provider with the authoritycom.example.app.provider that recognizes the following content URIs pointing to tables:

  • content://com.example.app.provider/table1: A table called table1.
  • content://com.example.app.provider/table2/dataset1: A table called dataset1.
  • content://com.example.app.provider/table2/dataset2: A table called dataset2.
  • content://com.example.app.provider/table3: A table called table3.

The provider also recognizes these content URIs if they have a row ID appended to them, as for examplecontent://com.example.app.provider/table3/1 for the row identified by 1 in table3.

The following content URI patterns would be possible:

content://com.example.app.provider/*
Matches any content URI in the provider.
content://com.example.app.provider/table2/*:
Matches a content URI for the tables  dataset1 and  dataset2, but doesn't match content URIs for table1 or  table3.
content://com.example.app.provider/table3/#: Matches a content URI for single rows in  table3, such as content://com.example.app.provider/table3/6 for the row identified by  6.

The following code snippet shows how the methods in UriMatcher work. This code handles URIs for an entire table differently from URIs for a single row, by using the content URI pattern content://<authority>/<path>for tables, and content://<authority>/<path>/<id> for single rows.

The method addURI() maps an authority and path to an integer value. The method match() returns the integer value for a URI. A switch statement chooses between querying the entire table, and querying for a single record:

public class ExampleProvider extends ContentProvider {
...
    // Creates a UriMatcher object.
    private static final UriMatcher sUriMatcher;
...
    /*
     * 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
    }

Another class, ContentUris, provides convenience methods for working with the id part of content URIs. The classes Uri and Uri.Builder include convenience methods for parsing existing Uri objects and building new ones.

Implementing the ContentProvider Class


The ContentProvider instance manages access to a structured set of data by handling requests from other applications. All forms of access eventually call ContentResolver, which then calls a concrete method ofContentProvider to get access.

Required methods

The abstract class ContentProvider defines six abstract methods that you must implement as part of your own concrete subclass. All of these methods except onCreate() are called by a client application that is attempting to access your content provider:

query()
Retrieve data from your provider. Use the arguments to select the table to query, the rows and columns to return, and the sort order of the result. Return the data as a  Cursor object.
insert()
Insert a new row into your provider. Use the arguments to select the destination table and to get the column values to use. Return a content URI for the newly-inserted row.
update()
Update existing rows in your provider. Use the arguments to select the table and rows to update and to get the updated column values. Return the number of rows updated.
delete()
Delete rows from your provider. Use the arguments to select the table and the rows to delete. Return the number of rows deleted.
getType()
Return the MIME type corresponding to a content URI. This method is described in more detail in the section  Implementing Content Provider MIME Types.
onCreate()
Initialize your provider. The Android system calls this method immediately after it creates your provider. Notice that your provider is not created until a  ContentResolver object tries to access it.

Notice that these methods have the same signature as the identically-named ContentResolver methods.

Your implementation of these methods should account for the following:

  • All of these methods except onCreate() can be called by multiple threads at once, so they must be thread-safe. To learn more about multiple threads, see the topic Processes and Threads.
  • Avoid doing lengthy operations in onCreate(). Defer initialization tasks until they are actually needed. The section Implementing the onCreate() method discusses this in more detail.
  • Although you must implement these methods, your code does not have to do anything except return the expected data type. For example, you may want to prevent other applications from inserting data into some tables. To do this, you can ignore the call to insert() and return 0.

Implementing the query() method

The ContentProvider.query() method must return a Cursor object, or if it fails, throw an Exception. If you are using an SQLite database as your data storage, you can simply return the Cursor returned by one of thequery() methods of the SQLiteDatabase class. If the query does not match any rows, you should return aCursor instance whose getCount() method returns 0. You should return null only if an internal error occurred during the query process.

If you aren't using an SQLite database as your data storage, use one of the concrete subclasses of Cursor. For example, the MatrixCursor class implements a cursor in which each row is an array of Object. With this class, use addRow() to add a new row.

Remember that the Android system must be able to communicate the Exception across process boundaries. Android can do this for the following exceptions that may be useful in handling query errors:

Implementing the insert() method

The insert() method adds a new row to the appropriate table, using the values in the ContentValuesargument. If a column name is not in the ContentValues argument, you may want to provide a default value for it either in your provider code or in your database schema.

This method should return the content URI for the new row. To construct this, append the new row's _ID (or other primary key) value to the table's content URI, using withAppendedId().

Implementing the delete() method

The delete() method does not have to physically delete rows from your data storage. If you are using a sync adapter with your provider, you should consider marking a deleted row with a "delete" flag rather than removing the row entirely. The sync adapter can check for deleted rows and remove them from the server before deleting them from the provider.

Implementing the update() method

The update() method takes the same ContentValues argument used by insert(), and the sameselection and selectionArgs arguments used by delete() and ContentProvider.query(). This may allow you to re-use code between these methods.

Implementing the onCreate() method

The Android system calls onCreate() when it starts up the provider. You should perform only fast-running initialization tasks in this method, and defer database creation and data loading until the provider actually receives a request for the data. If you do lengthy tasks in onCreate(), you will slow down your provider's startup. In turn, this will slow down the response from the provider to other applications.

For example, if you are using an SQLite database you can create a new SQLiteOpenHelper object inContentProvider.onCreate(), and then create the SQL tables the first time you open the database. To facilitate this, the first time you call getWritableDatabase(), it automatically calls the SQLiteOpenHelper.onCreate()method.

The following two snippets demonstrate the interaction between ContentProvider.onCreate() andSQLiteOpenHelper.onCreate(). The first snippet is the implementation of 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();
    }
}

The next snippet is the implementation of SQLiteOpenHelper.onCreate(), including a helper class:

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

Implementing ContentProvider MIME Types


The ContentProvider class has two methods for returning MIME types:

getType()
One of the required methods that you must implement for any provider.
getStreamTypes()
A method that you're expected to implement if your provider offers files.

MIME types for tables

The getType() method returns a String in MIME format that describes the type of data returned by the content URI argument. The Uri argument can be a pattern rather than a specific URI; in this case, you should return the type of data associated with content URIs that match the pattern.

For common types of data such as as text, HTML, or JPEG, getType() should return the standard MIME type for that data. A full list of these standard types is available on the IANA MIME Media Types website.

For content URIs that point to a row or rows of table data, getType() should return a MIME type in Android's vendor-specific MIME format:

  • Type part: vnd
  • Subtype part:
    • If the URI pattern is for a single row: android.cursor.item/
    • If the URI pattern is for more than one row: android.cursor.dir/
  • Provider-specific part: vnd.<name>.<type>

    You supply the <name> and <type>. The <name> value should be globally unique, and the <type> value should be unique to the corresponding URI pattern. A good choice for <name> is your company's name or some part of your application's Android package name. A good choice for the <type> is a string that identifies the table associated with the URI.

For example, if a provider's authority is com.example.app.provider, and it exposes a table named table1, the MIME type for multiple rows in table1 is:

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

For a single row of table1, the MIME type is:

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

MIME types for files

If your provider offers files, implement getStreamTypes(). The method returns a String array of MIME types for the files your provider can return for a given content URI. You should filter the MIME types you offer by the MIME type filter argument, so that you return only those MIME types that the client wants to handle.

For example, consider a provider that offers photo images as files in .jpg.png, and .gif format. If an application calls ContentResolver.getStreamTypes() with the filter string image/* (something that is an "image"), then the ContentProvider.getStreamTypes() method should return the array:

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

If the app is only interested in .jpg files, then it can call ContentResolver.getStreamTypes() with the filter string *\/jpeg, and ContentProvider.getStreamTypes() should return:

{"image/jpeg"}

If your provider doesn't offer any of the MIME types requested in the filter string, getStreamTypes() should return null.

Implementing a Contract Class


A contract class is a public final class that contains constant definitions for the URIs, column names, MIME types, and other meta-data that pertain to the provider. The class establishes a contract between the provider and other applications by ensuring that the provider can be correctly accessed even if there are changes to the actual values of URIs, column names, and so forth.

A contract class also helps developers because it usually has mnemonic names for its constants, so developers are less likely to use incorrect values for column names or URIs. Since it's a class, it can contain Javadoc documentation. Integrated development environments such as Android Studio can auto-complete constant names from the contract class and display Javadoc for the constants.

Developers can't access the contract class's class file from your application, but they can statically compile it into their application from a .jar file you provide.

The ContactsContract class and its nested classes are examples of contract classes.

Implementing Content Provider Permissions


Permissions and access for all aspects of the Android system are described in detail in the topic Security and Permissions. The topic Data Storage also described the security and permissions in effect for various types of storage. In brief, the important points are:

  • By default, data files stored on the device's internal storage are private to your application and provider.
  • SQLiteDatabase databases you create are private to your application and provider.
  • By default, data files that you save to external storage are public and world-readable. You can't use a content provider to restrict access to files in external storage, because other applications can use other API calls to read and write them.
  • The method calls for opening or creating files or SQLite databases on your device's internal storage can potentially give both read and write access to all other applications. If you use an internal file or database as your provider's repository, and you give it "world-readable" or "world-writeable" access, the permissions you set for your provider in its manifest won't protect your data. The default access for files and databases in internal storage is "private", and for your provider's repository you shouldn't change this.

If you want to use content provider permissions to control access to your data, then you should store your data in internal files, SQLite databases, or the "cloud" (for example, on a remote server), and you should keep files and databases private to your application.

Implementing permissions

All applications can read from or write to your provider, even if the underlying data is private, because by default your provider does not have permissions set. To change this, set permissions for your provider in your manifest file, using attributes or child elements of the <provider> element. You can set permissions that apply to the entire provider, or to certain tables, or even to certain records, or all three.

You define permissions for your provider with one or more <permission> elements in your manifest file. To make the permission unique to your provider, use Java-style scoping for the android:name attribute. For example, name the read permission com.example.app.provider.permission.READ_PROVIDER.

The following list describes the scope of provider permissions, starting with the permissions that apply to the entire provider and then becoming more fine-grained. More fine-grained permissions take precedence over ones with larger scope:

Single read-write provider-level permission
One permission that controls both read and write access to the entire provider, specified with the android:permission attribute of the  <provider> element.
Separate read and write provider-level permission
A read permission and a write permission for the entire provider. You specify them with the android:readPermission and  android:writePermission attributes of the  <provider> element. They take precedence over the permission required by  android:permission.
Path-level permission
Read, write, or read/write permission for a content URI in your provider. You specify each URI you want to control with a  <path-permission> child element of the  <provider> element. For each content URI you specify, you can specify a read/write permission, a read permission, or a write permission, or all three. The read and write permissions take precedence over the read/write permission. Also, path-level permission takes precedence over provider-level permissions.
Temporary permission
A permission level that grants temporary access to an application, even if the application doesn't have the permissions that are normally required. The temporary access feature reduces the number of permissions an application has to request in its manifest. When you turn on temporary permissions, the only applications that need "permanent" permissions for your provider are ones that continually access all your data.

Consider the permissions you need to implement an email provider and app, when you want to allow an outside image viewer application to display photo attachments from your provider. To give the image viewer the necessary access without requiring permissions, set up temporary permissions for content URIs for photos. Design your email app so that when the user wants to display a photo, the app sends an intent containing the photo's content URI and permission flags to the image viewer. The image viewer can then query your email provider to retrieve the photo, even though the viewer doesn't have the normal read permission for your provider.

To turn on temporary permissions, either set the android:grantUriPermissions attribute of the<provider> element, or add one or more <grant-uri-permission> child elements to your <provider>element. If you use temporary permissions, you have to call Context.revokeUriPermission() whenever you remove support for a content URI from your provider, and the content URI is associated with a temporary permission.

The attribute's value determines how much of your provider is made accessible. If the attribute is set totrue, then the system will grant temporary permission to your entire provider, overriding any other permissions that are required by your provider-level or path-level permissions.

If this flag is set to false, then you must add <grant-uri-permission> child elements to your<provider> element. Each child element specifies the content URI or URIs for which temporary access is granted.

To delegate temporary access to an application, an intent must contain theFLAG_GRANT_READ_URI_PERMISSION or the FLAG_GRANT_WRITE_URI_PERMISSION flags, or both. These are set with the setFlags() method.

If the android:grantUriPermissions attribute is not present, it's assumed to be false.

The <provider> Element


Like Activity and Service components, a subclass of ContentProvider must be defined in the manifest file for its application, using the <provider> element. The Android system gets the following information from the element:

Authority ( android:authorities)
Symbolic names that identify the entire provider within the system. This attribute is described in more detail in the section  Designing Content URIs.
Provider class name (  android:name )
The class that implements  ContentProvider. This class is described in more detail in the section Implementing the ContentProvider Class.
Permissions
Attributes that specify the permissions that other applications must have in order to access the provider's data:

Permissions and their corresponding attributes are described in more detail in the section Implementing Content Provider Permissions.

Startup and control attributes
These attributes determine how and when the Android system starts the provider, the process characteristics of the provider, and other run-time settings:
  • android:enabled: Flag allowing the system to start the provider.
  • android:exported: Flag allowing other applications to use this provider.
  • android:initOrder: The order in which this provider should be started, relative to other providers in the same process.
  • android:multiProcess: Flag allowing the system to start the provider in the same process as the calling client.
  • android:process: The name of the process in which the provider should run.
  • android:syncable: Flag indicating that the provider's data is to be sync'ed with data on a server.

The attributes are fully documented in the dev guide topic for the <provider> element.

Informational attributes
An optional icon and label for the provider:
  • android:icon: A drawable resource containing an icon for the provider. The icon appears next to the provider's label in the list of apps in Settings > Apps > All.
  • android:label: An informational label describing the provider or its data, or both. The label appears in the list of apps in Settings > Apps > All.

The attributes are fully documented in the dev guide topic for the <provider> element.

Intents and Data Access


Applications can access a content provider indirectly with an Intent. The application does not call any of the methods of ContentResolver or ContentProvider. Instead, it sends an intent that starts an activity, which is often part of the provider's own application. The destination activity is in charge of retrieving and displaying the data in its UI. Depending on the action in the intent, the destination activity may also prompt the user to make modifications to the provider's data. An intent may also contain "extras" data that the destination activity displays in the UI; the user then has the option of changing this data before using it to modify the data in the provider.

You may want to use intent access to help ensure data integrity. Your provider may depend on having data inserted, updated, and deleted according to strictly defined business logic. If this is the case, allowing other applications to directly modify your data may lead to invalid data. If you want developers to use intent access, be sure to document it thoroughly. Explain to them why intent access using your own application's UI is better than trying to modify the data with their code.

Handling an incoming intent that wishes to modify your provider's data is no different from handling other intents. You can learn more about using intents by reading the topic Intents and Intent Filters.


内容提供者管理存取数据的中央存储库。您可以实现供应商为一个或多个类别的Android应用程序,并在清单文件中的元素一起。你的一个类实现了一个子类 ContentProvider的,这是你的供应商和其他应用程序之间的接口。尽管内容提供商,目的是使数据提供给其他应用程序,你当然可以在你的应用程序的活动,允许用户查询和修改由服务提供商管理的数据。

本主题的其余部分是用于构建一个内容提供商和API使用列表步骤的基本清单。

开始之前大厦


你开始建立一个供应商之前,请执行以下操作:

  1. 如果你需要一个内容提供商决定。你需要,如果你想提供以下一项或多项特征构建一个内容提供商:
    • 要复杂的数据或文件提供给其它应用程序。
    • 要允许用户在复杂的数据,从您的应用程序复制到其他应用程序。
    • 你要提供使用搜索架构自定义搜索建议。

    不要需要一个供应商,如果使用完全是你自己的应用程序中使用SQLite数据库。

  2. 如果你还没有这样做的话,阅读主题 内容提供商基础知识更多地了解供应商。

接下来,请按照下列步骤来建立你的供应商:

  1. 设计为您的数据的原始存储。内容提供者通过两种方式提供数据:
    文件数据
    数据通常进入文件,如照片,音频或视频。存储在应用程序的私人空间中的文件。在响应于来自其他应用程序的文件的请求时,你的提供者可以提供的句柄文件。
    “结构化”数据
    数据通常进入一个数据库,数组或类似的结构。在形式与行和列的表格兼容的存储数据。中的一行表示一个实体,如一个人或在清单中的项目。列表示实体的一些数据,比如一个人的姓名或项目的价格。一种常见的方式来存储这类数据是在SQLite数据库,但你可以使用任何类型的持久性存储。要了解更多关于存储类型可供选择Android系统,请参见  设计数据存储
  2. 定义一个具体实现的ContentProvider的类及其所需的方法。这个类是你的数据和Android系统的其余部分之间的接口。有关此类的更多信息,请参见 实现ContentProvider类
  3. 定义供应商的授权字符串,其内容的URI和列名。如果您希望供应商的应用程序来处理意向,还定义意图的行动,额外的数据和标志。还定义您将需要要访问您的数据应用程序的权限。你应该考虑定义所有这些值作为一个单独的合同类常量; 以后,你可以将这个类给其他开发商。有关内容的URI的更多信息,请参见设计内容的URI。关于意图的更多信息,请参见意图和数据访问
  4. 添加其它可选件,如样本数据或实现AbstractThreadedSyncAdapter可同步提供商和基于云的数据之间的数据。

设计数据存储


内容提供商是界面保存在一个结构化的格式的数据。在创建界面之前,你必须决定如何存储数据。可以存储在你喜欢的任何形式的数据,然后设计读取和将数据作为必要写的接口。

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

  • Android系统,包括Android的自身提供商使用存储面向表的数据的SQLite数据库API。该 SQLiteOpenHelper类帮助您创建数据库以及 SQLiteDatabase类是访问数据库的基类。

    请记住,你不必使用数据库来实现你的资料库。一个提供外部显示为一组表,类似于一个关系数据库,但是这不是对提供者的内部实现的要求。

  • 为了存储文件数据,Android有多种面向文件的的API。要了解更多有关文件存储,阅读主题 数据存储。如果你正在设计一个供应商,提供媒体相关数据,如音乐或视频,你可以结合表数据和文件的供应商。
  • 对于基于网络的数据时,使用类java.net和 android.net。还可以同步基于网络的数据到本地数据存储,例如数据库,然后提供该数据作为表或文件。该 样品同步适配器示例应用程序演示了这种类型的同步。

数据设计注意事项

这里有一些提示设计提供商的数据结构:

  • 表中的数据应该始终有一个供应商保持为每个行的唯一数值的“主键”一栏。您可以使用此值的行链接到其他表中相关行(使用它作为“外键”)。尽管可以为此列以任何名义,使用BaseColumns._ID是最好的选择,因为连接的提供者查询到的结果 ListView的要求取回一列有姓名 _ID
  • 如果你想提供的位图图像或其他非常大块面向文件的数据,数据存储在一个文件,然后为其提供间接而不是直接在表中存储它。如果你这样做,你需要告诉您的供应商的用户,他们需要使用 ContentResolver的文件的方法来访问数据。
  • 使用二进制大对象(BLOB)数据类型来存储在尺寸变化或具有变化的结构的数据。例如,你可以使用一个BLOB列来存储 协议缓冲区或 JSON结构

    你也可以使用一个BLOB实现架构的独立表。在这种类型的表,可以定义主键列,MIME类型列,以及一个或多个通用列作为BLOB。在BLOB列中的数据的含义是由MIME类型列中的值表示。这使您可以存储不同行类型在同一个表。联系人提供商的“数据”表 ContactsContract.Data是一个模式独立表的一个例子。

设计内容的URI


一个内容URI是一个URI,在供应商标识数据。内容的URI包括整个提供商(其的符号名称权威)和指向一个表或文件(名称路径)。可选的ID部分指向表中的一个单独的行。每个数据访问方法 的ContentProvider有一个内容URI作为参数; 这使您可以确定表,行或文件来访问。

内容URI的基础知识中的主题描述 内容提供商基础

设计一个权威

提供者通常有一个单一的机构,作为其Android平台的内部名称。为了避免与其他供应商的冲突,你应该使用互联网域名所有权(反向)作为您的供应商权力的基础。因为这个建议也为Android包名真的,你可以定义你的供应商权威包含提供包的名称的扩展。例如,如果你的Android包名称是 com。示例。<应用程序名称>,你应该给你的供应商的授权com。示例。<APPNAME> .provider

设计路径结构

开发人员通常通过追加指向单个表的路径创建从权威内容的URI。例如,如果你有两个表table1中和 表2,您从前面的例子相结合的权力产生的URI内容 com。示例。<应用程序名称> .provider /表1和 com。示例。<应用程序名称> .provider /表2。路径不局限于单一的段,并有不必须为路径中的每个级别的表。

处理内容URI标识

按照惯例,提供者通过接受内容的URI与在URI的末尾行的ID值提供访问单个行中的一个表。同样按照约定,供应商ID值表的匹配_ID列,并执行对符合该行的请求的访问。

该公约为便于访问的应用程序供应商共同设计模式。该应用程序确实对提供查询并显示结果光标 在ListView中使用的CursorAdapter。的定义的CursorAdapter要求在一列的 光标_id

用户然后拾取为了看或修改数据从UI的显示的行中的一个。该应用程序从获得相应的行光标来头 的ListView,获取_ID值此行,就追加内容URI,并发送访问请求提供商。然后,供应商可以做对的确切行的用户选择了查询或修改。

内容URI模式

为了帮助您选择乘坐传入内容URI该动作,提供API包括方便的类UriMatcher,它映射内容URI“模式”为整数值。您可以在使用整数值开关的选择的内容一个或多个URI匹配特定模式所需的操作说明。

A含量URI模式匹配使用通配符内容的URI:

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

由于设计和编码内容URI处理的一个例子,考虑与管理局提供com.example.app.provider识别指着桌子下面内容的URI:

  • 内容://com.example.app.provider/table1:所谓的A表table1的
  • 内容://com.example.app.provider/table2/dataset1:所谓的A表 数据集1
  • 内容://com.example.app.provider/table2/dataset2:所谓的表 dataset2
  • 内容://com.example.app.provider/table3:所谓的表表3

如果他们有一个行ID附加到它们,例如在提供者也认识到这些内容的URI//com.example.app.provider/table3/1:含量为所确定的行 1表3

下面的内容URI模式是可能的:

内容://com.example.app.provider/*
匹配供应商的任何内​​容URI。
内容://com.example.app.provider/table2/*
匹配的表的内容URI  数据集1 和 dataset2,但不匹配内容的URI  表1和  表3
内容://com.example.app.provider/table3/#:比赛中单行的内容URI  表3,如  内容://com.example.app.provider/table3/6由标识的行  6

下面的代码片段展示了如何方法UriMatcher工作。此代码的URI处理的URI整个表为不同的单排,用内容URI模式的内容:// <授权> / <路径>为表, 内容:// <授权> / <路径> / < ID>单行。

该方法addURI()映射的权威和路径的整数值。该方法匹配()返回一个URI整数值。一个开关查询整个表,和查询单条记录的声明选择:

公共  ExampleProvider  扩展 ContentProvider的 { 
... 
    //创建一个UriMatcher对象。
    私人 静态 最终 UriMatcher sUriMatcher ; 
... 
    / * 
     *为addURI()调用去这里,为所有供应商的内容URI模式的
     *应该承认。对于本段中,只示出了表3中的呼叫。
     * / 
... 
    / * 
     *设定为表3中的多个行到1。请注意,没有通配符用于整数值
     *在路径
     * / 
    sUriMatcher addURI “com.example.app.provider” ,“表3”   1 ); 

    / * 
     *设定为单行至2的代码。在这种情况下,该“#”通配符
     *使用。“内容://com.example.app.provider/table3/3”匹配,但
     *“的内容://com.example.app.provider/table3没有。
     * / 
    sUriMatcher addURI ”com。示例。 app.provider“ ,”表3 /#“   2 ); 
... 
    //器具 
     
        
        
        
        
         

        
         选择查询表,并根据该代码返回的呼入排序顺序
         *的URI。在这里,只对表3中的语句被示出。
         * / 
        开关 sUriMatcher 匹配URI )) { 


            //如果传入URI是所有表3的
            情况下, 1 

                如果 文本实用程序的isEmpty 中将sortOrder ))中将sortOrder =  “_ID ASC” ; 
                打破; 

            //如果输入的URI是一个单行
            的情况下 2 

                / * 
                 *因为这个URI是一个单列的_ID值部分
                 *目前获得来自URI的最后一个路径段;这是_ID值
                 *然后,附加价值的WHERE子句查询
                 * / 
                选择= 选择+  “_ID =” URI getLastPathSegment (); 
                打破; 

            默认
            ... 
                //如果URI不认可,你应该做一些错误处理在这里。
        } 
        //调用实际做查询代码
    }

另一类,ContentUris,提供了与工作方便的方法ID内容的URI的一部分。类乌里和 Uri.Builder包括解析现有的便捷方法 乌里对象,并建立新的。

实施ContentProvider类


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

所需的方法

抽象类ContentProvider的定义,必须实现作为自己的具体子类的第六部分抽象方法。所有这些方法除 的onCreate()是由正试图访问您的内容提供商的客户端应用程序调用:

查询()
从您的提供商获取数据。使用参数选择表进行查询,行和列返回,结果的排序顺序。返回的数据作为一个 游标对象。
插()
插入新行到您的供应商。使用参数选择目标表,并获取列值使用。返回内容URI为新插入的行。
更新()
在您的供应商更新现有行。使用参数选择表和行更新,并获得更新的列值。返回更新的行数。
删除()
从您的提供商删除行。使用参数选择表和要删除的行。返回删除的行数。
的getType()
返回对应于内容的URI的MIME类型。此方法进行更详细的部分中所述 实施内容提供商MIME类型
的onCreate()
初始化您的供应商。Android系统调用这个方法后立即创建您的供应商。请注意,不会创建你的供应商,直到  ContentResolver的对象试图访问它。

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

你的实现这些方法应该考虑以下几点:

  • 所有这些方法除的onCreate() 可以由多个线程同时被调用,所以它们必须是线程安全的。要了解更多关于多线程,请参阅 进程和线程
  • 避免在做冗长的操作的onCreate() 。延迟初始化任务,直到他们真正需要。本节实施onCreate()方法 更加详细地讨论这一点。
  • 尽管必须实现这些方法,你的代码没有做,除了返回预期的数据类型什么。例如,您可能要防止其他应用程序将数据插入到一些表。要做到这一点,你可以忽略调用 插入() ,并返回0。

实施query()方法

该 ContentProvider.query()方法必须返回一个游标对象,如果失败,抛出一个异常。如果您正在使用的SQLite数据库作为数据存储,您可以简单地返回光标由一个返回 查询()的方法SQLiteDatabase类。如果查询不匹配任何行,你应该返回一个游标 实例,其getCount将()方法返回0。您应该返回空值仅如果在查询过程中发生内部错误。

如果不使用SQLite数据库作为数据存储,使用的具体子类的一个光标。例如,MatrixCursor类实现一个光标,其中每一行是一个数组对象。有了这个类,使用addRow()来添加一个新行。

请记住,Android系统必须能够沟通的异常 跨进程边界。Android版 ​​可以为以下例外情况,可能是在处理查询错误有用做到这一点:

实施insert()方法

插件()方法添加一个新行到相应的表,可以使用在值ContentValues ​​说法。如果列名是不是在ContentValues ​​说法,您可能希望无论是在你的供应商代码或数据库模式为它提供一个默认值。

此方法应返回内容的URI新行。构造此,追加新行的_ID(或其他主键)值表中的内容URI,使用withAppendedId() 

实施delete()方法

delete()的方法没有实际从您的数据存储中删除行。如果您使用的是与你的供应商同步适配器,你应该考虑标记删除的行以“删除”标志,而不是删除的行。同步适配器可以检查删除的行,并从供应商删除之前从服务器上删除它们。

实施update()方法

更新()方法,采用相同的ContentValues ​​所用参数 )插入(和相同的选择selectionArgs两个被使用的参数 删除()和 ()ContentProvider.query。这可能让你重新使用这些方法之间的代码。

实施onCreate()方法

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

例如,如果您使用的是SQLite数据库,你可以创建一个新的SQLiteOpenHelper对象ContentProvider.onCreate() ,然后再创建SQL表你第一次打开数据库。为了推动这项工作,你第一次调用getWritableDatabase() ,它会自动调用 SQLiteOpenHelper.onCreate()方法。

下面的两个片段演示之间的相互作用 ContentProvider.onCreate()和 SQLiteOpenHelper.onCreate() 。第一个片段是实施 ContentProvider.onCreate() 

公共  ExampleProvider  扩展 的ContentProvider 

    / * 
     *定义一个处理数据库辅助对象。该MainDatabaseHelper类定义
     *在下面的代码片段。
     * / 
    私人 MainDatabaseHelper mOpenHelper ; 

    //定义数据库名称
    专用 静态 最终 字符串DBNAME =  “mydb的” ; 

    //保存数据库对象
    的私人 SQLiteDatabase 分贝; 

    公共 布尔的onCreate () { 

        / * 
         *创建一个新的辅助对象。此方法总是迅速恢复。
         *请注意,数据库本身不会创建或打开
         *,直至SQLiteOpenHelper.getWritableDatabase被称为
         * / 
        mOpenHelper =   MainDatabaseHelper 
            的getContext ()        //应用程序上下文
            DBNAME               //数据库的名称)
                            //使用缺省的SQLite光标
            1                     //版本号
        ); 

        返回 ; 
    } 

    ... 

    //实现了供应商的插入方法
    公开 光标插入开放的URI  ContentValues ​​值 { 
        //此处插入代码,以确定该表以打开,处理错误检查,等等

        ... 

        / * 
         *获取的可写数据库。如果它不存在,这将触发其创建
         * 
         * / 
        分贝= mOpenHelper getWritableDatabase (); 
    } 
}

接下来的片段是实施 SQLiteOpenHelper.onCreate() ,其中包括一个辅助类:

... 
//定义的SQL语句创建一个表中的字符串
私人 静态 最终 字符串SQL_CREATE_MAIN =  “CREATE TABLE”  + 
    “主”  +                        //表的名字
    “(”  +                            //在表中的列
    “_ID INTEGER PRIMARY KEY,“  + 
    ”字文“ 
    ”FREQUENCY INTEGER“  + 
    ”LOCALE文本)“ ; 
...... 
/ ** 
 。实际上创建和管理服务提供商的基础数据资源库* Helper类
 * / 
保护 静态 最后  MainDatabaseHelper  扩展 SQLiteOpenHelper  { 

    / * 
     *实例化的供应商的SQLite的数据存储库,一个开放的帮手
     *不要做数据库的建立和升级在这里。
     * / 
    MainDatabaseHelper 上下文语境 { 
        背景下DBNAME   1 ); 
    } 

    / * 
     *创建的数据存储库。这就是所谓的当供应商试图打开
     *库和SQLite报告说,它不存在。
     * / 
    公共 无效的onCreate SQLiteDatabase DB  { 

        //创建主表
        分贝execSQL SQL_CREATE_MAIN ); 
    } 
}

实现ContentProvider的MIME类型


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

的getType()
对,你必须为所有供应商实施所需的方法。
getStreamTypes()
您所期望实现,如果你的供应商提供文件的方法。

MIME类型的表

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

对于常见的类型,如文本,HTML,或JPEG数据 的getType()应返回标准MIME类型的数据。这些标准类型的完整列表,请在 IANA MIME媒体类型 的网站。

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

  • 一部分:VND
  • 亚型部分:
    • 如果URI模式是单个行:。android.cursor  /
    • 如果URI图案为多个行:。android.cursor DIR /
  • 供应商特定的部分:。越南盾<名><类型>

    您提供的<名><类型>。在<名称>值应该是全球唯一的,并且<类型>值应该是唯一的,以相应的URI图案。对于一个很好的选择<名>为您的公司名称或您的应用程序的Android包名的某些部分。为一个很好的选择<类型>是标识与URI关联的表中的字符串。

例如,如果一个供应商的权威 com.example.app.provider,它暴露了一个表名为 table1的,的MIME类型多行的表1是:

越南盾机器人光标DIR / 越南盾的COM 例如供应商表1

对于单排表1,MIME类型为:

越南盾机器人光标项目 / 越南盾的COM 例如供应商表1

MIME类型的文件

如果您的供应商提供的文件,实施 getStreamTypes() 。该方法返回一个字符串 MIME类型,为您的供应商可以返回一个给定的内容URI文件数组。你应该过滤你的MIME类型过滤器参数报价MIME类型,让你只返回客户端要处理这些MIME类型。

例如,考虑一个供应商,提供的照片图像,文件.JPG, .PNG.gif注意格式。如果一个应用程序调用ContentResolver.getStreamTypes()与过滤字符串图像/ *(东西是一个“形象”),那么ContentProvider.getStreamTypes()方法应该返回数组:

{  “图像/ JPEG” ,“图像/ PNG” ,  “图像/ GIF”  }

如果应用程序是只关心.JPG文件,那么它可以调用 ContentResolver.getStreamTypes()与过滤字符串* \ / JPEG和 ContentProvider.getStreamTypes()应该返回:

{ “图像/ JPEG” }

如果您的供应商不提供任何过滤字符串中所要求的MIME类型, getStreamTypes() 应返回空值

实施合同类


合同类是公共最后,它包含的URI,涉及到供应商常量定义,列名,MIME类型和其他元数据类。类建立通过确保提供商可正确访问即使有变化的URI的实际值,列名等等的提供者和其他应用之间的契约。

合同类还可以帮助开发人员,因为它通常具有其常量记忆的名字,所以开发商不太可能使用不正确的值列名或URI的。因为它是一个类,它可以包含Javadoc文档。集成开发环境如Android Studio可以从合同类和显示Javadoc文档的常量自动完成常量名。

开发商不能从您的应用程序访问合同类的类文件,但他们可以在静态编译到他们的应用程序从的.jar您提供的文件。

ContactsContract类及其嵌套类合同的类的实例。

实施内容提供商权限


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

  • 默认情况下,存储在设备的内部存储数据文​​件仅对您的应用程序和供应商。
  • SQLiteDatabase你创建数据库仅对您的应用程序和供应商。
  • 默认情况下,您保存到外部存储数据文件的公共和 世界可读。不能使用内容提供商,以限制访问外部存储的文件,因为其他应用程序可以使用其他的API调用来读,写他们。
  • 该方法要求打开或对你的设备的内部存储创建文件或SQLite数据库有可能给读写访问所有其他应用程序。如果你使用一个内部文件或数据库作为供应商的资料库,你给它的“世界可读”或“世界可写”的访问,您对在其清单提供商不会保护你的数据设置的权限。在内部存储的文件和数据库的默认访问是“私人”,并为您的供应商的资料库,你不应该改变这一点。

如果您想使用内容提供商权限来控制访问您的数据,那么你应该存储你的数据在内部文件,SQLite数据库,或者“云”(例如,在远程服务器上),你应该保持文件和数据库私有应用程序。

实施权限

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

你定义一个或多个提供商权限 <权限>在manifest文件的元素。为了使权限独特的提供商,使用Java风格的范围界定为 机器人:名称属性。例如,命名读取权限 com.example.app.provider.permission.READ_PROVIDER

下面的列表说明提供者的权限范围,从应用到整个提供商,然后变得更加细粒度的权限。更细粒度的权限优先于那些具有较大的适用范围:

单个读写提供商级别权限
一个权限同时控制读写访问到整个供应商,指定  的android:许可的属性  <提供商>元素。
单独的读写提供商级别权限
读权限和整个提供商写权限。你与他们指定  的android:readPermission和  安卓writePermission的属性  <提供商>元素。他们优先通过所需的权限  安卓权限
路径级别权限
读,写或读/写权限在你的提供者的内容URI。您可以指定想用控制每个URI  <路径许可>的子元素  <提供商>元素。对于每个指定的内容URI,可以指定一个读/写权限,读权限或写权限,或所有三种。在读取和写入权限优先于读/写权限。此外,道路级别权限接管供应商级别的权限优先。
临时许可
权限级别授予给应用程序的临时访问,即使该应用程序不具有通常所需的权限。临时访问功能减少权限的应用程序在其清单,要求的数量。当您打开临时权限,需要为您提供“永久”权限的唯一的应用程序是那些不断地访问所有数据。

想想你需要实现电子邮件提供商和应用程序的权限,当你希望允许外部图像浏览器应用程序从您的供应商显示照片附件。为了让图像浏览器,而无需权限必要的访问,内容的URI照片设立临时权限。设计你的电子邮件应用程序,以便当用户想要显示的照片,应用程序发送包含照片的内容URI和权限标志的图像浏览器的意图。然后,图像浏览器可以查询您的电子邮件提供商来获取照片,即使观众没有为您提供正常的读取权限。

要打开临时权限,要么设置 的android:grantUriPermissions的属性 <提供商>元素,或添加一个或多个<赠款URI的权限>子元素的 <提供商>元素。如果您使用临时权限,则必须调用Context.revokeUriPermission()每当你删除从您的提供者的内容URI的支持,以及内容URI与临时许可有关。

属性值的多少决定你的供应商是进行访问。如果该属性设置为,那么系统会给予临时许可,您的整个供应商,覆盖由您的提供商级或路径级别权限所需的任何其他权限。

如果该标志设置为,那么你必须添加 <赠款URI的权限>子元素的 <提供商>元素。每个子元素指定内容的一个或多个URI为其临时访问是理所当然 ​​的。

委托给一个应用程序的临时访问,意图必须包含FLAG_GRANT_READ_URI_PERMISSIONFLAG_GRANT_WRITE_URI_PERMISSION标志,或两者。这些设置与setFlags()方法。

如果 机器人:grantUriPermissions属性不存在,它的假设是 错误的

在<provider>元素


活动服务组件的子类的ContentProvider 必须在其应用程序清单文件中定义,使用 <提供商>元素。Android系统会从该元素的以下信息:

管理局( 安卓当局
符号名标识系统内的整个提供商。此特性进行更详细的部分中所述  的设计内容的URI
提供类名( 安卓名) 
实现类 的ContentProvider。这个类中有更详细的部分中所述  实施的ContentProvider类
权限
属性指定的权限的其他应用程序必须有为了访问提供者的数据:

权限和它们的相应的属性进行更详细的部分中所述 实施内容提供商权限

启动和控制属性
这些属性决定当Android系统启动供应商,供应商的工艺特点,以及其他运行时设置和如何:

该属性的开发者指南主题完全记录 <提供商> 元素。

信息属性
可选的图标和标签的供应商:
  • 安卓图标:包含供应商图标的绘制资源。旁边的提供商的标签出现在应用程序列表中的图标 设置 > 应用程序 > 所有
  • 机器人:标签:信息性 ​​标签说明提供者或它的数据,或两者兼而有之。该标签将出现在应用程序列表设置 > 应用程序 > 所有

该属性的开发者指南主题完全记录 <提供商>元素。

意图和数据访问


应用程序可以与一个间接访问内容提供商意图。应用程序不调用任何方法ContentResolver的ContentProvider的。相反,它发送一个启动活动,这往往是提供者的自己的应用程序的一部分的意图。目的地活性负责检索和在其UI显示数据。取决于意图的动作,在目标的活动也可提示用户进行修改,以提供者的数据。意图也可能含有“额外”的数据,在UI目标的活动显示; 然后用户具有使用它来 ​​修改在提供数据之前,改变该数据的选项。

您可能需要使用意图取用,以帮助确保数据的完整性。您的供应商可能会依赖于数据的插入,更新和删除按照严格定义的业务逻辑有。如果是这种情况,允许其他应用程序直接修改您的数据可能会导致无效的数据。如果你想开发者使用意向访问,一定要彻底将其记录下来。使用自己的应用程序的用户界面是不是试图用自己的代码来修改数据更好地解释他们为什么意图取用。

处理传入的意图希望修改您的供应商的数据无异于处理其他的意图不同。您可以通过阅读的话题更多地了解使用意图 意图和意图过滤器


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值