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 record
  • vnd.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!