This document is translated from Android official documentation, combined with your own tests, organized as follows.
Content provider Management data access, we can implement one or more custom provider in our own application (by inheriting the abstract class ContentProvider), of course these provider need to be registered in the manifest file. Although content provider is used to access data for other programs, activities in its own program can obviously handle that data.
Considerations before creating Provider
Determine if you need to provide content provider. You need to create a content provider if you have one or more of the following requirements:
- A complex data or file that you want to provide to other programs;
- Want to allow users to copy complex data from our program to another program;
- You want to use the query framework to provide custom query suggestions.
If you are using a SQLite database inside your program, you do not need provider.
Next, create the provider by following these steps (a brief summary, followed by a detailed introduction):
For data design storage, content provider is available in two ways:
- File data
Data is saved in files, examples, videos, audio and so on. These files are stored in private spaces and provider can provide external program access.
- Structured data
Such data is usually stored in databases, arrays, or similar structures, which can, of course, be saved in a table in a compatible manner. A row in the table represents an entity (record), and a column represents the value of the entity-related property. Usually this data is stored in the SQLite database, and of course it can be saved permanently.
You need to inherit the abstract class ContentProvider and overwrite the necessary methods. This class is the interface between our data and other program interactions.
- Defines provider permissions (authority), content URIs, and column names. If the program wants to process intents, you must also define intent's action,extra data and flags. It is also necessary to define the license (permission) that other programs want to access the provider must request. It is common to consider defining these values as constants and defining them in another class.
- Add additional information.
Design data storage
Before providing provider, we have to determine how our data should be stored, and of course we can specify it, and then design the provider for that storage mode.
The following storage methods are available:
- stored in the SQLite database. In this way we do not have to use a database, provider is represented as a set of tables, so there is no need to implement the related database internally.
- stored in the file.
- stored in the network.
Precautions
- Table data typically requires a primary key, and provider assigns a unique value to each row. Although the primary key column can be any name, it is recommended
BaseColumns._ID
to use, so that the ListView can be easily retrieved.
- If a bitmap or larger file data is provided, the data will be stored in a file and provided in an indirect manner. If we use this data, we should inform the client that they should use the file method in the Contentresovler class to get the data.
- The size or structure of the stored BLOB data type can vary.
Design Content URIs
A content URI is a URI that identifies the data in the provider, including permissions (authority) and Paths (path). Permissions to find the provider, the path to find the table or file. You can also have an ID that can represent a row.
Design Permissions Authority
Permissions are used to distinguish between different programs, and are generally named in the form of package names in order to avoid conflicts. For example, if the package name is: com.example.sywyg
, the permission can be: com.example.sywyg.provider
.
Design path
The URI is a way to find the specified table by the permission plus path. Paths are distinguished from tables or other forms (such as files) in the same program, and can be added directly after permissions. For example, Table1 and table2, the resulting URIs are: com.example.sywyg.provider/table1
and com.example.sywyg.provider/table2
.
The final content URI needs to precede the permissions and paths with a content://
content URI. For example, a standard content URI is written as follows: content://com.example.sywyg.provider/table1
.
Handle the ID of the URI
Appending the ID to the URI allows you to retrieve the specified row in the table, with the ID corresponding to the column named _id.
URI Pattern Matching
The Urimatcher class maps the content URI pattern to an integer type number, which we can use to make pattern matching.
The URI pattern is matched by a wildcard character:
- *: Matches any length and valid string;
- #: Matches any length of numeric characters;
Assume that the permission is: com.example.app.provider
, identify the table that corresponds to the following URI:
content: //com Span class= "Hljs-preprocessor" >.example .app .provider /table1: Table is table1. content: //com .example .app .provider /table2/dataset1: Table is DataSet1. content: //com .example .app .provider /table2/dataset2: Table is Dataset2. content: //com .example .app .provider /table3: Table is table3.
If you have an ID, you can also identify:
content://com.example.app.provider/table3/1
Line 1th in table Table3
The following URI pattern:
content://com.example.app.provider/*
Match any URI in the provider
content://com.example.app.provider/table2/*
Will match the table DataSet1 and Dataset2, but will not match table1 or table3.
content://com.example.app.provider/table3/#
Will match any row in the Table3.
content://com.example.app.provider/table3/6
Will match the 6th row in the Table3.
In summary, the URI standard is: content:///or content:////, the former for the table, the latter for the specified row.
We can use the Urimatcher class to quickly implement a content URI match. Common code is as follows:
Inheriting the ContentProvider class
The ContentProvider class can manage the data in our provider, and the external contentresovler can invoke the corresponding ContentProvider method to implement the Operation data. Therefore, we have to provide the appropriate method to manipulate the data.
Override method
We need to implement the following methods to facilitate Contentresovler access to data.
query()
Retrieves the data and returns the cursor object.
insert()
Inserts a row of data, returning the URI of the newly inserted row.
update()
Updates a row that is present and returns the updated line number.
delete()
Deletes a row that exists, returning the deleted line number.
getType()
Returns the MIME type of the corresponding URI.
onCreate()
Initializes the provider. When the provider object is created, it is called immediately. Note the provider object is created only if the Contentresovler object attempts to access the data.
You can see methods that have the same name in Contentresovler for the above-mentioned methods of manipulating data.
There are a few things to consider when overriding a method:
- In addition
onCreate()
to the method, you should pay attention to multi-threading security issues.
- Avoid
onCreate()
long-time operations. The corresponding content is initialized until it is needed.
- Although we need to implement these methods, we don't have to do anything except the return value. For example, we do not want the outside world to delete data, so we just need to return the corresponding line number instead of writing any code in the method.
Realize
query()
Method
The method returns a cursor object, or throws an exception if it fails. If the corresponding row is not found, a getCount()
cursor object with a method of 0 should be returned. Null is returned only if an internal error occurs. If you use the SQLite database to save the data, you can call the Sqlitedatabase class query()
method to return the cursor object directly. If you do not use it, you will use the specific subclass of the cursor class.
The following exceptions may be thrown at query time:
- IllegalArgumentException (when an invalid URI is received)
- NullPointerException
If you are accessing the SQLite database, the query()
simple implementation code is as follows:
Public class exampleprovider extends contentprovider { Private Static FinalUrimatcher Surimatcher; Surimatcher.adduri ("Com.example.app.provider","Table3",1); Surimatcher.adduri ("Com.example.app.provider","table3/#",2);the//parameter for Contentresovler calls the query () method passed over the PublicCursorQuery(Uri Uri, string[] projection, string selection, string[] Selectionargs, String sortor Der) {Switch(Surimatcher.match (URI)) {//Correspondence Table3 Case 1:if(Textutils.isempty (sortOrder)) SortOrder ="_id ASC"; Break;//corresponds to Table3 with ID Case 2: Selection = Selection +"_id ="Uri.getlastpathsegment (); Break;default:Throw NewIllegalArgumentException ("Unknown URI"+ URI); }//Actual query of SQLite database statements}
Realize
insert()
Method
The Insert method adds a new row to the specified table, and the data is from the data passed by the parameter contentvalues, and if a column is not specified, the default value (which depends on the provider or the database itself) is provided.
The method returns the URI of the new row. We can withAppendId()
build the URI by Contenturis the URI by adding the primary key ID (usually _id) after the URIs. Directly through the parse()
method is also OK.
Realize
update()
Method
Similar to insert, no longer described.
Realize
delete()
Method
Deletes the specified row. This method does not need to really delete a row, if we use the synchronization adapter, we can consider the first to delete the data to mark the deletion. The sync adapter can check out the deleted rows before actually deleting the data from provider, and remove them for false deletion.
Realize
getType()
Method
will be explained in detail in the following sections.
Realize
onCreate()
Method
The system calls the method when the provider is created onCreate()
. We should ensure that the content initialized here is necessary and can be executed quickly, and that it is not necessary and time-consuming to initialize it when needed. For example, database creation and data loading can be performed when the operation data is actually requested. If it is too time-consuming, provider start-up can be time consuming, which obviously affects the program that responds to requests for that provider.
MIME type of ContentProvider
ContentProvider has two methods to return a type:
getType()
This needs to be implemented in subclasses.
getStreamTypes()
This is required if provider provides file access.
MIME type of table
getType()
method returns a MIME-formatted string that describes the data type for the parameter URI. The parameter URI can match a pattern instead of the specified URI, so we should return and match the pattern of the URIs related data type.
For the common type: Text,html,jpeg, the getType()
method should return the standard MIME type.
For URIs that specify one or more rows, the getType()
method should return the MIME format specified by Android:
- Type section: VND
- Subtype section:
- URI pattern is only one line: android.cursor.item/
- URI pattern has multiple lines: android.cursor.dir/
- Provider designation section: VND.
The name should be globally unique, and the type should be unique to the corresponding URI pattern. Typically, name should be the package name, and type should be the name of the table associated with the URI.
For example, with the provider permission of com.example.app.provider
the table named: Table1, the MIME type of the Table1 multiline is:
vnd.android.cursor.dir/vnd.com.example.provider.table1
The MIME type of a single line is:
vnd.android.cursor.item/vnd.com.example.provider.table1
MIME type of File
You need to implement the method if provider provides the file getStreamTypes()
. The method returns a MIME-type string array, based on the given URI. We should filter the MIME type based on the MIME type filter parameter to return the MIME type that the client wants to process.
For example, provider provides picture files:. jpg,.png and. gif formats. If a program calls the getStreamTypes()
image/* using the filter parameter, the method returns:
{ "image/jpeg", "image/png", "image/gif"}
If the program requires only. jpg, you can use the filter parameter *\/jpeg, then the method returns:
{"image/jpeg"}
If provider does not provide a type, the method returns NULL.
Implementing related Classes
You typically need a public final type of related class to define constants: URIs, column names, MIME, or other provider-related information. This class enables provider and other programs to establish a relationship that ensures that the provider is properly fetched.
For other programs, the associated class can be manipulated using the. jar file we provide, which enables operation provider.
Implement content provider Licensing
There are several points to note:
- By default, data files stored in device memory (not running memory) are private to our programs and provider.
- SQLite database is our program and provider private.
- By default, the data that is saved on the memory card is public and can be accessed. We cannot use provider to restrict access to this data.
- A method call to open or create a file on storage memory or a SQLite database potentially grants other programs permission to read and write to that data. If we use the data stored in memory as provider data sets, other programs have read and write permissions, and our manifest settings will not work. The default fetch data is private and should not be changed.
If we want to use the content provider permission to control the reading of the data, we need to store the data in internal files, SQLite databases or servers, and make sure that the files and databases are private.
Implementing a License
By default, provider does not have a license set and all programs have access to provider data. We can <provider>
configure the properties or sub-tags of the tags in the mainfest file. Licenses can be configured for the entire provider or for specific tables or for specific records.
Declare the license <permission>
to use the label, for example:
<permission android:name="com.example.app.provider.permission.READ_PROVIDER">
The detailed licensing settings for provider are described below:
- Single provider read/write license (read-write provider-level permission)
This license controls the read-write permission of the entire provider. <provider>
set in the properties in the tag android:permission
.
- Provider level separate read or write permissions (separate read and write Provider-level permission
)
<provider>
set the Read permission in the properties in the tag, android:readPermission
<provider>
and set the Write permission in the properties in the tag android:writePermission
. These two licenses are better than the licenses set by Android:permission.
- Path-level Licensing (Path-level permission)
Read or write or read or write permission to the specified URI. <provider>
set in a sub-tab in the label <path-permission>
. This level of authority is superior to the two licenses above.
Temporary license (Temporary permission)
Grant the program permission to obtain data temporarily.
Set in the <provider>
properties in the tag android:grantUriPermissions
, or <provider>
<grant-uri-permission>
add one or more to the child labels in the label.
If a temporary license is used, it must be called whenever support for a URI is removed from provider, Context.revokeUriPermission()
and the URI is related to the temporary license. If this property is set to True, the system supports licensing of temporary licenses and overrides any other license (provider level or path level).
If False is set, you will need to <provider>
<grant-uri-permission>
add one or more of the child tags in the label. Each sub-label specifies that one or more URIs have temporary access to the license.
In order to delegate temporary access permission in a program, intent must FLAG_GRANT_READ_URI_PERMISSION
contain FLAG_GRANT_WRITE_URI_PERMISSION
one or two flags (by setFlags()
setting) in and.
If the property is not set, it is android:grantUriPermissions
considered false.
<provider>
Label
We know that the four components need to be configured in the Mainfest file, and the ContentProvider implementation class is configured via a <provider>
label. Some important attributes and sub-tags are also included in the tag:
android:authorities
Used to identify provider in this system (identify the application first).
android:name
The class name of the implementation class for ContentProvider.
Permission
The above is described in detail, mainly including:
- `android:grantUriPermssions`;- `android:permission`;- `android:readPermission`;- `android:writePermission`。
Start and Control properties
android:enabled
Whether to allow instantiation
android:exported
Whether the external can be used
android:initOrder
An integer value that represents the order that is initialized in the same process, and the higher the value, the sooner it is initialized
android:multiProcess
Whether to allow instantiation in multiple processes
android:process
The process in which it resides
android:syncable
Information properties
android:icon
Icon
android:label
Name
Summarize
The collaborative relationship between Contentresovler and ContentProvider is described by querying the SQLite database as an example:
The query()
parameter URI in the method of the Contentresovler object, through the permission authority in the URI, can find the corresponding ContentProvider implementation class, instantiate the class and invoke the query()
method, in the query()
method through UriMatcher.match()
The method matches the URI, matches the query method to the SQLite database after the successful match, returns the cursor, and returns the cursor to the caller via the ContentProvider instance. You can see that through permissions you can determine a provider, so you can include more than one providers in a program.
Legacy issues:
- Multithreading security issues.
Interpreting Android ContentProvider (2) Create your own provider