This article describes the implementation method of creating your own content provider in Android programming. Share it for your reference, as follows:
We learned how to access data from other applications in our own programs. Overall, the idea is very simple. You only need to obtain the content URI of the application and then use ContentResolver to perform CRUD operations. But have you ever thought about how those applications that provide external access interfaces implement this function? How do they ensure the security of data so that private data will not be leaked?
Steps to Create a Content Provider
As mentioned earlier, if you want to implement the function of sharing data across programs, the official recommended way is to use a content provider. You can create your own content provider by creating a new class to inherit the ContentProvider. There are six abstract methods in the ContentProvider class. When we use subclasses to inherit it, we need to rewrite all of these six methods. Create a new MyProvider inherits from ContentProvider, and the code is as follows:
public class MyProvider extends ContentProvider { @Override public boolean onCreate() { return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { return null; } @Override public Uri insert(Uri uri, ContentValues values) { return null; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } @Override public String getType(Uri uri) { return null; } }
Among these six methods, I believe most of you are already very familiar with them. Let me introduce them briefly.
1. onCreate()
Called when initializing the content provider. Usually, the database creation and upgrading operations are completed here. Returning true means the content provider initialization is successful, and returning false means failure. Note that the content provider is initialized only when a ContentResolver exists to try to access data in our program.
2. query()
Query data from the content provider. Use the uri parameter to determine which table to query, the project parameter is used to determine which columns to query, the selection and selectionArgs parameters are used to constrain which rows to query, the sortOrder parameter is used to sort the results, and the query results are stored in the Cursor object to return.
3. insert()
Add a piece of data to the content provider. Use the uri parameter to determine the table to be added, and the data to be added is saved in the values parameter. After the addition is complete, a URI is returned to represent the new record.
4. update()
Update the data already in the content provider. Use the uri parameter to determine which table data is updated. The new data is saved in the values parameter. The selection and selectionArgs parameters are used to constrain which rows are updated. The number of affected rows will be returned as the return value.
5. delete()
Delete data from the content provider. Use the uri parameter to determine which table data to be deleted. The selection and selectionArgs parameters are used to constrain which rows to delete. The number of rows deleted will be returned as the return value.
6. getType()
Returns the corresponding MIME type based on the content URI passed in. As you can see, almost every method will have the Uri parameter, which is also passed when the ContentResolver adds, deletes, modifys and searches are called. Now, we need to parse the incoming Uri parameter and analyze the tables and data that the caller expects to access.
To review, a standard content URI is written like this:
content:///table1
This means that the caller expects to access the data in the table1 table of the application. In addition, we can also add an id to the URI of this content, as follows:
content:///table1/1
This means that the caller expects to access data with id 1 in the table1 table of this application. The content URI format is mainly the above two. Ending with a path means expecting to access all data in the table, and ending with id means expecting to access data with the corresponding id in the table. We can use wildcards to match content URIs in these two formats separately, and the rules are as follows.
1. *: Any character that matches any length
2. #: Indicates that numbers match any length, so a URI format that can match any table can be written as:
content:///*
A content URI format that can match any row of data in the table1 table can be written as:
content:///table1/#
Next, we can easily implement the function of matching content URIs with the help of the UriMatcher class. UriMatcher provides an addURI() method. This method receives three parameters and can pass permissions, paths and a custom code into it respectively. In this way, when the match() method of UriMatcher is called, a Uri object can be passed in, and the return value is a custom code that can match the corresponding Uri object. Using this code, we can determine which table the caller expects to access. Modify the code in MyProvider as follows:
public class MyProvider extends ContentProvider { public static final int TABLE1_DIR = 0; public static final int TABLE1_ITEM = 1; public static final int TABLE2_DIR = 2; public static final int TABLE2_ITEM = 3; private static UriMatcher uriMatcher; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); ("", "table1", TABLE1_DIR); (" ", "table1/#", TABLE1_ITEM); (" ", "table2", TABLE2_ITEM); (" ", "table2/#", TABLE2_ITEM); } …… @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { switch ((uri)) { case TABLE1_DIR: // Query all data in table1 tablebreak; case TABLE1_ITEM: // Query a single piece of data in table1 tablebreak; case TABLE2_DIR: // Query all data in table2 tablebreak; case TABLE2_ITEM: // Query a single piece of data in the table2 tablebreak; default: break; } …… } …… }
It can be seen that four integer constants have been added to MyProvider, where TABLE1_DIR means accessing all data in table1 table, TABLE1_ITEM means accessing a single piece of data in table1 table, TABLE2_DIR means accessing all data in table2 table, and TABLE2_ITEM means accessing a single piece of data in table2 table. Then in the static code block we create an instance of UriMatcher and call the addURI() method to pass in the URI format that expects to match. Note that the path parameters passed here can be wildcarded. Then when the query() method is called, the incoming Uri object will be matched through the match() method of UriMatcher. If you find that the URI format of a content in UriMatcher successfully matches the Uri object, the corresponding custom code will be returned, and then we can determine what data the caller expects to access.
The above code is just an example to illustrate the query() method. In fact, the implementation of insert(), update(), and delete() methods is similar. They all carry the Uri parameter, and then use the match() method of UriMatcher to determine which table the caller expects to access, and then perform corresponding operations on the data in the table.
In addition, there is another method you will be unfamiliar with, namely the getType() method. It is a method that all content providers must provide to obtain the MIME type corresponding to the Uri object. The MIME string corresponding to a content URI is mainly composed of three parts. Android has made the following format regulations for these three parts.
1. Must start with vnd.
2. If the content URI ends with a path, it is followed by /, and if the content URI ends with a id, it is followed by /.
3. Finally, follow vnd.<authority>.<path>.
Therefore, for the content:///table1 URI, its corresponding MIME
Types can be written as:
/.table1
For the content:///table1/1 URI, the corresponding MIME type can be written as:
/vnd. .table1
Now we can continue to improve the content in MyProvider. This time we will implement the logic in the getType() method. The code is as follows:
public class MyProvider extends ContentProvider { …… @Override public String getType(Uri uri) { switch ((uri)) { case TABLE1_DIR: return "/. table1"; case TABLE1_ITEM: return "/. table1"; case TABLE2_DIR: return "/. table2"; case TABLE2_ITEM: return "/. table2"; default: break; } return null; } }
At this point, a complete content provider is created, and any application can now use ContentResolver to access the data in our program. So, as mentioned above, how can we ensure that privacy data will not be leaked? In fact, thanks to the good mechanism of the content provider, this problem has been solved unknowingly. Because all CRUD operations must match the corresponding content URI format before they can be performed, and of course it is impossible for us to add a URI of privacy data to UriMatcher, so this part of the data cannot be accessed by external programs at all, and security issues no longer exist.
For more information about Android related content, please check out the topic of this site:Android resource operation skills summary》、《A summary of SD card operation methods for Android programming and development》、《Android file operation skills summary》、《Android database operation skills summary》、《Android programming activity operation skills summary》、《Android development introduction and advanced tutorial》、《Android View View Tips Summary"and"Android control usage summary》
I hope this article will be helpful to everyone's Android programming design.