ContentProvider简介

在Android中,可以通过文件存储、SharedPreferences和数据库这三种数据持久化技术来保存数据,但是这三种方式只能访问本应用的数据,如果要访问其他应用的数据,就需要使用ContentProvider。

ContentProvider是Android提供的跨程序共享数据的标准方式,主要用于在不同应用程序之间实现数据共享,它可以控制程序的一部分数据进行共享,另一部分数据不共享,从而保证程序的数据不会被泄露。

ContentProvider通常有两种使用方式,一种是访问其他程序提供的ContentProvider对其数据进行读取或操作,一种是在自己的应用程序创建ContentProvider,来向其它程序提供数据访问的接口。

访问其他程序的数据

ContentProvider的增删改查方法不接受表名,而是使用Uri参数,Uri参数由authority和path两部分组成,其中authority通常是应用程序包名,用来对区分不同的应用程序,path通常是表名,用来区分统一程序中不同的表。
URI通常和网址一样用字符串表示,所以在使用ContentProvider的增删改查方法时,需要使用Uri.parse()进行解析得到Uri对象。
URI标准格式:**协议名://authority/path
解析URI对象:Uri.parse(“协议名://authority/path”)

下面是通过ContentProvider查询系统联系人的代码。

  1. class MainActivity : AppCompatActivity() {
  2. private var contacts = ArrayList<String>()
  3. lateinit var adapter: ArrayAdapter<String>
  4. override fun onCreate(savedInstanceState: Bundle?) {
  5. super.onCreate(savedInstanceState)
  6. setContentView(R.layout.activity_main)
  7. adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, contacts)
  8. contactsList.adapter = adapter
  9. // 读取系统联系人需要READ_CONTACTS权限
  10. if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_DENIED) {
  11. ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CONTACTS), 1)
  12. } else {
  13. readContacts()
  14. }
  15. }
  16. override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
  17. super.onRequestPermissionsResult(requestCode, permissions, grantResults)
  18. when (requestCode) {
  19. 1 -> {
  20. if (permissions.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_DENIED) {
  21. Toast.makeText(this, "You Denied the Permission", Toast.LENGTH_SHORT).show()
  22. } else {
  23. readContacts()
  24. }
  25. }
  26. }
  27. }
  28. private fun readContacts() {
  29. // 使用getContentResolver获取ContentResolver对象,然后查询系统联系人
  30. contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null, null)
  31. ?.apply {
  32. // 通过游标访问查询到的数据
  33. while (moveToNext()) {
  34. val name = getString(getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
  35. val number = getString(getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
  36. contacts.add("$name\n$number")
  37. }
  38. adapter.notifyDataSetChanged()
  39. close()
  40. }
  41. }
  42. }

activity_main.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:layout_width="match_parent"
  6. android:layout_height="match_parent"
  7. tools:context=".MainActivity">
  8. <ListView
  9. android:id="@+id/contactsList"
  10. android:layout_width="match_parent"
  11. android:layout_height="match_parent"
  12. app:layout_constraintBottom_toBottomOf="parent"
  13. app:layout_constraintLeft_toLeftOf="parent"
  14. app:layout_constraintRight_toRightOf="parent"
  15. app:layout_constraintTop_toTopOf="parent" />
  16. </androidx.constraintlayout.widget.ConstraintLayout>

创建ContentProvider供外部访问

创建ContentProvider的步骤

  1. 新建一个类继承自ContentProvider。
  2. 重写ContentProvider类的6个抽象方法。
  • onCreate() 在初始化时调用,通常在这里完成数据库的创建和升级,返回值为true表示初始化成功,为false表示初始化失败。
  • query()查询数据
  • insert()插入数据,添加成功后,返回一个表示这条新纪录的URI。
  • updata()更新数据
  • delete()删除数据,被删除的行数将会作为返回值返回。
  • getType()根据传入的内容URI返回相应的MIME类型。