ContentProvider一般用法有两种:一种是使用现有的ContentProvider读取和操作相应程序中的数据:
* 另一种是创建自己的ContentProvider,给程序的数据提供外部访问接口
*
* 如果想要访问ContentProvider中共享的数据,就要借助ContentResolver类
* 可以通过Context中的getContentResolver()获取该类的实例
* ContentResolver中提供了update() delete() query() insert()方法用于增删改查
*
* 不同于SQLiteDatabase,ContentProvider中的增删改查都是不接收表名参数的,而是使用了一个Uri参数代替,
* 这个参数成为内容URI。内容URI给ContentProvider中的数据建立了唯一标识符,主要有两部分组成,authority和path。
* authority是用于对不同应用程序做区分的,一般采用包名进行命名。比如某程序包名为com.example.app,
* 那么该应用的authority可以命名为com.example.app.privider。
* path用于对同一应用程序中不同的表做区分的,通常添加到authority后面,比如某个应用数据库中存在两张表table1 table2
* 这时可以将path分别命名为/table1 /table2,然后将authority和path进行组合,内容URI就变成了
* com.example.app.provider/table1不过目前还是很难认出这个字符串是内容URI,所以还要在头部加上协议声明:
* content://com.example.app.provider/table1 得到URI字符串后还需要解析成Uri对象才可以作为参数传入,解析方法如下
* val uri = Uri.parse("content://com.example.app.provider/table1")
* 现在就可以使用Uri对象查询table1表中的数据了,代码示例如下
* val crusor = contentResolver.query(uri,projection,selection,selectionArgs,sortOrder)
* 查询返回的是一个Cursor对象,这个时候就可以将数据从Cursor对象中逐个读取出来了,示例如下
* while(cursor.moveToNext()){
* val column1 = cursor.getString(cursor.getColumnIndex("column1"))
* val column2 = cursor.getString(cursor.getColumnIndex("column2"))
* }
* cursor.close()
*
* 添加数据
* val values = contentValuesOf("column1" to "text","column2" to "1)
* contentResolver.insert(uri,values)
* 更新数据,例如将值清空
* val values = contentValuesOf("column1" to "")
* contentResolver.update(uri,values,"column1 = ? and column2 = ?,arrayOf("text","1"))
* 上述代码使用了selection和selectionArgs参数来对想要更新的数据进行约束,防止所有的行都受影响
* 删除数据
* contentResolver.delete(uri,"column2 = ?",arrayOf("1")
读取系统联系人并展示到简单的ListView控件上,不要忘了AndroidManifest.xml的权限声明
package com.example.contactstest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.ContactsContract
import android.widget.Adapter
import android.widget.ArrayAdapter
import android.widget.ListView
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
class MainActivity : AppCompatActivity() {
private val contactsList = ArrayList<String>()
private lateinit var adapter: ArrayAdapter<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val contactsView : ListView = findViewById(R.id.contactsView)
adapter = ArrayAdapter(this,android.R.layout.simple_list_item_1,contactsList)
contactsView.adapter = adapter
if (ContextCompat.checkSelfPermission(this,android.Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this,
arrayOf(android.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,"You denied the permission",
Toast.LENGTH_SHORT).show()
}
}
}
}
@SuppressLint("Range")
private fun readContacts(){
/查询联系人数据
contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,null,null,null)?.apply {
while (moveToNext()){
/获取联系人姓名
val displayName = getString(getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
/获取联系人手机号
val number = getString(getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
contactsList.add("$displayName\n$number")
}
adapter.notifyDataSetChanged()
close()
}
}
}
创建内容提供器
package com.example.databasetest
import android.content.ContentProvider
import android.content.ContentValues
import android.content.UriMatcher
import android.database.Cursor
import android.net.Uri
class DatabaseProvider : ContentProvider() {
private val bookDir = 0
private val bookItem = 1
private val categoryDir = 2
private val categoryItem = 3
private val authority = "com.example.databasetest.provider"
private var dbHelper : MyDatabaseHelper? = null
/by lazy代码块是一种懒加载技术,一开始不执行,只有首次调用uriMatcher变量才会执行,并且将代码块最后一行的值返回
private val uriMacher by lazy {
val matcher = UriMatcher(UriMatcher.NO_MATCH)
matcher.addURI(authority,"book",bookDir)
matcher.addURI(authority,"book/#",bookItem)
matcher.addURI(authority,"category",categoryDir)
matcher.addURI(authority,"category/#",categoryItem)
matcher
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?) = dbHelper?.let {
/删除数据
val db = it.writableDatabase
val deletedRows = when(uriMacher.match(uri)){
bookDir -> db.delete("Book",selection,selectionArgs)
bookItem -> {
val bookId = uri.pathSegments[1]
db.delete("Book","id = ?", arrayOf(bookId))
}
categoryItem -> {
val categoryId = uri.pathSegments[1]
db.delete("Category","id = ?", arrayOf(categoryId))
}
categoryDir -> db.delete("Category",selection,selectionArgs)
else ->0
}
deletedRows
}?:0
override fun getType(uri: Uri) = when(uriMacher.match(uri)){
bookItem -> "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book"
bookDir -> "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book"
categoryDir -> "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category"
categoryItem -> "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category"
else -> null
}
override fun insert(uri: Uri, values: ContentValues?) = dbHelper?.let {
/添加数据
val db = it.writableDatabase
val uriReturn = when(uriMacher.match(uri)){
bookItem,bookDir -> {
val newBookId = db.insert("Book",null,values)
Uri.parse("content://$authority/book/$newBookId")
}
categoryItem,categoryDir -> {
val newCategoryId = db.insert("Category",null,values)
Uri.parse("content://$authority/category/$newCategoryId")
}
else -> null
}
uriReturn
}
override fun onCreate() = context?.let {
dbHelper = MyDatabaseHelper(it,"BookStore.db",2)
true
}?:false
/Uri对象的getpathSegments:会将内容URI权限之后的部分以“/”进行分割,并将分割结果放入字符串,那么0存的是路径,1存的是id
override fun query(
uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?) = dbHelper?.let {
/查询数据
val db = it.readableDatabase
val cursor = when(uriMacher.match(uri)){
bookDir -> db.query("Book",projection,selection,selectionArgs,null,null,sortOrder)
bookItem -> {
val bookId = uri.pathSegments[1]
db.query("Book",projection,"id = ?", arrayOf(bookId),null,null,sortOrder)}
categoryDir -> db.query("Category",projection,selection,selectionArgs,null,null,sortOrder)
categoryItem -> {
val categoryId = uri.pathSegments[1]
db.query("Category",projection,"id = ?", arrayOf(categoryId),null,null,sortOrder)}
else -> null
}
cursor
}
override fun update(
uri: Uri, values: ContentValues?, selection: String?,
selectionArgs: Array<String>?
) = dbHelper?.let {
val db = it.writableDatabase
val updateRows = when(uriMacher.match(uri)){
bookDir -> db.update("Book",values,selection,selectionArgs)
bookItem -> {
val bookId = uri.pathSegments[1]
db.update("Book",values,"id = ?", arrayOf(bookId))
}
categoryDir -> db.update("Category",values,selection,selectionArgs)
categoryItem -> {
val categoryId = uri.pathSegments[1]
db.update("Category",values,"id = ?", arrayOf(categoryId))
}
else -> null
}
updateRows
}?:0
}
读取内容提供器中的内容
package com.example.providertest
import android.annotation.SuppressLint
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import androidx.core.content.contentValuesOf
class MainActivity : AppCompatActivity() {
private var bookId : String? = null
@SuppressLint("Range")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val query : Button = findViewById(R.id.queryData)
val update : Button = findViewById(R.id.updateData)
val delete : Button = findViewById(R.id.deleteData)
val insert : Button = findViewById(R.id.insertData)
insert.setOnClickListener {
/添加数据
val uri = Uri.parse("content://com.example.databasetest.provider/book")
val values = contentValuesOf("name" to "A Clash of Kings",
"author" to "George Martin","pages" to 1000,"price" to 22.33)
val newUri = contentResolver.insert(uri,values)
bookId = newUri?.pathSegments?.get(1)
}
query.setOnClickListener {
/查询数据
val uri = Uri.parse("content://com.example.databasetest.provider/book")
contentResolver.query(uri,null,null,null,null)?.apply {
while (moveToNext()){
val name = getString(getColumnIndex("name"))
val author = getString(getColumnIndex("author"))
val pages = getString(getColumnIndex("pages"))
val price = getString(getColumnIndex("price"))
Log.d("MainActivity","book name is $name")
Log.d("MainActivity","book author is $author")
Log.d("MainActivity","book pages is $pages")
Log.d("MainActivity","book price is $price")
}
close()
}
}
update.setOnClickListener {
/更新数据
bookId?.let {
val uri = Uri.parse("content://com.example.databasetest.provider/book/$it")
val values = contentValuesOf("name" to "A Storm of Swords",
"pages" to 1226,"price" to 24.33)
contentResolver.update(uri,values,null,null)
}
}
delete.setOnClickListener {
/删除数据
bookId?.let {
val uri = Uri.parse("content://com.example.databasetest.provider/book/$it")
contentResolver.delete(uri,null,null)
}
}
}
}