Deeply explore the ContentProvider for Android application data sharing


This article will discuss in depth the ContentProvider, a very important data sharing mechanism in Android development.

The main contents include:

  • Basic definition and characteristics of ContentProvider
  • How to implement a customized ContentProvider
  • The functions provided by the ContentProvider and the permission control of external applications
  • Some common usage scenarios of ContentProvider
  • Best Practices and Precautions for Using ContentProvider

1、 What is a ContentProvider?


The ContentProvider is a mechanism provided by the Android system to share data between applications, and is also one of the four major components of Android. It can be seen that it plays a very important role in Android.

Its core idea is to enable applications to access and operate each other's data safely through standardized interfaces (operations such as adding, deleting, modifying and querying). Compared with direct access to files or databases, ContentProvider provides a more secure and standardized data sharing method.

The ContentProvider can be understood as an open interface for Android applications. All requests that meet the URI format defined by it can be accessed and operated normally.

Android applications can use the ContentResolver object to request execution through a method with the same name as the ContentProvider. What is executed is the method with the same name in the ContentProvider.


As shown in the figure:

 Insert picture description here


2、 Why use ContentProvider?


1. Data sharing requirements

  • In actual development, we often encounter scenarios where multiple applications need to share data, such as the address book application needs to be accessed by other applications.

  • If data is shared directly through files or databases, some problems will arise, such as difficult control of data access permissions, inconsistent data formats, etc.

  • ContentProvider provides a standardized data sharing mechanism, which can easily share data between different applications.


2. Requirements for permission control

  • Data security is a top priority in mobile application development. If the database or file is directly exposed, it is easy to cause the risk of data leakage and tampering.

  • The ContentProvider can finely control data access permissions, and developers can set read and write permissions according to requirements to fully protect data security.

  • When other applications request access to data, the ContentProvider will perform permission verification to prevent unauthorized access.


3. Requirements for unified interface

  • Different applications may use different data storage methods, such as SQLite, file system, etc. This will bring some complexity to data access.
  • The ContentProvider provides a standardized CRUD interface for data access. Developers do not need to care about the underlying data storage details, but can focus on the implementation of business logic.
  • This unified interface not only simplifies the development, but also greatly improves the maintainability of the code.

4. Requirements for cross process communication

  • Android applications are based on process isolation, and data between different applications is isolated.
  • The ContentProvider implements inter process communication based on Binder mechanism, making data interaction between applications more smooth.
  • Through the ContentProvider, developers can realize data sharing between applications without caring about the underlying details of interprocess communication.

3、 How to implement a custom ContentProvider?


To implement a custom ContentProvider, you need to:

  • Step 1: Inherit ContentProvider Class, and implement 6 abstract methods.

  • Step 2: AndroidManifest.xml Declare the ContentProvider and its Authority in.

  • Step 3: Use in other applications ContentResolver Access the data provided by the ContentProvider.


