文章目录
ContentProvider总结
概述
- ContentProvider主要用于在不同的程序之间实现数据共享的功能,保证了数据的安全性
- ContentProvider时Android实现跨程序共享数据的标准方式
基本用法
API说明
- 访问ContentProvider中共享的数据,需要借助ContentResolver类,通过
Context#getContentResolver()
方法获取对象。 - ContentResolver提供一系列方法用于对数据的操作:
insert()
添加数据update()
更新数据delete()
删除数据query()
查询数据
- ContentResolver中的增删改查需要使用URI,URI主要由两部分组成:
- authority:用于区分不同的应用程序,如某应用的包名为
com.example.app
,则authority可以命名为com.example.app.provider
- path:用于同一程序中不同的表做区分,如某应用存在来鼓掌表table1和table2,则path可以分别命名为
/table1
和/table2
- 在头部添加协议并将authority和path进行组合,URI变成
content://com.example.app.provider/table1
和content://com.example.app.provider/table2
- authority:用于区分不同的应用程序,如某应用的包名为
使用
val uri = Uri.parse("content://com.example.app.provider/table1")
contentResolver.query(uri,
projection,
selection,
selectionArgs,
sortOrder
)
ContentResolver中的query()
方法与SQLiteDatabase中的query()
方法比较像:
query()方法参数 | 对应SQL语句 | 说明 |
---|---|---|
uri | from table_name | 查询某张表 |
projection | select solumn1, column2 | 查询的字段 |
selection | where column = value | where约束条件 |
selectionArgs | - | where约束条件的值 |
sortOrder | order by column1, column2 | 排序方式 |
读取系统联系人
class MainActivity : AppCompatActivity() {
private lateinit var listView: ListView
private lateinit var adapter: ArrayAdapter<String>
private val contactsList = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
listView = findViewById<ListView>(R.id.listView)
adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, contactsList)
listView.adapter = adapter
//申请权限
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CONTACTS), 1)
} else {
readContacts()
}
}
//权限回调
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray,
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
1 -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
readContacts()
} else {
Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show()
}
}
}
}
//读取手机联系人
private fun readContacts() {
// content://com.android.contacts/data/phones
val uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI
val cursor = contentResolver.query(uri, null, null, null, null)
if (cursor != null) {
while (cursor.moveToNext()) {
val displayName =
cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
val number =
cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
contactsList.add("$displayName - $number")
}
adapter.notifyDataSetChanged()
cursor.close()
}
}
}
自定义ContentProvider
API说明
- 自定义ContentProvider需要实现6个抽象方法:
onCreate()
初始化时调用。通常在这里完成数据库的创建和升级等操作,返回true表示初始化成功,返回false则表示失败query()
从ContentProvider中查询数据insert()
向ContentProvider中添加一条数据update()
更新ContentProvider中的数据delete()
删除ContentProvider中的数据getType()
返回MIME类型
- 在URI后面添加id,如
content://com.example.app.provider/table/1
表示table中id为1的数据,除了可以传数字还使用通配符:*
表示匹配任意长度的任意字符,匹配任意表content://com.example.app.provider/*
#
表示匹配任意长度的数字,匹配任意idcontent://com.example.app.provider/table/#
- 可以借助UriMatcher类实现匹配URI的功能,
UriMatcher#addURI()
方法负责添加URI,这个方法有3个参数:authority、path、code值;UriMatcher#match()
方法负责匹配URI,匹配成功返回code值
自定义ContentProvider类
新建一个项目,并在该项目内操作以下步骤:
创建数据库类
private const val DB_NAME = "BookStore.db"
private const val VERSION = 2
class MyDatabaseHelper(context: Context) :
SQLiteOpenHelper(context, DB_NAME, null, VERSION) {
private val createBook = """
create table $TAB_BOOK (
id integer primary key autoincrement,
author text,
price real,
pages integer,
name text,
category_id integer
)
""".trimIndent()
private val createCategory = """
create table $TAB_CATEGORY (
id integer primary key autoincrement,
category_name text,
category_code integer
)
""".trimIndent()
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(createBook)
db.execSQL(createCategory)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
if (oldVersion <= 1) {
db.execSQL(createCategory)
}
if (oldVersion <= 2) {
db.execSQL("alter table $TAB_BOOK add column category_id integer")
}
}
}
自定义ContentProvider类
class DatabaseProvider : ContentProvider() {
private var dbHelper: MyDatabaseHelper? = null
private val authority = "com.example.databaseprovider.provider"
private val bookDir = 0 //访问所有数据
private val bookItem = 1 //访问指定id的数据
private val categoryDir = 2
private val categoryItem = 3
private val uriMatcher by lazy {
val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
uriMatcher.addURI(authority, "book", bookDir)
uriMatcher.addURI(authority, "book/#", bookItem)
uriMatcher.addURI(authority, "category", categoryDir)
uriMatcher.addURI(authority, "category/#", categoryItem)
uriMatcher
}
override fun onCreate(): Boolean {
return context?.let {
dbHelper = MyDatabaseHelper(it)
true
} ?: false
}
/**
* 查询数据
*/
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?,
): Cursor? {
var cursor: Cursor? = null
dbHelper?.let {
val db = it.readableDatabase
when (uriMatcher.match(uri)) {
bookDir -> {
cursor = db.query(TAB_BOOK,
projection,
selection,
selectionArgs,
null,
null,
sortOrder)
}
bookItem -> {
val bookId = uri.pathSegments[1]
cursor =
db.query(TAB_BOOK,
projection,
"id=?",
arrayOf(bookId),
null,
null,
sortOrder)
}
categoryDir -> {
cursor = db.query(TAB_CATEGORY,
projection,
selection,
selectionArgs,
null,
null,
sortOrder)
}
categoryItem -> {
val categoryId = uri.pathSegments[1]
cursor = db.query(TAB_CATEGORY,
projection,
"id=?",
arrayOf(categoryId),
null,
null,
sortOrder)
}
}
}
return cursor
}
/**
* 添加数据
*/
override fun insert(uri: Uri, values: ContentValues?): Uri? {
var uriReturn: Uri? = null
dbHelper?.let {
val db = it.writableDatabase
when (uriMatcher.match(uri)) {
bookDir, bookItem -> {
val newBookId = db.insert(TAB_BOOK, null, values)
uriReturn = Uri.parse("content://$authority/book/$newBookId")
}
categoryDir, categoryItem -> {
val newCategoryId = db.insert(TAB_CATEGORY, null, values)
uriReturn = Uri.parse("content://$authority/category/$newCategoryId")
}
}
}
return uriReturn
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?,
): Int {
var updateRows = 0
dbHelper?.let {
val db = it.writableDatabase
when (uriMatcher.match(uri)) {
bookDir -> {
updateRows = db.update(TAB_BOOK, values, selection, selectionArgs)
}
bookItem -> {
val bookId = uri.pathSegments[1]
updateRows = db.update(TAB_BOOK, values, "id=?", arrayOf(bookId))
}
categoryDir -> {
updateRows = db.update(TAB_CATEGORY, values, selection, selectionArgs)
}
categoryItem -> {
val categoryId = uri.pathSegments[1]
updateRows = db.update(TAB_CATEGORY, values, "id=?", arrayOf(categoryId))
}
}
}
return updateRows
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
var deleteRows = 0
dbHelper?.let {
val db = it.writableDatabase
when (uriMatcher.match(uri)) {
bookDir -> {
deleteRows = db.delete(TAB_BOOK, selection, selectionArgs)
}
bookItem -> {
val bookId = uri.pathSegments[1]
deleteRows = db.delete(TAB_BOOK, "id=?", arrayOf(bookId))
}
categoryDir -> {
deleteRows = db.delete(TAB_CATEGORY, selection, selectionArgs)
}
categoryItem -> {
val categoryId = uri.pathSegments[1]
deleteRows = db.delete(TAB_CATEGORY, "id=?", arrayOf(categoryId))
}
}
}
return deleteRows
}
override fun getType(uri: Uri): String? {
return when (uriMatcher.match(uri)) {
bookDir -> {
"vnd.android.cursor.dir/vnd.com.example.databaseprovider.provider/book"
}
bookItem -> {
"vnd.android.cursor.item/vnd.com.example.databaseprovider.provider/book"
}
categoryDir -> {
"vnd.android.cursor.dir/vnd.com.example.databaseprovider.provider/category"
}
categoryItem -> {
"vnd.android.cursor.item/vnd.com.example.databaseprovider.provider/category"
}
else -> null
}
}
}
在AndroidManifest.xml
文件里注册Provider,并设置相关权限
<permission
android:name="com.example.databaseprovider.permission.PERMISSION_READ"
android:protectionLevel="normal" />
<permission
android:name="com.example.databaseprovider.permission.PERMISSION_WRITE"
android:protectionLevel="normal" />
<provider
android:name=".DatabaseProvider"
android:authorities="com.example.databaseprovider.provider"
android:enabled="true"
android:exported="true"
android:readPermission="com.example.databaseprovider.permission.PERMISSION_READ"
android:writePermission="com.example.databaseprovider.permission.PERMISSION_WRITE" />
使用ContentProvider
再新建一个项目,并在该项目中操作以下步骤:
在AndroidManifest.xml
文件里声明权限
<uses-permission android:name="com.example.databaseprovider.permission.PERMISSION_READ" />
<uses-permission android:name="com.example.databaseprovider.permission.PERMISSION_WRITE" />
//在Android11上需要声明
<queries>
<package android:name="com.example.databaseprovider" />
</queries>
class MyProviderActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my_provider)
}
fun addData(v: View) {
val uri = Uri.parse("content://com.example.databaseprovider.provider/book")
var contentValues = ContentValues().apply {
put("name", "西游记1")
put("author", "吴承恩")
put("pages", "1008")
put("price", "98")
}
var contentValues2 = ContentValues().apply {
put("name", "哈利波特2")
put("author", "罗琳")
put("pages", "111")
put("price", "66.77")
}
contentResolver.insert(uri, contentValues)
contentResolver.insert(uri, contentValues2)
}
fun queryData(v: View) {
val uri = Uri.parse("content://com.example.databaseprovider.provider/book")
val cursor = contentResolver.query(uri, null, null, null, null)
if (cursor != null) {
while (cursor.moveToNext()) {
val name = cursor.getString(cursor.getColumnIndex("name"))
val author = cursor.getString(cursor.getColumnIndex("author"))
val pages = cursor.getString(cursor.getColumnIndex("pages"))
val price = cursor.getString(cursor.getColumnIndex("price"))
Log.e("ContentProvider", "$name $author $pages $price")
}
cursor.close()
}
}
fun updateData(v: View) {
val bookId = 1
val uri = Uri.parse("content://com.example.databaseprovider.provider/book/$bookId")
val contentValues = ContentValues().apply {
put("price", "678")
}
contentResolver.update(uri, contentValues, null, null)
}
fun deleteData(v: View) {
val bookId = 1
val uri = Uri.parse("content://com.example.databaseprovider.provider/book/$bookId")
contentResolver.delete(uri, null, null)
}
}