ContentProVider
ContentProvider有两种用法,第一种是用现成的ContentProvider,另一种是创建自己的。
ContentResolver 基本用法
Insert、 update、 delete、 query
不同与SQLiteDataBase,ContentResolver的四种基本操作是不接受参数的,而是统一使用一个Uri参数代替
Uri由两部分组成:authority和path,authority是用于区分不同的包名,path是用于区分不同表:
比如此时当前包名是com.example.app.provider
则Uri参数可以是
content://com.example.app.provider/table1
content://com.example.app.provider/table2
代表着表table1和表table2,开头的content:是协议声明。
val uri = Uri.parse("content://com.example.app.provider/table1")
val cursor = contentResolver.query(
uri,
projection,//指定查询的列名
selection,//指定where的约束条件
selectionArgs,//为where的占位符提供具体的值
sortOrder)//指定排序
while(cursor.moveToNext()){
val column1 = cursor.getString(cursor.getColumnIndex("column1"))
val column2 = cursor.getInt(cursor.getColumnIndex("column2"))
}
cursor,close()
这是添加
val values = contentValuesOf("column1" to "text","column2" to 1)
contentResolver.insert(uri,values)
芝士更新
val values = contentValuesOf("column"to"")
contentResolver.update(uri,values,"column1 = ?and column2 = ?",arrayOf("text","1"))
芝士删除
contentResolver.delete(uri,"column2 = ?",arrayOf("1"))
读取系统联系人
修改activity_main.xml中的代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView android:layout_width="match_parent"
android:layout_height="match_parent"
android:id = "@+id/contactview"/>
</LinearLayout>
简单放了一个ListView。
package com.example.contactstest
import android.Manifest
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.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.example.contactstest.databinding.ActivityMainBinding
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)
adapter = ArrayAdapter(this,android.R.layout.simple_list_item_1,contactsList)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.contactview.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
) {
...
}
@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()
}
}
}
创建自己的ContentProvider
可以通过新建一个类去继承ContentProvider,那么需要重写以下几个函数
class MyProvider:ContentProvider() {
override fun onCreate(): Boolean {
return false
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
return null
}
override fun getType(uri: Uri): String? {
return null
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
return null
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
return 0
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int {
return 0
}
}
- onCreate:初始化时调用,对数据库的初始化和升级等操作,返回true则表示初始化成功。
- query:查询功能,参数格式前面有讲过。
- insert:value存放插入的值,uri表示要插入的表,结果返回一条URI。
- update:selections和selectionArgs用于确定更新的行数。
- delete:同上。
- getType:根据传入的Uri内容返回对应的MIME类型。
这里回顾一下标准的Uri写法是:
content://com.example.app.provider/table1
除此之外还可以加上一个id
content://com.example.app.provider/table1/1
表示这个应用表table1中的id为1的数据。
通配符:
- *表示任意长度的任意字符
- #表示任意长度的数
识别Uri可以使用到UriMatcher类,其提供一个addURI()的方法,分别接收三个参数,一个是authority,一个是path,另一个是自定义字符。这样当调用match方法时,就可以辨认出是哪一个自定义的字符了。
class MyProvider:ContentProVider(){
private val table1Dir = 1
private val table1Item = 2
private val table2Dir = 3
private val table2Item =4
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
init {
uriMatcher.addURI("com.example.app.provider","table1",table1Dir)
uriMatcher.addURI("com.example.app.provider","table1/#",table1Item)
uriMatcher.addURI("com.example.app.provider","table2",table2Dir)
uriMatcher.addURI("com.example.app.provider","table2/#",table2Item)
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
when(uriMatcher.match(uri))
{
table1Dir ->{
//
}
table1Item ->{
//
}
table2Dir -> {
//
}
table2Item -> {
//
}
}
..
...}
}
getType(),前面说到用于获取Uri对象的MIME类型。
说明一下什么是MIME字符串,主要包括三部分。
- 必须以vnd开头
- 如果内容URI以路径结尾,则后接android.cursor.dir/,若是id结尾则后接android.cursor.item/
- 最后接上vnd.< authority >.< path >
例如对于 content://com.example.app.provider/table1的MIME类型是:
vnd.android.cursor.dir/vnd.com.example.app.provider.table1
修改getType代码如下:
override fun getType(uri: Uri) = when(uriMatcher.match(uri)) {
table1Dir -> "vnd.android.cursor.dir/vnd.com.example.app.provider.table1"
table1Item -> "vnd.android.cursor.item/vnd.com.example.app.provider.table1"
table2Dir -> "vnd.android.cursor.dir/vnd.com.example.app.provider.table2"
table2Item -> "vnd.android.cursor.item/vnd.com.example.app.provider.table1"
else -> null
}