ContentProvider官方教程(9)定义一个provider完整示例:实现方法,定义权限等

Creating a Content Provider

In this document

  Designing Data Storage
  Designing Content URIs
  Implementing the ContentProvider Class
  Required Methods
  Implementing the query() method
  Implementing the insert() method
  Implementing the delete() method
  Implementing the update() method
  Implementing the onCreate() method

  Implementing Content Provider MIME Types 
  MIME types for tables
  MIME types for files
  Implementing a Contract Class
  Implementing Content Provider Permissions
  The <provider> Element  在这里
  Intents and Data Access

Key classes
  ContentProvider
  Cursor
  Uri
  Related Samples
  Note Pad sample application

See also
  Content Provider Basics
  Calendar 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 subclass ContentProvider, 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 请考虑下面几点再定义一个provider

  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: 

  定义provider的步骤大概如下:

  1. Design the raw storage for your data. A content provider offers data in two ways: 

  第1步:字义数据源,用文件还是数据库存数据。
    • 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.

  第2步:定义ContentProvider子类,并实现其中的方法。

  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.

  第3步:定义字段类,定义URI权限等。

  4. Add other optional pieces, such as sample data or an implementation of AbstractThreadedSyncAdapter that can synchronize data between the provider and cloud-based data.

  第4步:定义AbstractThreadedSyncAdapter,它可以同步云端数据。

 

Designing Data Storage 第1步:定义数据源


  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:

  下面是常见的给provider提供数据的方式。
  • 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 topic Data 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.
    文件存储.详见:Data Storage
  • 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. The Sample Sync Adapter sample application demonstrates this type of synchronization.
    网络存储。Sync Adapter sample有相关示例。

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.
    注意这点,如果作sdk时,可能用到,

   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 定义数据的URI


  A 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 is com.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 在URI中传递_ID

  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 a switch 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:

  URI中支持的通配符
  • *: 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 authority com.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 example content://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. 

  URI的常见格式: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:

 1 public class ExampleProvider extends ContentProvider {
 2 ...
 3     // Creates a UriMatcher object.
 4     private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 5 
 6     static {
 7         /*
 8          * The calls to addURI() go here, for all of the content URI patterns that the provider
 9          * should recognize. For this snippet, only the calls for table 3 are shown.
10          */
11 
12         /*
13          * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used
14          * in the path
15          */
16         sUriMatcher.addURI("com.example.app.provider", "table3", 1);
17 
18         /*
19          * Sets the code for a single row to 2. In this case, the "#" wildcard is
20          * used. "content://com.example.app.provider/table3/3" matches, but
21          * "content://com.example.app.provider/table3 doesn't.
22          */
23         sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);
24     }
25 ...
26     // Implements ContentProvider.query()
27     public Cursor query(
28         Uri uri,
29         String[] projection,
30         String selection,
31         String[] selectionArgs,
32         String sortOrder) {
33 ...
34         /*
35          * Choose the table to query and a sort order based on the code returned for the incoming
36          * URI. Here, too, only the statements for table 3 are shown.
37          */
38         switch (sUriMatcher.match(uri)) {
39 
40 
41             // If the incoming URI was for all of table3
42             case 1:
43 
44                 if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
45                 break;
46 
47             // If the incoming URI was for a single row
48             case 2:
49 
50                 /*
51                  * Because this URI was for a single row, the _ID value part is
52                  * present. Get the last path segment from the URI; this is the _ID value.
53                  * Then, append the value to the WHERE clause for the query
54                  */
55                 selection = selection + "_ID = " uri.getLastPathSegment();
56                 break;
57 
58             default:
59             ...
60                 // If the URI is not recognized, you should do some error handling here.
61         }
62         // call the code to actually do the query
63     }

  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 第2步:定义provider子类并实现相应方法 


  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 of ContentProvider 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.
    onCreate必需线程安全,它随时可能被多个线程创建。
  • 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.
    onCreate还能执行时间过长。
  • 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 the query() methods of the SQLiteDatabase class. If the query does not match any rows, you should return a Cursor 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:

  • IllegalArgumentException (You may choose to throw this if your provider receives an invalid content URI)
  • NullPointerException