1. First, we define a MyContentProvider The ContentProvider class of the ContentProvider Base class:


 public  class  MyContentProvider  extends  ContentProvider  {
     //Declare the Authority, which is the unique identifier of the ContentProvider
     public  static  final  String  AUTHORITY  =  "com.example.mycontentprovider" ;

     //Declare Uri constants for building Uri
     public  static  final  Uri  CONTENT_URI  =  Uri . parse ( "content://"  +  AUTHORITY  +  "/users" ) ;

     //Declare table name constants
     private  static  final  String  TABLE_NAME  =  "users" ;

     //Declare database related objects
     private  SQLiteDatabase db ;
     private  MySQLiteOpenHelper dbHelper ;

     @Override
     public  boolean  onCreate ( )  {
         //Initialize database dbHelper =  new  MySQLiteOpenHelper ( getContext ( ) ) ; db = dbHelper . getWritableDatabase ( ) ;
         return  true ;
     }

     @Override
     public  Cursor  query ( Uri uri, String [ ] projection, String selection, String [ ] selectionArgs, String sortOrder )  {
         //Execute query operation
         return db . query ( TABLE_NAME  projection, selection, selectionArgs, null  null , sortOrder ) ;
     }

     @Override
     public  Uri  insert ( Uri uri, ContentValues values )  {
         //Perform Insert Operation
         long id = db . insert ( TABLE_NAME  null , values ) ;
         return  ContentUris . withAppendedId ( CONTENT_URI , id ) ;
     }

     @Override
     public  int  update ( Uri uri, ContentValues values, String selection, String [ ] selectionArgs )  {
         //Perform update operation
         return db . update ( TABLE_NAME  values, selection, selectionArgs ) ;
     }

     @Override
     public  int  delete ( Uri uri, String selection, String [ ] selectionArgs )  {
         //Perform the delete operation
         return db . delete ( TABLE_NAME  selection, selectionArgs ) ;
     }

     @Override
     public  String  getType ( Uri uri )  {
         //Return the MIME type, taking "vnd. android. cursor. dir/vnd. com. example. mycontentprovider. users" as an example
         return  "vnd.android.cursor.dir/vnd."  +  AUTHORITY  +  "."  +  TABLE_NAME ;
     }
 }

2. Next, we need to apply AndroidManifest.xml Declare the customized ContentProvider in the file:


 < provider
     android: name = " .MyContentProvider "
     android: authorities = " com.example.mycontentprovider "
     android: exported = " true "  />

Among them, android:authorities The property specifies the Authority of the ContentProvider, which must be the same as the previously defined AUTHORITY The string is consistent. android:exported Property indicates whether the ContentProvider is open to other applications. It is set to true


3. Now, we can use this customized ContentProvider in other applications


For example, in another application, we can access the MyContentProvider Data in:

 //Build Uri
 Uri uri =  MyContentProvider . CONTENT_URI ;

 //Execute query operation
 Cursor cursor =  getContentResolver ( ) . query ( uri, null  null  null  null ) ;

 //Traversal query results
 while  ( cursor . moveToNext ( ) )  {
     int id = cursor . getInt ( cursor . getColumnIndex ( "id" ) ) ;
     String name = cursor . getString ( cursor . getColumnIndex ( "name" ) ) ;
     //Processing data
     Log . d ( "MyApp"  "id: "  + id +  ", name: "  + name ) ;
 }

In this example, we first use the MyContentProvider.CONTENT_URI Built access MyContentProvider Uri of. Then, use the getContentResolver().query() Method executes the query operation, traverses the query results, and obtains data.

Similarly, we can also use insert() update() and delete() Methods Add, delete or modify data.


4、 Common usage scenarios of ContentProvider


1. Access system level data

  • Android system comes with some ContentProviders, such as Contacts, Media, Calendar, etc. Through these ContentProviders, developers can access system level data, such as address books, pictures, calendars, etc.

  • These system level ContentProviders provide standardized interfaces, so developers can easily obtain and operate system data without caring about the underlying implementation.


2. Realize data sharing between applications

  • In actual development, there is often a need to share data between multiple applications. For example, social applications need to obtain user contact information.
  • Developers can customize the ContentProvider to expose the data inside the application to other applications. By setting appropriate read/write permissions, data security can be ensured and data sharing between applications can be realized.

3. Support data persistence within the application

  • In addition to realizing data sharing between applications, the ContentProvider can also be used for data persistence within applications.

  • For example, developers can combine Room or SQLite to use the ContentProvider as the external interface of the application's internal database to provide standardized data access methods for the upper layer's ViewModel and UI layer.


4. Implement data driven UI with Android Jetpack

  • In recent years, with the wide application of Android Jetpack, ViewModel and LiveData have become the mainstream solutions to realize data-driven UI.
  • In this architecture, the ContentProvider can be used as a data source to provide a standardized data access interface for ViewModel, which is responsible for observing data changes and refreshing the UI in real time. This design can greatly improve the maintainability and testability of the code.

