ContentProvider of Android View Manual

ContentProvider of the fifth Android view manual

As one of the four major components of Android, ContentProvider does not appear much in the process of normal use, but this does not affect its status in the Android system. Because our application data is usually internally self-digested and independent, once it involves data interaction with other applications, the role of ContentProvider is reflected.

overview

ContentProvider (content provider), the function is to provide a unified interface for data sharing between different applications, identify the data to be accessed by other applications through uri, and realize the sharing of data through the addition, deletion, modification and query methods of ContentResolver operate. You can also monitor whether the data has changed by registering ContentObserver to refresh the page accordingly.

Among them, ContentProvider only assumes the role of an intermediate worker, and the operation of the internal data source is implemented by the provided application developer himself , such as Sqlite, file, XMl, network and so on.
insert image description here

Uniform Resource Identifier (URI)

Before understanding ContentProvider, let's briefly understand what a Uniform Resource Identifier (URI) is.
A URI is essentially a string that uniquely marks the location or name of a resource. It can not only mark the resources of the World Wide Web, but also mark other arbitrary resources such as the mail system and local file system, and the resources can be static text stored on the disk, and page data can also be dynamic services provided by java and php .
A normal URI logo is shown in the figure below, which is often composed of these four parts:
insert image description here
In Android, if we want to use ContentProvider for data sharing, we need to follow the following rules:

  • Protocol name: use content as a prefix (this is stipulated by Android)
  • Host name: use the Authority value corresponding to the ContentProvider in the registration list (configured in the list file)
  • Path name: Here you need to fill in the resource path, you can add the corresponding resource path when you create the ContentProvider
  • Query parameter: This parameter can not be filled in, because the query parameter ContentProvider has already provided a way for us to call, of course, if you want to fill in, you can also get the parsed value and perform corresponding logical processing.

Example: content://com.ftd.test.myprovider/test

Steps for usage

The general steps are as follows:

  1. custom ContentProviderclass
  2. AndroidManifestRegister in the manifest file
  3. Perform data access in-process | inter-process
ContentProvider customization

First of all, we need to inherit ContentProvider and implement the methods in it, as follows:

package com.ftd.test;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * 内容提供者
 * tangxianfeng
 * 2022.10.29
 */
public class MyProvider extends ContentProvider {
    
    

    /**
     * 进行创建
     */
    @Override
    public boolean onCreate() {
    
    
        return false;
    }
    /**
     * 获取数据类型
     */
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
    
    
        return null;
    }
    /**
     * 查询数据
     */
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
    
    
        return null;
    }
    /**
     * 插入数据
     */
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
    
    
        return null;
    }
    /**
     * 删除数据
     */
    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
    
    
        return 0;
    }
    /**
     * 更新数据
     */
    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
    
    
        return 0;
    }
}
The role of each method
  • onCreate()
    The method that will be called when the ContentProvider is created. Generally, it can be used for database and other operations that need to be initialized.
  • getType(Uri)
    returns the URI of the content corresponding to the MIME type. The getType here is generally used as a custom return of the type. When the type we pass in in some cases needs to be verified, it can be realized by rewriting this method. Generally, it is not required. do processing.
  • query(Uri, String[], String, String[], String)
    queries data and returns a data Cursor object.
  • insert(Uri, ContentValues)
    inserts a piece of data
  • delete(Uri, String, String[])
    delete data according to conditions
  • update(Uri, ContentValues, String, String[])
    update data according to conditions

The following are some simple simulation operations that I use cursors to perform, mainly involving query and insertion. In actual situations, we can replace them with databases such as sqlite.

package com.ftd.test;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * 内容提供者
 * tangxianfeng
 * 2022.10.29
 */
public class MyProvider extends ContentProvider {
    
    
    private static final String AUTHORITY = "com.ftd.test.myprovider"; //用于区分不同的ContentProvider

    //UriMatch主要为了区配URI,比如应用A提供了数据,但是并不是说有的应用都可以操作这些数据,只有提供了满足应用A的URI,才能访问A的数据,而UriMatch就是做这个区配工作的
    private static final UriMatcher sUriMatcher;
    //用以表明数据类型
    public static final int USER_DIR = 0;
    public static final int USER_ITEM = 1;

    private MatrixCursor TestCursor;
    static {
    
    
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        //只对这三种uri进行处理 如果匹配上 通过 sUriMatcher.match(uri)可以得到 对应的Code
        sUriMatcher.addURI(AUTHORITY, "test", USER_ITEM);
        sUriMatcher.addURI(AUTHORITY, "test1", USER_ITEM);
        sUriMatcher.addURI(AUTHORITY, "testmulti", USER_DIR);
    }