Implementing the insert() method 实现插入方法

  The insert() method adds a new row to the appropriate table, using the values in the ContentValues argument. 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. 

  注意:通常在这里可以不真正的删除数据,可以只把数据标为已删除状态,sync adapter就是这么做的,后台数据库才是真正的删除。

Implementing the update() method 实现更新方法

  The update() method takes the same ContentValues argument used by insert(), and the same selection 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 in ContentProvider.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() and SQLiteOpenHelper.onCreate(). The first snippet is the implementation of ContentProvider.onCreate():onCreate示例:

 1 public class ExampleProvider extends ContentProvider
 2 
 3     /*
 4      * Defines a handle to the database helper object. The MainDatabaseHelper class is defined
 5      * in a following snippet.
 6      */
 7     private MainDatabaseHelper mOpenHelper;
 8 
 9     // Defines the database name
10     private static final String DBNAME = "mydb";
11 
12     // Holds the database object
13     private SQLiteDatabase db;
14 
15     public boolean onCreate() {
16 
17         /*
18          * Creates a new helper object. This method always returns quickly.
19          * Notice that the database itself isn't created or opened
20          * until SQLiteOpenHelper.getWritableDatabase is called
21          */
22         mOpenHelper = new MainDatabaseHelper(
23             getContext(),        // the application context
24             DBNAME,              // the name of the database)
25             null,                // uses the default SQLite cursor
26             1                    // the version number
27         );
28 
29         return true;
30     }
31 
32     ...
33 
34     // Implements the provider's insert method
35     public Cursor insert(Uri uri, ContentValues values) {
36         // Insert code here to determine which table to open, handle error-checking, and so forth
37 
38         ...
39 
40         /*
41          * Gets a writeable database. This will trigger its creation if it doesn't already exist.
42          *
43          */
44         db = mOpenHelper.getWritableDatabase();
45     }
46 }


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

 1 ...
 2 // A string that defines the SQL statement for creating a table
 3 private static final String SQL_CREATE_MAIN = "CREATE TABLE " +
 4     "main " +                       // Table's name
 5     "(" +                           // The columns in the table
 6     " _ID INTEGER PRIMARY KEY, " +
 7     " WORD TEXT"
 8     " FREQUENCY INTEGER " +
 9     " LOCALE TEXT )";
10 ...
11 /**
12  * Helper class that actually creates and manages the provider's underlying data repository.
13  */
14 protected static final class MainDatabaseHelper extends SQLiteOpenHelper {
15 
16     /*
17      * Instantiates an open helper for the provider's SQLite data repository
18      * Do not do database creation and upgrade here.
19      */
20     MainDatabaseHelper(Context context) {
21         super(context, DBNAME, null, 1);
22     }
23 
24     /*
25      * Creates the data repository. This is called when the provider attempts to open the
26      * repository and SQLite reports that it doesn't exist.
27      */
28     public void onCreate(SQLiteDatabase db) {
29 
30         // Creates the main table
31         db.execSQL(SQL_CREATE_MAIN);
32     }
33 }

 

Implementing ContentProvider MIME Types 实现getType()、getStreamTypes()

  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 表格的MIME type

  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 文件的MIME type

  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.

  默认返回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  如何在manifest.xml中定义权限

  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. 

  可在manifest.xml中设置 整个provider、某个表、某一条记录的访问权限。用<provider>的子元素<permission>。


  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 定义整个provider的同时读写级权限。

    One permission that controls both read and write access to the entire provider, specified with the android:permission attribute of the <provider> element. 

  用<provider android:permission=xxx >...
  • 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.

  使用<provider android:readPermission=xxx >...只有读权限。
  • 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. 

  用<path-permission>...路经级权限优先与整个provider权限。
  • 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.

  provider通过返回intent的权限标记授临时权限。
  通过 <android:grantUriPermissions>属性声明临时权限,或<grant-uri-permission>子元素声明临时权限。
  通过Context.revokeUriPermission() 关闭临时权限。


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 to true, 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.

  如果android:grantUriPermissions 是false,那么只能通过grant-uri-permission>子元素声明权限。默认是false。


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

  intent.setFlags()可以设置权限标记。

 

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

 

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.

 

转载于:https://www.cnblogs.com/sjjg/p/5929462.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值