5. Realize file sharing

  • The ContentProvider can be used not only to share structured data, but also to share file data.

  • Developers can customize the ContentProvider to expose the file resources inside the application to other applications, such as sharing files or pictures across applications.


5、 Best Practices and Precautions for Using ContentProvider


1. Reasonably design the Authority and Uri, and use ContentUris to manage Uri


  • Authority is the unique identifier of the ContentProvider, which should be unique and readable. It usually takes the form of anti domain name, such as "com. example. myprovider".
  • Uri is an identifier used to access ContentProvider data. It should follow certain naming conventions, such as“ content://com.example.myprovider/users "。
  • Rational design of Authority and Uri can improve the readability and maintainability of code, and is also conducive to permission control.

 public  class  MyContentProvider  extends  ContentProvider  {
     public  static  final  String  AUTHORITY  =  "com.example.mycontentprovider" ;
     public  static  final  Uri  CONTENT_URI  =  Uri . parse ( "content://"  +  AUTHORITY  +  "/users" ) ;

     @Override
     public  Uri  insert ( Uri uri, ContentValues values )  {
         //Perform Insert Operation
         long id = db . insert ( TABLE_NAME  null , values ) ;
         return  ContentUris . withAppendedId ( CONTENT_URI , id ) ;
     }

     @Override
     public  Cursor  query ( Uri uri, String [ ] projection, String selection, String [ ] selectionArgs, String sortOrder )  {
         //Determine whether the operation object is a single record or a collection of records
         if  ( ContentUris . parseId ( uri )  !=  - one )  {
             //Query a single record selection =  BaseColumns . _ID +  " = ?" ; selectionArgs =  new  String [ ]  {  String . valueOf ( ContentUris . parseId ( uri ) )  } ;
         }
         //Execute query operation
         return db . query ( TABLE_NAME  projection, selection, selectionArgs, null  null , sortOrder ) ;
     }
 }

In the above example, we used ContentUris Tool class to manage Uri.

stay insert() In the method, we use ContentUris.withAppendedId() Method appends the record id after the basic Uri to generate a unique Uri.

stay query() In the method, we use ContentUris.parseId() Method parses the record id in the Uri, and performs targeted query operations according to the id.

This way can better standardize the use of Uri.