    /**
     * 进行创建
     */
    @Override
    public boolean onCreate() {
    
    
        //这里可以创建数据库,也可以创建其他类型的数据,如下为我用游标创建的一个表
        String[] tableCursor = new String[] {
    
     "time", "food", "where" };
        TestCursor = new MatrixCursor(tableCursor);
        TestCursor.addRow(new Object[] {
    
     "2022", "烤鸭", "北京" });
        TestCursor.addRow(new Object[] {
    
     "2021", "肉夹馍", "陕西" });
        TestCursor.addRow(new Object[] {
    
     "2020", "大饼", "山东" });
        return true;
    }

    /**
     * 获取数据类型
     */
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
    
    
        if(sUriMatcher.match(uri)==USER_DIR) {
    
    
            // 查询多条数据
            return "vnd.android.cursor.dir/testmulti";
        }else{
    
    
            // 查询一条数据
            return "vnd.android.cursor.item/testsingle";
        }
    }

    /**
     * 查询数据
     * uri 代表资源位置的uri
     * projection 代表返回内容(conlumn名)
     * selection 设置条件
     * selectionArgs是selection的内容
     * sortOrder是根据column排序
     */
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
    
    
        switch (sUriMatcher.match(uri)) {
    
    
            case USER_DIR:
               return TestCursor;
            case USER_ITEM:
                //条件查询等情况处理
                break;
        }
        return null;
    }
    /**
     * 插入数据
     */
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
    
    
        if (values==null){
    
    
            return null;
        }
        switch (sUriMatcher.match(uri)) {
    
    
            case USER_DIR:
                break;
            case USER_ITEM:
                String time = (String) values.get("time");
                String food = (String) values.get("food");
                String where = (String) values.get("where");
                TestCursor.addRow(new Object[] {
    
     time, food, where });
                getContext().getContentResolver().notifyChange(uri, null);//通知指定URI数据已改变
                break;
            default:
        }
        return null;
    }

    /**
     * 删除数据
     * selection 设置条件
     * selectionArgs是selection的内容
     */
    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
    
    
        return 0;//此处就不赘述了,拿到条件后根据条件对数据进行删除即可
    }
    /**
     * 更新数据
     * selection 设置条件
     * selectionArgs是selection的内容
     */
    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
    
    
        return 0;//此处就不赘述了,拿到条件后根据条件对数据进行更新即可
    }
}

Register in the manifest file
	<provider
            android:authorities="com.ftd.test.myprovider"
            android:name="com.ftd.test.MyProvider"
            android:exported="true"
            android:grantUriPermissions="true"
            android:permission="ftd.Provider"
            android:readPermission="ftd.Provider"
            android:writePermission="ftd.Provider"
            android:enabled="true"
            android:multiprocess="true"/>

The android:authorities mentioned above are set here and used as a unique identifier.
As far as ContentProvider is concerned, there are many permission controls, and the attributes of nodes can be configured in the AndroidManifest.xml file. Generally, the following attribute settings are used:

  • android:grantUriPermssions: Temporary permission flag.
  • android:permission:Provider read and write permissions.
  • android:readPermission: Provider's read permission.
  • android:writePermission: Provider's write permission.
  • android:enabled: The flag allows the system to start the Provider.
  • The android:exported: flag allows other applications to use this Provider.
  • The android:multiProcess: flag allows the system to call the client in the same process as the provider.
data interaction

When performing data interaction operations, we can manage and operate data uniformly through ContentResolver:

  • Inquire
        // 获取ContentResolver
        ContentResolver resolver =  getContentResolver();
        // 通过ContentResolver 向ContentProvider中查询数据
        Cursor cursor = resolver.query( Uri.parse("content://com.ftd.test.myprovider/testmulti"), null, null, null, null);
        while (cursor.moveToNext()){
    
    
            System.out.println("query data:" + cursor.getString(0) +" "+ cursor.getString(1)+"   "+ cursor.getString(2));
            // 将表中数据全部输出
        }
        cursor.close();
        // 关闭游标
  • insert
       // 获取ContentResolver
        ContentResolver resolver =  getContentResolver();

        // 插入表中数据
        ContentValues values = new ContentValues();
        values.put("time", "2002");
        values.put("food", "瘦肉丸");
        values.put("where", "温州");

        // 通过ContentResolver 根据URI 向ContentProvider中插入数据
        resolver.insert(Uri.parse("content://com.ftd.test.myprovider/test"),values);

  • delete
