Android 四大组件之Content Provider(内容提供器)总结
内容提供器Content Provider
用于不同程序之间进行数据共享:允许其它程序访问操作提供内容提供器的程序,同时保证提供被访问的数据的安全性。
内容提供器Content Provider
一般有两种用法:
- 使用其他现有提供内容提供器的程序来读取和操作该程序的数据;
- 自身程序创建内容提供器供其他程序读取和操作自身程序的数据,即对外提供接口。
比如常用的电话簿、短信、媒体库等都提供了内容提供器供其他程序读取和操作数据。
内容提供器Content Provider
基本用法
通过Context
中的getContentResolver()
获取ContentResolver
类的实例来访问其他程序的内容提供器的数据,进行相关CRUD
操作。相关方法与SQLiteDatabase
基本相同,只不过方法参数有些不同。
insert()
:添加数据;update()
:更新数据;delete()
:删除数据;query()
:查询数据。
ContentResolver
中CRUD
操作不接受表名,而是使用Uri
参数(内容URI)。内容URI给内容提供器提供了唯一标志符,它是有两部分组成:authority
和path
。
authority
:用于区分应用程序,一般为了更好的区分,都会采用程序包名再加上provider
来命名。比如程序的包名com.demo.app
,那么该程序的authority
可以命名为com.demo.app.provider
。path
:用于区分同一程序中不同表,通常都会添加在authority
后面。比如程序存在2张表table1
和table2
,对应的path就可以分别命名为/table1
和/table2
;- 内容URI:是将上面的
authority
和path
进行组合,然后再在头部加上协议声明。比如content://com.demo.app.provider/table1
和content://com.demo.app.provider/table2
query()操作
通过内容URI的内容,就可以定位是哪个程序中的哪张数据库表。接着将内容URI解析为Uri
对象作为参数传入ContentResolver
对象中。
Uri uri = Uri.parse("content://com.demo.app.provider/table1");
Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
这些参数和SQLiteDatabase
中的query()
方法参数类似,下面是简单的对比:
query()参数 | 对应SQL部分 | 说明 |
---|---|---|
uri | from table | 指定查询某个应用程序的表 |
projection | select column1, column2, ... | 查询指定列名 |
selection | where column = value | where的约束条件 |
selectionArgs | where约束条件的占位符提供的具体值 | |
order by | order by column1, column2 | 指定查询结果的排列方式 |
查询返回Cursor
对象,就可以将数据从Cursor
对象逐个读取出来。
Uri uri = Uri.parse("content://com.demo.app.provider/table1");
Cursor cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
if (null != cursor) {
while (cursor.moveToNext()) {
String column1 = cursor.getString(cursor.getColumnIndex("column1"));
int column2 = cursor.getString(cursor.getColumnIndex("column2"));
}
}
if(null != cursor) {
cursor.close();
}
insert()操作
Uri uri = Uri.parse("content://com.demo.app.provider/table1");
ContentValues values = new ContentValues();
values.put("column1", "text");
values.put("column2", 1);
context.getContentResolver().insert(uri, values);
update()操作
Uri uri = Uri.parse("content://com.demo.app.provider/table1");
ContentValues values = new ContentValues();
values.put("column1", "new_value");
context.getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[] {"text1", "1"});
delete()操作
Uri uri = Uri.parse("content://com.demo.app.provider/table1");
context.getContentResolver().delete(uri, "column1 = ?", new String[] {"1"});
读取通讯录
public class MainActivity extends AppCompatActivity {
ArrayAdapter<String> adapter;
List<String> contactsList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView contactsView = (ListView) findViewById(R.id.contacts_view);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, contactsList);
contactsView.setAdapter(adapter);
if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String [] {Manifest.permission.READ_CONTACTS}, 1);
} else {
readContacts();
}
}
@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) {
readContacts();
} else {
Toast.makeText(this, "You denied the permission!", Toast.LENGTH_LONG).show();
}
break;
default:break;
}
}
private void readContacts() {
Cursor cursor = null;
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (null != cursor) {
while (cursor.moveToNext()) {
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contactsList.add(displayName + "---" + number);
}
adapter.notifyDataSetChanged();
}
if(null != cursor) {
cursor.close();
}
}
}
AndroidManifest.xml
中添加权限声明:
<uses-permission android:name="android.permission.READ_CONTACTS" />
创建自己的内容提供器
创建自己的内容提供器可以通过继承ContentProvider
类来实现。ContentProvider
类有6个抽象方法:
onCreate()
:初始化内容提供器是调用。通常进行数据库创建、升级等操作,返回true
表示内容提供器初始化成功,返回false
则表示失败。query()
:内容提供器提供的查询数据接口。uri
参数确定查询那张表,projection
参数用于确定查询返回结果的列,selection
和selectionArgs
参数用于约束条件,sortOrder
参数对结果进行排序。查询结果存放在Cursor
对象中。insert()
:内容提供器提供的添加数据接口。uri
参数确定添加记录的表,values
参数为添加的记录的数据。返回一个用于表示这条新纪录的URI。update()
:内容提供器提供的更新数据接口,uri
参数确定更新记录的表,values
参数为更新记录的数据,selection
和selectionArgs
参数用于约束条件。delete()
:内容提供器提供的删除数据接口。uri
参数确定更新记录的表,selection
和selectionArgs
参数用于约束条件。getType()
:根据传入的内容URI返回相应的MIME
类型。
public class DemoContentProvider 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;
}
}
内容URI的格式
内容URI的格式主要有2种,
content://com.demo.app.provider/table1
,表示该应用的table1
表中的数据(以路径结尾,则期望匹配该表中的所有数据)。content://com.demo.app.provider/table1/1
,表示该应用的table1
表中id为1的数据(以id结尾,则期望该表中拥有id的数据)。
上面的URI格式使用通配符来表示:
*
:匹配任意长度的任意字符,如content://com.demo.app.provider/*
匹配应用的任意表。#
:匹配任意长度的数字,如content://com.demo.app.provider/table1/#
匹配应用中的表table1的任意一行数据。
UriMatcher
类
有了内容URI的正则表达式匹配规则,就可以借助UriMatcher
类方便的匹配内容URI。
addURI()
方法:该方法接收3个参数,分别是authority
、path
、自定义码。match()
方法:根据addURI()
设置的内容,该方法接收将传递进来的Uri对象参数返回匹配对应的自定义码。利用自定义码,就可以确定该内容URI期望访问哪张表中数据。
ContentProvider
类的getType()方法
和MIME
getType()
方法用于获取Uri对象对应的MIME类型。其中一个内容URI对应的MIME字符串主要有3部分组成:
- 必须以
vnd
开头; - 如果内容URI以路径结尾,则该部分内容为
android.cursor.dir/
;如果内容URI以id结尾,则该部分内容为android.cursor.item/
; - 最后部分为
vnd.{authority}.{path}
。
根据上面的约定,
内容URIcontent://com.demo.app.provider/table1
对应MIME类型vnd.android.cursor.dir/vnd.com.demo.app.provider.table1
;
内容content://com.demo.app.provider/table1/1
对应的MIME类型vnd.android.cursor.item/vnd.com.demo.app.provider.table1
。
创建自己的内容提供器
DatabaseProvider
内容提供器:
public class DatabaseProvider extends ContentProvider {
public static final int BOOK_DIR = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR = 3;
public static final int CATEGORY_ITEM = 4;
public static final String AUTHORITY = "com.jyoryo.app.android.study.provider";
private MyDatabaseHelper dbHelper;
private static UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
public DatabaseProvider() {
}
@Override
public boolean onCreate() {
dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 1);
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
cursor = db.query("book", projection, selection, selectionArgs, null, null, sortOrder);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
cursor = db.query("book", projection, "id = ?", new String[] {bookId}, null, null, sortOrder);
break;
case CATEGORY_DIR:
cursor = db.query("category", projection, selection, selectionArgs, null, null, sortOrder);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
cursor = db.query("category", projection, "id = ?", new String[] {categoryId}, null, null, sortOrder);
break;
default:
break;
}
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
Uri urlReturn = null;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
case BOOK_ITEM:
long newBookId = db.insert("book", null, values);
urlReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);
break;
case CATEGORY_DIR:
case CATEGORY_ITEM:
long newCategoryId = db.insert("category", null, values);
urlReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId);
break;
default:
break;
}
return urlReturn;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int updateRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
updateRows = db.update("book", values, selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
updateRows = db.update("book", values, "id = ?", new String[] {bookId});
break;
case CATEGORY_DIR:
updateRows = db.update("category", values, selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
updateRows = db.update("category", values, "id = ?", new String[] {categoryId});
break;
default:
break;
}
return updateRows;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int deleteRows = 0;
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
deleteRows = db.delete("book", selection, selectionArgs);
break;
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
deleteRows = db.delete("book", "id = ?", new String [] {bookId});
break;
case CATEGORY_DIR:
deleteRows = db.delete("category", selection, selectionArgs);
break;
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
deleteRows = db.delete("category", "id = ?", new String [] {categoryId});
break;
default:break ;
}
return 0;
}
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.jyoryo.app.android.study.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.jyoryo.app.android.study.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.jyoryo.app.android.study.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.jyoryo.app.android.study.provider.category";
default:
return null;
}
}
}
MyDatabaseHelper
数据库帮助类:
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "CREATE TABLE `book` ("
+ " `id` INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " `author` TEXT,"
+ " `price` REAL,"
+ " `pages` INTEGER,"
+ " `name` TEXT"
+ ")";
public static final String CREATE_CATEGORY = "CREATE TABLE `category` ("
+ " `id` INTEGER PRIMARY KEY AUTOINCREMENT,"
+ " `name` TEXT,"
+ " `code` INTEGER)";
private Context mContext;
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists book");
db.execSQL("drop table if exists category");
onCreate(db);
}
}
使用内容提供器:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private String newId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button addData = (Button) findViewById(R.id.add_data),
queryData = (Button) findViewById(R.id.query_data),
updateData = (Button) findViewById(R.id.update_data),
deleteData = (Button) findViewById(R.id.delete_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse("content://com.jyoryo.app.android.study.provider/book");
ContentValues values = new ContentValues();
values.put("name", "name1");
values.put("author", "author1");
values.put("pages", 100);
values.put("price", 19.99D);
Uri newUri = getContentResolver().insert(uri, values);
newId = newUri.getPathSegments().get(1);
}
});
queryData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse("content://com.jyoryo.app.android.study.provider/book");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if(null != cursor) {
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));
Log.d(TAG, "Book Info---Name:" + name + "; Author:" + author + "; Pages:" + pages + "; Price:" + price);
}
}
}
});
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse("content://com.jyoryo.app.android.study.provider/book/" + newId);
ContentValues values = new ContentValues();
values.put("name", "new_name1");
values.put("pages", 6666);
values.put("price", 66.66D);
getContentResolver().update(uri, values, null, null);
}
});
deleteData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Uri uri = Uri.parse("content://com.jyoryo.app.android.study.provider/book/" + newId);
getContentResolver().delete(uri, null, null);
}
});
}
}
在AndroidManifest.xml
中声明内容提供器:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jyoryo.app.android.study">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<provider
android:name=".DatabaseProvider"
android:authorities="com.jyoryo.app.android.study.provider"
android:enabled="true"
android:exported="true"></provider>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>