2. Reasonably control read and write permissions


  • The ContentProvider provides a flexible permission control mechanism. Developers can set different permissions for different operations (add, delete, modify, query).
  • Reasonable control of read and write permissions can improve the security of data and prevent unauthorized access. At the same time, it can flexibly control the access range of other applications to data according to business requirements.
 public class MyContentProvider extends ContentProvider { public static final String AUTHORITY = "com.example.mycontentprovider"; private static final String TABLE_NAME = "users"; @Override public boolean onCreate() { //Initialize database // ... return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { //Check access if (getContext().checkCallingOrSelfPermission(Manifest.permission.READ_CONTACTS) !=  PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Permission denied to access the data"); } //Execute query operation return db.query(TABLE_NAME,  projection, selection, selectionArgs, null, null, sortOrder); } @Override public Uri insert(Uri uri, ContentValues values) { //Check access if (getContext().checkCallingOrSelfPermission(Manifest.permission.WRITE_CONTACTS) !=  PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Permission denied to access the data"); } //Perform Insert Operation long id = db.insert(TABLE_NAME, null, values); return ContentUris.withAppendedId(CONTENT_URI, id); } //The implementation of other methods is similar }

In the above example, we checked the read and write permissions in the query() and insert() methods respectively. If the caller does not have the required permissions, a SecurityException is thrown to deny access. This ensures data security.


3. Use MIME types properly and follow MIME type conventions


  • MIME type describes the data type returned by the ContentProvider. You should set a reasonable MIME type according to the actual situation.
  • The correct MIME type can help other applications correctly parse and process the data provided by the ContentProvider.
 @Override
 public  String  getType ( Uri uri )  {
     //Return the corresponding MIME type according to Uri
     if  ( uri . equals ( CONTENT_URI ) )  {
         return  "vnd.android.cursor.dir/vnd."  +  AUTHORITY  +  "."  +  TABLE_NAME ;
     }  else  if  ( ContentUris . parseId ( uri )  !=  - one )  {
         return  "vnd.android.cursor.item/vnd."  +  AUTHORITY  +  "."  +  TABLE_NAME ;
     }
     throw  new  IllegalArgumentException ( "Unknown URI "  + uri ) ;
 }

In the above implementation, we returned different MIME type strings according to the type of Uri.

When Uri represents a record set, we return the prefix "vnd. android. cursor. dir/".

When Uri represents a single record, we return the prefix "vnd. android. cursor. item/".

This allows other applications to better recognize and process the data provided by the ContentProvider.


4. Pay attention to thread safety and properly handle exceptions


  • Pay attention to thread safety to avoid data contention problems during multithreaded access

  • When implementing various methods of the ContentProvider, you should reasonably handle possible exceptions, such as database operation exceptions, permission verification failures, etc.

  • Reasonable exception handling can improve the robustness of applications, and is also conducive to locating and solving problems.

 private  final  ReentrantLock lock =  new  ReentrantLock ( ) ;

 @Override
 public  Cursor  query ( Uri uri, String [ ] projection, String selection, String [ ] selectionArgs, String sortOrder )  { lock . lock ( ) ;
     try  {
         //Execute query operation
         return db . query ( TABLE_NAME  projection, selection, selectionArgs, null  null , sortOrder ) ;
     }  finally  { lock . unlock ( ) ;
     }
 }

 @Override
 public  Uri  insert ( Uri uri, ContentValues values )  { lock . lock ( ) ;
     try  {
         //Perform Insert Operation
         long id = db . insert ( TABLE_NAME  null , values ) ;
         return  ContentUris . withAppendedId ( CONTENT_URI , id ) ;
     }  finally  { lock . unlock ( ) ;
     }
 }

In the above example, we used ReentrantLock To ensure the thread safety of each method of the ContentProvider.

When performing database operations, we first obtain the lock, and then release the lock after the operation is completed. This can avoid the data contention problem when multithreading accesses.


6、 Conclusion


As a standardized data sharing mechanism provided by the Android system, the ContentProvider plays a very important role in the development process.

By deeply understanding its working principle, we can not only easily realize data interaction between applications, but also improve the security and maintainability of data access.

I believe that through the introduction of this article, you have mastered the core knowledge of using ContentProvider, and I believe that you will be able to develop Android with ease in the future.

Let's look forward to the wonderful content of the next article!

  • twenty-eight
    give the thumbs-up
  • step on
  • sixteen
    Collection
    Think it's good? One click collection
  •  Reward
    Reward
  • zero
    comment

Is "relevant recommendation" helpful to you?

  • Very unhelpful
  • No help
  • commonly
  • to be helpful to
  • Very helpful
Submit
comment
Add Red Packet

Please fill in the red envelope greeting or title

individual

The minimum number of red packets is 10

element

The minimum amount of red packet is 5 yuan

Current balance three point four three element Go to recharge>
To be paid: ten element
Achieve 100 million technicians!
After receiving, you will automatically become a fan of the blogger and the red envelope owner rule
hope_wisdom
Red packet sent

Reward the author

W rain or shine w

Your encouragement will be the greatest impetus for my creation

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
Scan code for payment: ¥1
Obtaining
Scan code for payment

Your balance is insufficient, please change the scanning code to pay or Recharge

Reward the author

Paid in element
Payment with balance
Click to retrieve
Scan code for payment
Wallet balance zero

Deduction description:

1. The balance is the virtual currency of wallet recharge, and the payment amount is deducted at a ratio of 1:1.
2. The balance cannot be directly purchased and downloaded, but VIP, paid columns and courses can be purchased.

Balance recharge