Like attention, no more lost, your support means a lot to me!
🔥 Hi, I’m Chouchou. GitHub · Android-Notebook has been included in this article. Welcome to grow up with Chouchou Peng. (Contact information at GitHub)
preface
-
ContentProvider is one of the four components of Android and belongs to the content sharing component.
-
In this article, I will discuss the basic use of ContentProvider, in the next article I will introduce the principle of ContentProvider & source analysis. Please be sure to like and follow if you can help, it really means a lot to me.
-
The Android | ContentProvider base paper,
-
The Android | ContentProvider master”
directory
Front knowledge
The content of this article will involve the following pre/related knowledge, dear I have prepared for you, please enjoy ~
- Binder mechanism:
1. An overview of the
1.1 role
ContentProvider is a unified interface for sharing content between processes. Note: The Purpose of ContentProvider is not to implement interprocess communication, but to provide a unified interface for interprocess communication, which is actually implemented by the underlying Binder mechanism.
1.2 Advantages: Transparent content delivery
Using ContentProviders allows applications to transparently open their data to other applications, so that access to the data is unified and fixed regardless of the underlying data implementation (network, memory, file, or database). The outside world is only interested in using CURD to access the ContentProvider’s data, and is unaware of whether the internal data is stored in files or databases.
1.3 Is ContentProvider a singleton?
In general, contentProviders are singletons, and in special cases you can set the Android: multiProcess property to determine if it is a singleton: When the property is true, there is an instance of ContentProvider for each caller process. The official interpretation is to avoid the overhead of interprocess communication, but this approach is rarely used in practice. So we say that in general contentProviders are singletons, creating instances only in the service provider process.
2. Related concepts
2.1 Uniform Resource Identifier (URI)
The Uniform Resource Indentifier uniquely identifies the data of the ContentProvider. The URI is the required parameter when parsing data through a ContentResolver, and follows the format shown in Contenturis.java:
The Content URIs, have the syntax: the Content: / / authority/path/idCopy the code
As you can see, the format of the URI following fixed, a total is divided into four parts: the schema: / / authority/path/idschema: / / authority/path/idschema: / / authority/path/id
For example: the content: / / com. Xurui/user / 1 content: / / com. Xurui/user / 1 content: / / com. Xurui/user / 1
The element | describe |
---|---|
Schema | Fixed to the content: / / |
The authority of STH | A unique string that identifies the ContentProvider, corresponding to the Android: Authority property specified at registration time |
Path (path) | Identifies some subset of the Authority data |
Id (Record ID) | Identifies a record in a subset of PATH (does not specify all records) |
/android.provider /android.provider /android.provider /android.provider /android.provider /android.provider
Authority | describe |
---|---|
com.android.contacts | The address book |
media | The media |
com.android.calendar | The calendar |
user_dictionary | User dictionary |
2.2 MIME Data types
MIME types (Multipurpose Internal Mail Extensions) are an Internet standard used to specify the mapping between files with certain Extensions and applications. A MIME type is divided into main type and sub-type. For example, the MIME type of an. HTML file is text/ HTML, where text is the main type and HTML is the sub-type.
In ContentProvider, getType(Uri) is used to determine the MIME type of the Uri. The return value can be a standard MIME type or a custom MIME type. This is an abstract method that needs to be implemented by subclasses:
ContentProvider.java
public abstract String getType(Uri uri);
Copy the code
2.2.1 Standard MIME types
The main types common in standard MIME types are:
- Voice: audio
- Video: video
- Image: the image
- Text: text
Examples of corresponding MIMIE types:
extension | MIME |
---|---|
.html | text/html |
.txt | text/plain |
.png | image/png |
.jpeg | image/jpeg |
2.2.2 Customizing MIME Types
In Android, there are only two main types of custom MIME types:
vnd.android.cursor.item
: Single-line recordvnd.android.cursor.dir
: Multi-line record (set)
For example, the Address book ContentProvider defines two MIME types that represent multiple records and a single record:
ContactsContract.java
/**
* The MIME type of {@link #CONTENT_URI} providing a directory of contact directories.
*/
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_directories";
/**
* The MIME type of a {@link #CONTENT_URI} item.
*/
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_directory";
Copy the code
3. Main methods
ContentProvider uses tables to manage data and exposes four operations: Add, delete, update, and query (INSERT, delete, Update, and Query) :
Public abstract Uri INSERT (Uri Uri, ContentValues values); Public abstract int DELETE (Uri Uri, String Selection, String[] selectionArgs); Binder thread public abstract Update (Uri Uri, ContentValues VALUES, String Selection, String[] selectionArgs); Public abstract Cursor Query (Uri Uri, String[] projection, String Selection, String[] selectionArgs, String sortOrder);Copy the code
In addition to the four core methods, ContentProvider has other important methods, such as:
Public abstract Boolean onCreate(); Public Abstract String getType(Uri Uri);Copy the code
Note that the four core methods are executed in the ContentProvider registration process and in the Binder thread pool, not the main thread. Considering the existence of multi-threaded concurrent access, in order to ensure data security in the implementation of ContentProvider is also required to ensure thread synchronization. The onCreate() method executes on the main thread of the ContentProvider registration process and therefore cannot perform time-consuming operations. The call to the onCreate() method is covered in more detail in section 4, launching the ContentProvider.
The main method | The thread of execution |
---|---|
insert() | Binder thread |
delete() | Binder thread |
update() | Binder thread |
query() | Binder thread |
onCreate() | The main thread |
3.1 Inserting Data
To insert a new row of data, use ContentProvider#insert(…) . For example, the following program inserts an agenda data into the system calendar:
ContentValues eventValues = new ContentValues(); eventValues.put(CalendarContract.Events.CALENDAR_ID, catId); / / calendar account ID eventValues. Put (CalendarContract. Events. EVENT_TIMEZONE, TimeZone. GetDefault (.) getID ()); / / time zones eventValues. Put (CalendarContract. Events. DTSTART, beginTimeMillis); / / start time eventValues. Put (CalendarContract. Events. DTEND, endTimeMillis); / / end time eventValues. Put (CalendarContract. Events. The TITLE, the TITLE); / / title eventValues. Put (CalendarContract. Events. The DESCRIPTION, DESCRIPTION). / / describe eventValues. Put (CalendarContract. Events. EVENT_LOCATION, location). / / location Uri resultUri = context. GetContentResolver (), insert (CalendarContract. Events. CONTENT_URI, eventValues); If (null == resultUri) {return; }Copy the code
After successful insertion, the Uri of the row is returned in the following format:
content://com.android.calendar/events<id_value>
Copy the code
In the URI < id_value > is the value of the bank _ID column, and prefix the content: / / com. Android. Calendar/events is to insert the data using a URI. Note that you do not need to specify the _ID column of the data. This column is the primary key of the table, and the ContentProvider automatically maintains this column and assigns a unique value. To extract the value of the _ID column from the Uri, call contenturis.parseId (…). :
ContentUris.java
public static long parseId(Uri contentUri) {
String last = contentUri.getLastPathSegment();
return last == null ? -1 : Long.parseLong(last);
}
Copy the code
Tip: the client program does not call ContentProvider#insert() directly, but indirectly through ContentResolver#insert(), as described below.
3.2 Querying Data
The process of querying data from a ContentProvider is divided into three steps:
3.2.1 Requesting Access Permission
The ContentProvider program can specify permissions that other applications must have, such as reading a user’s dictionary requires android.permission.READ_USER_DICTIONARY, Writing to the user dictionary requires Android.permission.WRITE_USER_DICTIONARY.
To obtain the permissions required by contentProviders, your application needs to use them in the Manifest file to request them. When Android Package Manager installs APK, it will prompt the user for the permissions required by the application. Continuing the installation is equivalent to implicitly granting permissions. Of course, after Android 6.0, some permissions need to be applied dynamically.
<uses-permission android:name = “ android.permission.READ_USER_DICTIONARY” >
Copy the code
3.2.2 Constructing query conditions
ContentProvider queries are similar to SQL queries as shown in the following table:
ContentProvider query | The SQL query | role |
---|---|---|
Uri | FROM table_name | The collection of data for the query |
projection | col,col,col… | Columns required for query results |
selectionClause | WHERE col = value | The selection criteria |
selectionArgs | (There is no exact equivalent term) | Select the conditional parameter (if selection) used in? A placeholder |
sortOrder | ORDER BY col,col,… | Collation of result set Cursor |
cursor = context .getContentResolver().query(
UserDictionary.Words.CONTENT_URI,
projection,
selectionClause,
selectionArgs,
sortOrder);
Copy the code
For example:
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI; String[] projection = { ContactsContract.Contacts._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER }; String selectionClause = ContactsContract.CommonDataKinds.Phone.NUMBER + " = ?" ; String[] selectionArgs = {"123456"}; Cursor cursor = getContentResolver().query(uri, projection, selectionClause, selectionArgs, "sort_key COLLATE LOCALIZED asc");Copy the code
This query is similar to an SQL query:
SELECT _ID, displayName, data1 FROM content://com.android.contacts/data/phones WHERE data1 = "123456" ORDER BY sort_key COLLATE LOCALIZED asc
Copy the code
3.2.3 Processing result set
Query result is a Cursor object, processing example is as follows:
If (null == mCursor) {// failed} else if (McUrsor.getcount () < 1) {// Query result is empty} else {// query result is not empty while (cursor.movetonext ()) {// Contact name String contractName = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); The String / / contact phone phone = cursor. Get String (cursor. GetColumnIndex (ContactsContract.Com monDataKinds. Phone. NUMBER)); . } cursor.close(); // Close the result set}Copy the code
3.3 Deleting Data
Deleting data is similar to querying data. Query conditions need to be constructed. The number of deleted rows is displayed after the deletion.
int rowsDeleted = context.getContentResolver().delete(...) ;Copy the code
3.4 Updating Data
An update operation is similar to a combination of a query and an insert operation, requiring both a ContentValues object and a query condition to be constructed, and returning the number of rows successfully modified after the delete operation is complete.
int rowsUpdated = context.getContentResolver().update(
UserDictionary.Words.CONTENT_URI,
updateValues,
selectionClause,
selectionArgs
);
Copy the code
4. ContentProvider core class
4.1 ContentResolver
The External world (including other components of the current process) cannot access the ContentProvider directly. Instead, it needs to be accessed indirectly through the ContentResolver. This design has the advantage of unifying the ContentProvider that the application relies on, without worrying about the ContentProvider implementation classes that actually operate.
ContentResolver is an abstract class, we are familiar with Context# getContentResolver () is actually its subclasses ApplicationContentResolver.
Context.java
public abstract ContentResolver getContentResolver();
Copy the code
ContextImpl.java
class ContextImpl extends Context { private final ApplicationContentResolver mContentResolver; @Override public ContentResolver getContentResolver() { return mContentResolver; } private static final class ApplicationContentResolver extends ContentResolver { private final ActivityThread mMainThread; @Override protected IContentProvider acquireProvider(Context context, String auth) { ... } @Override protected IContentProvider acquireExistingProvider(Context context, String auth) { ... } @Override public boolean releaseProvider(IContentProvider provider) { ... }... }}Copy the code
In the article the Android | ContentProvider master “, I want to tell you more about ContentResolver# query (…). Methods of execution process, where we discuss it later ApplicationContentResolver specific behaviors in the method body.
4.2 ContentUris
ContentUris is a utility class for URIs. The ContentUris documentation describes the format followed by the ContentProvider Uri. In addition, ContentUris provides three utility methods:
1, from the Uri parsing primary key id public static long parseId contentUri (Uri) {String last. = contentUri getLastPathSegment (); return last == null ? -1 : Long.parseLong(last); } public static uri. Builder appendId(uri. Builder Builder, appendId) long id) { return builder.appendEncodedPath(String.valueOf(id)); Public static Uri withAppendedId(Uri contentUri, long ID) {return appendId(contenturi.buildupon (), appendId(contenturi.buildupon (), appendeDID ()); id).build(); }Copy the code
4.3 UriMatcher
UriMatcher is a utility class for customizing ContentProviders. The main purpose is to match the corresponding data table according to the Uri.
Public class ExampleProvider extends ContentProvider {public class ExampleProvider extends ContentProvider { NO_MATCH Indicates that no Uri is matched. Private static final UriMatcher UriMatcher = new UriMatcher(urimatcher.no_match); Static {uriMatcher. AddURI ("com.example.app.provider", "table3", 1); static {uriMatcher. uriMatcher.addURI("com.example.app.provider", "table3/#", 2); }... Public Cursor query(Uri Uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { switch (uriMatcher.match(uri)) { case 1: If (textutils.isempty (sortOrder)) sortOrder = "_ID ASC"; break; Case 2: 3.2 Matching Table3 /# selection = Selection + "_ID = "+ uri.getLastPathSegment(); break; Default: 3.3 Default... } 3.4 Actually execute query}}Copy the code
Wildcards can be used:
*
: Matches a string of any length#
: Matches numeric strings of any length
4.4 ContentObserver
ContentObserver .java
Public void onChange(Boolean selfChange) {// Do nothing. Subclass should override.} public void onChange(Boolean selfChange) {// Do nothing onChange(boolean selfChange, Uri uri) { onChange(selfChange); }Copy the code
ContentObserver is used to listen for changes (additions, deletions, and changes) to data identified by the Uri specified in the ContentProvider, using two ContentResolver methods:
ContentResolver.java
Public final void registerContentObserver(Uri Uri, Boolean notifyForDescendants, ContentObserver observer) cancellation to monitor public final void unregisterContentObserver (ContentObserver observer)Copy the code
Note that the ContentProvider internally needs to manually notify the change event to effectively call back to the ContentResolver, for example:
ContentProvider implementation class
public class UserContentProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues values) { ... Inform getContext (). GetContentResolver (). NotifyChange (uri, null); }}Copy the code
5. To summarize
-
ContentProvider is a unified interface for content sharing between processes. Binder mechanism is at the bottom to realize inter-process communication. The advantage of using ContentProvider is to provide content transparently, and the outside world does not care about the data implementation mode of the content layer.
-
The Uri uniquely identifies the data of the ContentProvider. The MIME type describes the relationship between the extension name and the application degree. For example, the MIME type of.html is text/ HTML.
-
ContentProvider provides the CURD four core method classes to access data, executed in the Binder thread pool of the service provider process, and the onCreate() method in the main thread of the service provider process
The resources
- Android: Everything You need to know about ContentProviders! — Carson_Ho (bytes)
- Understanding the Principles of ContentProvider by Gityuan (Byte)
- ContentProviderRecord for Four Components. By Gityuan (Byte)
- ContentProvider Reference Counting by Gityuan (Bytes)
- ContentProviders Series Documentation — Android Developers
- Exploring the Art of Android Development by Ren Yugang
Creation is not easy, your “three lian” is chouchou’s biggest motivation, we will see you next time!