介绍
ContentProvider 主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。目前,使用 ContentProvider 是 Android 实现跨程序共享数据的标准方式
不同于 文件存储和 SharedPreferences 存储中的两种全局可读写操作模式,ContentProvider 可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄露的风险
ContentProvider 的用法一般有两种:一种是使用现有的 ContentProvider 读取和操作相应程序中的数据;另一种是创建自己的 ContentProvider 给程序的数据提供外部访问接口
ContentResolver 的基本用法
知识储备
对于每一个应用程序来说,想要访问 ContentProvider 中共享的数据,就要借助 ContentResolver 类,可以通过 getContentResolver()
方法来获取该类实例。ContentResolver 中提供了一系列方法对数据进行增删改查:insert()
添加数据;update()
更新数据;delete()
删除数据;query()
查询数据
接收一个 Uri 参数,被称为 内容 URI。 内容 URI 给 ContentProvider 中的数据建立了唯一标识符,它由 authority
和 path
组成。authority
是用于对不同的应用程序做区分的,一般为了避免冲突会采用包名的方式进行命名。例如某个应用的包名是 com.example.app ,那么该应用对应的 authority 可命名为 com.example.app.provider。path
则是用于对同一应用程序中不同的表做区分的,通常会添加到 authority 后边。比如某个应用的数据库中有两张表 table1 和 table2,这时可以将 path 分别命名为 /table1 和 /table2,然后把 authority 和 path 进行组合,内容URI 就变成了 com.example.app.provider/table1 和com.example.app.provider/table2 。最后需要在字符串头部加上协议声明。因此内容 URI 最标准的格式为:
content://com.example.app.provider/table1
content://com.example.app.provider/table2
然后需要把它解析成 Uri 对象才可以作为参数传入:
val uri = Uri.parse("content://com.example.app.provider/table1")
现在可以使用这个 Uri 对象查询 table1 表中的数据了:
val cursor = contentResolver.query(
uri,
projection,
selection,
selectionArgs,
sortOrder
)
参数说明:
uri
:指定查询某个应用程序下的某一张表
projection
:指定查询的列名
selection
:指定 where 的约束条件
selectionArgs
:为 where 中占位符提供具体的值
sortOrder
:指定查询结果的排序方式
查询完成后仍是 Cursor 对象,读取方式如下:
while(cursor.moveToNext()){
val column1 = cursor.getString(cursor.getColumnIndex("column1"))
val column2 = cursor.getInt(cursor.getColumnIndex("column2"))
}
cursor.cloase()
增加一条数据可以这么写:
val values = contentValuesOf("column1" to "text","column2" to 1)
contentResolver.insert(uri,values)
如果想要更新这条添加的数据,把 column1 的值清空,可以借助 ContentResolver 的 update()
方法实现:
val values = contentValuesOf("column1" to "")
contentResolver.update(uri,values,"column1 = ? and column2 = ?",arrayOf("text","1"))
注意
,上述代码使用了 selection 和 selectionArgs 参数来约束,防止所有行都会受到影响
删除这条数据:
contentResolver.delete(uri,"column2 = ?",arrayOf("1))
接下来我们利用目前所学的知识,读取系统通讯录中的联系人信息
读取系统联系人
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/contactsView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
代码
class TestActivity : AppCompatActivity() {
private val contactsList = ArrayList<String>()
private lateinit var adapter: ArrayAdapter<String>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, contactsList)
contactsView.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, "You denied the permission", Toast.LENGTH_SHORT).show()
}
}
}
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(