// 获取ContentResolver
ContentResolver resolver =  getContentResolver();

 // 删除表中数据
resolver.delete(Uri.parse("content://com.ftd.test.myprovider/test"),"time=",new String[]{
    
    "2022"});
        
  • renew
        // 获取ContentResolver
        ContentResolver resolver =  getContentResolver();

        // 更新表中数据
        ContentValues values = new ContentValues();
        values.put("time", "2002");
        values.put("food", "瘦肉丸");
        values.put("where", "温州");

        resolver.update(Uri.parse("content://com.ftd.test.myprovider/test"),values,,"time=",new String[]{
    
    "2022"});

At this point, the general use of a ContentProvider is over.

tips

1. MIME type
MIME: full name Multipurpose Internet Mail Extensions, multi-functional Internet mail extension service. It is a multi-purpose Internet mail extension protocol, which was first applied to the e-mail system in 1992, but it was also applied to the browser later. The MIME type is to set a file with a certain extension to be opened with an application. When the file with the extension is accessed, the browser will automatically use the specified application to open it. It is mostly used to specify some client-defined file names, and some media file opening methods.
Simply put, the MIME type is used to identify the file types that the current Activity can open .
Take a chestnut:

<activity 
   android:name=".TESTActivity" 
   android:label="@string/test"> 
   <intent-filter> 
       <action android:name="com.ftd.test"/> 
       <categoryandroid:name="android.intent.category.DEFAULT"/> 
       <data android:mimeType="image/bmp"/> 
   </intent-filter> 
</activity> 

Here, the MimeType value of the data field is specified to be "image/bmp", that is, when using implicit Intent matching, this Activity can only be enabled when the specified MimeType is "image/bmp", that is, this Activity can only open image /bmp type files.
A typical call is as follows:

Intent intent =new Intent();
intent.setAction(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);

The activity Intent.ACTION_DIAL will get the contentprovider type of the address book you use through tel:10086. If it is consistent with the mimeType specified in the intentfilter, then it proves that the parameter uri passed is available, and then open the interface of dialing.

In getType, we can see that the getType function will generate a string representing MimeType according to the URI passed in; and the generation of this string also has rules:

  • If it is a single record, it should return a string starting with vnd.android.cursor.item/
  • If there are multiple records, it should return a string headed by vnd.android.cursor.dir/

We write the first part of MIME according to Google's requirements, and the latter part can be written according to our own actual needs.

2. The bottom layer of ontentProvider is implemented using the Binder mechanism in Android. If you want to study this principle in detail, you can first understand the Binder mechanism

Possible related problems

1. Can the application that provides the ContentProvider get the data without waking up?
No, in Android, when application A goes to application B to obtain data through ContentProvider, it needs to wake up application B first, and then obtain resource data through B's ContentProvider. If the process of B is not awakened, the data will not be obtained. At this time, the background will find Log: ActivityThread: Failed to find providerinfo for xxx.
2. What is the function of getType of ContentProvider?

  • Implicitly call activity to pass in data data. And this data data is the uri parameter of a ContentProvider
  • In order to prevent the data from being unable to be processed by the activity, the activity needs to set mime for data verification
  • In order for acvity to have results when verifying the custom ContentProvider, it is necessary to implement the getType of the ContentProvider.
  • If the type needs to involve storage, reading and other permissions, you can also make a judgment application before jumping to the activity.
    3. Why use the ContentResolver class to interact with the ContentProvider class instead of directly accessing the ContentProvider class?
    Generally speaking, an application needs to use multiple ContentProviders. If you need to understand the different implementations of each ContentProvider to complete data interaction, the operation cost is high & difficult, so
    add a ContentResolver class to the ContentProvider class to perform all ContentProvider Unified management.
    4. Advantages of ContentProvider
    1) Security
    ContentProvider provides a safe environment for data interaction between applications: it allows you to open your own application data to other applications for addition, deletion, modification, and query according to your needs, without worrying about directly opening the database Security issues caused by permissions
    2) Access is simple & efficient
    Compared with other ways of sharing data externally, the way of data access will be different due to the way of data storage:
    using file mode to share data externally requires file operations to read and write data;
    using Sharedpreferences share data, you need to use sharedpreferences API to read and write data,
    which makes accessing data complicated & difficult.
    However, the ContentProvider method is adopted, which decouples the storage method of the underlying data, so that no matter what method is used for the storage of the underlying data, the external access to the data is uniform, which makes the access simple & efficient

Guess you like

Origin blog.csdn.net/number_cmd9/article/details/127584362