[TOC]
What is a content provider?
Content Providers Mainly used in different applications between the realization of data sharing function, he provides a complete set of mechanisms, allowing a program to access another program in the data, but also to ensure the security of the data to be accessed, the current use of content provider ten Android to achieve cross-program data sharing standard way. Content providers can choose which portions of their data to share, thus ensuring the security of our data.
2. How to use content providers to get data from other applications
2.1 Permission Statement
Runtime permissions: a new feature introduced in 6.0 to better protect user privacy.
The usual permission declaration only needs to add the permission to be used in MainFest. 6.0 and later for some permissions, you need to check whether this permission exists in the code at runtime otherwise a dialog box will pop up to apply for permission. If rejected, you will not be able to use it.
Runtime permissions are not required for all permissions, only for user privacy. In addition to being declared in the ManiFest, it is requested again in the code, which will cause the application to throw a SecurityException() ** if the runtime permission is not used
public class MainActivity extends AppCompatActivity {
private String TAG = "TAG";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkSelf();// Check permissions
}
private void checkSelf(a) {
// If you want to check multiple permissions, you can put the permissions to be checked into an array or collection
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
/** * checkSelfPermission checks if the current application has obtained the specified permission, and if not, calls the method that requested the permission
Log.d(TAG, "CheckSelf: permission allowed");
} else {
/ * * * through ActivityCompat requestPermissions dynamic application permissions. * the first parameter: the current context * the second parameter: need to apply for the permission string, stored in the array. * The third argument: the query code, request permission to the final result will be through the way of the callback - > onRequestPermissionsResult (); In this method, it tells you if the last request was successful
ActivityCompat.requestPermissions(MainActivity.this.new String[]{Manifest.permission.READ_CALENDAR,Manifest.permission.CAMERA}, 1); }}@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
/** * The request code is used to determine the specific permission, and the result; * grantResult[] saves the request permission, according to the order of the request permission save */
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "OnRequestPermissionsResult: authorized");
for (int i = 0; i < grantResults.length; i++) {
Log.d(TAG, "OnRequestPermissionsResult: authorization situation."+grantResults[i]); }}else {
Log.d(TAG, "OnRequestPermissionsResult: you refused authorization");
}
break;
case 2:
break;
default:
break; }}}Copy the code
That’s the basic usage of runtime permissions
2.2 Basic usage and principle of content provider
Content providers are generally used in two ways, one is to use the existing content providers to read and manipulate the data in the corresponding program, the other is to create a content provider to provide external access to the data of our program (is through others (program) to provide the content provider to obtain others (program) want to give us the use of the data), the system’s own SMS, phone book, media library and other procedures have provided similar access interface, we can use this to develop and use again.
If you want to get data from a Content provider, you need the Content-resolver class. The GetContentResolver method in Context can get an instance of this class. Content-resolver provides methods similar to the SqLiteDatabase class for CRUD operations on shared data, with slightly different parameters.
Content providers are urIs that find data sources. Uris consist of authority and PATH. Authority is used to distinguish between different programs, usually using package names, and PATH is used to distinguish between different tables in the same program.
For example, a package called com.example.test has two tables table1,table2;
The standard Uri format is (:// the previous content is protocol):
content://com.example.test/table1
content://com.example.test/table2
This makes it clear which program and which table we want to access. So content providers’ CRUDs only accept Uri arguments to determine the location.
Looking for:
Uri uri=Uri.parse("content://com.example.test/table2");
Cursor cursor=getContentResolver().quert(uri,projection,selection,selectionArgs,sortOrder);
Quert () method parameters | Corresponding SQL section | describe |
---|---|---|
uri | from table_name | Specifies a table under an application to be queried |
projection | select column1,column2 | Specifies the column name for the query |
selection | where column =value | Specify constraints on where |
selectionArgs | – | Provide specific values for placeholders in where |
sortOrder | order by column1,column2 | Specifies how to sort the query results |
Fetching a value from a cursor is the same as the operation of a database. Select the type of data you want to retrieve and then select the column name
. String data=cursor.getString(cursor.getColumnIndex("columnName")); .Copy the code
Insert:
Just like a database, you assemble the data into ContentValues
ContentValues values=new ContentValues();
values.put("columnName1"."value1");
values.put("columnName2"."value2");
getContentResolver().insert(uri,values);
Copy the code
Update:
ContentValues values=new ContentValues();
values.put("columnName1"."value1");
getContentResolver().update(Uri.parse(""),contentValues,"where column1=?".new String[]{"1"});
Copy the code
Update () : The first parameter specifies the location of the data; The second parameter specifies what value to update to; The third parameter specifies the condition; The fourth parameter specifies the default value in the WHERE condition statement;
Delete:
getContentResolver().delete(Uri.parse(""),"column=?".new String[]{"1"});
Copy the code
Parameters are similar to the above ones. There’s not much more to explain.
2.3 Use content providers to get data
Let’s use the content provider provided by the system to get the phone number of the name in the address book. Make sure you actually save several phone numbers in the address book.
1. Obtain the READ_CANTACTS permission first, otherwise the data cannot be read and an error will be reported.
2. The usage of query statements is similar to that of databases
public class MainActivity extends AppCompatActivity {
private String TAG = "TAG";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_CONTACTS) ! = PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this.new String[]{Manifest.permission.READ_CONTACTS}, 1);
} else{ getData(); }}private void getData(a) {
Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null.null.null.null);
/ / incoming Uri is ContactsContract.Com monData - Kinds. Phone class already help we encapsulate a constant
// Click open source to see:
//public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,
"phones");
if(cursor ! =null) {
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
/ / ContactsContract.Com monDataKinds. Phone. DISPLAY_NAME is a constant, to save the data of the name of the class. The same below
String phone = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
Log.d(TAG, "getData: " + name + "-"+ phone); }}}@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getData();
} else {
Log.d(TAG, "OnRequestPermissionsResult: you click refused to!");
}
break;
default:
break; }}}Copy the code
Run the program to see the name printed with the phone number.
The main content is in getData(); In the method, the specific explanation is written in the comments, as long as you know the principle then becomes very convenient to use.
3. Create your own content provider
Basic knowledge:
Create a ContentProvider by creating a new class that inherits the ContentProvider, but must override the six methods in it;
Rewrite method: | The return value | use |
---|---|---|
onCreate(); | boolean | Called when the content provider is initialized. This is where the creation and upgrade of the database is usually done. Returning true indicates successful initialization of the content provider,false fails. |
query(uri,projection, selection, selectionArgs, sortOrder); | Cursor | It finds data from content providers, uses uri parameters to determine which tables to look for, uses projection to determine which columns to look for, Selection to determine search criteria,selectionArgs to fill in default values for criteria in Selection, and sortOrder to check query nodes Fruit sorting; Query results into Cursor return; |
insert(uri, values); | Uri | Add a piece of data to the content provider, using the URI to determine the table to be added to, and place the data to be added in the values parameter; Return a Cursor that represents the new record when added successfully. |
update(uri, values, selection, selectionArgs); | int | Update existing data in the content provider, using the Uri parameter to determine which table to update, the new data is stored in values (only the values written here are updated), and the selection and selectIonArgs parameters are used to constrain which rows to update. The number of affected rows is returned as the return value |
delete(uri, selection, selectionArgs) | int | Delete data in the content provider. The URI parameter is used to determine which table data to delete, and the selection and selectionArgs parameters are used to constrain which rows to delete. The number of deleted rows is returned as a return value; |
getType(uri)(); | String | Returns the corresponding MIME type string based on the Uri passed in |
Description of the two formats of Uri:
content://com.example.test/table1/1;
Exampke. test indicates that the caller expects to access data 1 in table Table1 in the com.exampke.test application
content://com.example.test/table1;
Exampke. test: indicates that the caller expects to access all data from table Table1 in the com.exampke.test application
However, wildcards are usually used to match the two formats:
: Matches a number of any length
#* : matches characters of any length
The content of the above can be written as: the content: / / com. Example. The test / * or the content: / / com. Example. The test/table1 / #
getType(); Returned data
content://com.example.test/table1/1;
content://com.example.test/table1;
Again, take these two examples.
MIME string: starts with VND +. + Android.cursor. dir/(or Android.cursor.item /) + VND. + AUTHORITY +. + PATH
vnd.android.cursor.dir/vnd.com.example.test.table1;
vnd.android.cursor.item/vnd.com.example.test.table1
3.1 Basic Creation steps
Create a class that inherits from ContentProvider and rewrite the six methods
The content provider must be registered in manifest.xml or it cannot be used
<provider
android:name=".MyProvider"
android:authorities="com.example.h.content_demo_provider"
android:enabled="true"
android:exported="true" />
Copy the code
The first argument: class name
The second argument: is usually used with the package name to distinguish content providers between different programs
Third parameter: enable
The fourth parameter: indicates that access is allowed by other applications
Create a new content provider:
public class MyProvider extends ContentProvider {
public static final int BOOK_DIT = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR = 2;
public static final int CATEGORT_ITEM = 3;
public static UriMatcher sUriMatcher;
public static final String AUTHORITY = "com.example.h.content_demo_provider";
private MyDatabaseHelper mMyDatabaseHelper;
private SQLiteDatabase db;
{
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, "book", BOOK_DIT);
sUriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
sUriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
sUriMatcher.addURI(AUTHORITY, "category/#", CATEGORT_ITEM);
//UriMatcher can match urIs by calling its match() method, which returns the third argument we filled in when we added the URI above
}
@Override
/** * is called when initializing the content provider, which is usually used to create and upgrade the database
public boolean onCreate(a) {
// Initializes the resources required by the current content provider
mMyDatabaseHelper = new MyDatabaseHelper(getContext(), "info.db".null.1);
db = mMyDatabaseHelper.getWritableDatabase();
Log.d(TAG, "OnCreate: Content provider initialization complete");
return true;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
// query method to determine which table of which program you want to query by parsing the URI. Match with UriMatchder and return the code you entered earlier in addUri() if any
Cursor cursor = null;
switch (sUriMatcher.match(uri)) {
case BOOK_DIT:
cursor = db.query("book", projection, selection, selectionArgs, null.null,
sortOrder);
Log.d(TAG, "Query: query the entire table" + cursor);
return cursor;
case BOOK_ITEM:
String itemId = uri.getPathSegments().get(1);
cursor = db.query("book", projection, "id=?".new String[]{itemId}, null.null,
sortOrder);
/** *.getPathsegments () splits the content URI permissions with/and places the split result in a list of strings. * returns a list [0] containing the path and [1] containing the ID */
return cursor;
case CATEGORT_ITEM:
String itemId2 = uri.getPathSegments().get(1);
cursor = db.query("category", projection, "id=?".new String[]{itemId2}, null.null,
sortOrder);
return cursor;
case CATEGORY_DIR:
cursor = db.query("category", projection, selection, selectionArgs, null.null,
sortOrder);
return cursor;
default:
returncursor; }}@Nullable
@Override
public String getType(@NonNull Uri uri) {
switch (sUriMatcher.match(uri)) {
case BOOK_DIT:
return "vnd.android.cursor.dir/vnd.com.example.h.content_demo_provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.example.h.content_demo_provider.book";
case CATEGORT_ITEM:
return "vnd.android.cursor.item/vnd.com.example.h.content_demo_provider.category";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.example.h.content_demo_provider.category";
default:
return null; }}@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
Uri uriReturn = null;
switch (sUriMatcher.match(uri)) {
case BOOK_DIT:
case BOOK_ITEM:
long value = db.insert("book".null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + value);
return uriReturn;
// Returns the row ID of the newly inserted row, or -1 if an error occurs
case CATEGORT_ITEM:
case CATEGORY_DIR:
long value2 = db.insert("category".null, values);
uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + value2);
return uriReturn;
default:
returnuriReturn; }}@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
int deleteRows = 0;
switch (sUriMatcher.match(uri)) {
case BOOK_DIT:
deleteRows = db.delete("book", selection, selectionArgs);
return deleteRows;
case BOOK_ITEM:
String itemId1 = uri.getPathSegments().get(1);
deleteRows = db.delete("book"."id=?".new String[]{itemId1});
return deleteRows;
case CATEGORT_ITEM:
String itemId2 = uri.getPathSegments().get(1);
deleteRows = db.delete("category"."id=?".new String[]{itemId2});
return deleteRows;
case CATEGORY_DIR:
deleteRows = db.delete("category", selection, selectionArgs);
return deleteRows;
default:
returndeleteRows; }}@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
int updateRows = 0;
switch (sUriMatcher.match(uri)) {
case BOOK_DIT:
updateRows = db.update("book", values, selection, selectionArgs);
return updateRows;
case BOOK_ITEM:
String itemId1 = uri.getPathSegments().get(1);
updateRows = db.update("book", values, "id=?".new String[]{itemId1});
return updateRows;
case CATEGORT_ITEM:
String itemId2 = uri.getPathSegments().get(1);
updateRows = db.update("category", values, "id=?".new String[]{itemId2});
return updateRows;
case CATEGORY_DIR:
updateRows = db.update("category", values, selection, selectionArgs);
return updateRows;
default:
returnupdateRows; }}}Copy the code
Emerging methods:
uri.getPathSegments().get(1); //getPathSegments() returns a set that splits the uri after authority (get(0) and id (get(1)) so that the selected ID can be used as a conditional query against the database.
3.2 Cross-program data acquisition
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btn1, btn2, btn3, btn4;
private Uri mUriBook = Uri.parse("content://com.example.h.content_demo_provider/book");
private Uri mUriCategory = Uri.parse("content://com.example.h.content_demo_provider/category");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
//randomInsert();
iQuery();
}
private void initView(a) {
btn1 = findViewById(R.id.button);
btn2 = findViewById(R.id.button2);
btn3 = findViewById(R.id.button4);
btn4 = findViewById(R.id.button5);
btn4.setOnClickListener(this);
btn3.setOnClickListener(this);
btn2.setOnClickListener(this);
btn1.setOnClickListener(this);
}
private void iQuery(a) {
Cursor cursor = getContentResolver().query(mUriBook, null.null.null.null);
if(cursor ! =null) {
while (cursor.moveToNext()) {
String id = cursor.getString(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author")); System.out.println(id + name + author); }}else {
System.out.println("Empty"); }}private void randomInsert(a) {
ContentValues contentValues1 = new ContentValues();
ContentValues contentValues2 = new ContentValues();
for (int i = 0; i < 5; i++) {
contentValues1.put("name"."name" + i);
contentValues1.put("author"."author" + i);
System.out.println(getContentResolver().insert(mUriBook, contentValues1));
contentValues2.put("type"."type" + i);
contentValues2.put("code"."code" + i);
System.out.println(getContentResolver().insert(mUriCategory, contentValues2)); ;
}
// This method simply writes a bit of data to the database in another application through the content provider to facilitate subsequent CRUD.
}
private void iDelete(String value) {
ContentValues contentValues = new ContentValues();
int x = 0;
Uri uri = Uri.parse("content://com.example.h.content_demo_provider/book/" + value);
x = getContentResolver().delete(uri, null.null);
Log.d("TAG"."IDelete: deleted return value :" + x);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
iQuery();
break;
case R.id.button2:
break;
case R.id.button4:
iDelete("1");
break;
case R.id.button5:
break;
default:
break; }}}Copy the code
Basically, you define the Uri and you pass it to the content provider, tell it what you want to do, and then it will tell you what you want to do with the result of the execution in the return value, and the update function and the other ones are pretty much unparsed,