十五、Android-四大组件之ContentProvider

107 阅读3分钟

15.1 ContentProvider的基本用法

访问ContentProvider中的共享的数据,就要借助ContentResolver类,可以通过Context中的getContentResolver()方法获取该类的实例。ContentResolver类中提供了一系列方法:

  • insert()方法添加数据
  • update()方法更新数据
  • delete()方法删除数据
  • query()方法查询数据

不同于SQLiteDatabase,ContentResolver中的增删改查不接收表明参数,而是使用一个Uri参数,这个参数被称为内容URI。

内容URI由两部分组成:authoritypath

  • authority是用于区分不同的应用程序;一般采用包名的方式命名(com.example.test.provider)。
  • path用于区分不同的表;添加到authority后面(com.example.test.provider/table1)

然后加上协议头

content://com.example.test.provider/table1
content://com.example.test.provider/table2

使用Uri.parse()方法解析成Uri对象。

val uri = Uri.parse("content://com.example.test.provider/table1")

查询数据

val cursor = contentResolver.query(
    uri,
    projection,
    selection,
    selectionArgs,
    sortOrder
)
query()方法参数对应SQL部分描述
urifrom table_name指定查询某个应用程序的某一张表
projectionselection column1, column2指定查询的列名
selectionwhere column = value指定where的约束条件
selectionArgs-为where种的占位符提供具体的值
sortOrderorder by column1, column2指定查询结果的排序方式

查询完成后返回的是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"))

删除:

contentResolver.delete(uri, "column2 = ?", arrayOf("1"))

15.2 读取联系人

使用一个ListView来显示读取的数据

class MainActivity : AppCompatActivity() {
​
    private lateinit var binding: ActivityMainBinding
    private val contactsList = ArrayList<String>()
    private lateinit var adapter: ArrayAdapter<String>
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
​
        adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, contactsList)
        binding.contactList.adapter = adapter
        val readContactsPer = ContextCompat.checkSelfPermission(this, android.Manifest.permission.READ_CONTACTS)
        if (readContactsPer != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.READ_CONTACTS), 2)
        } else {
            readContacts()
        }
​
    }
​
    @SuppressLint("Range")
    private fun readContacts() {
        // 查询联系人数据
        contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null)?.apply {
            while (moveToNext()) {
                // 获取联系人姓名
                val name = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME
                val displayName = getString(getColumnIndex(name))
                // 获取联系人手机号
                val number = ContactsContract.CommonDataKinds.Phone.NUMBER
                val displayNumber = getString(getColumnIndex(number))
                contactsList.add("$displayName:  $displayNumber")
            }
            adapter.notifyDataSetChanged()
            close()
        }
    }
​
    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) {
                    call()
                } else {
                    Toast.makeText(this, "You denied Call the permission", Toast.LENGTH_SHORT).show()
                }
            }
            2 -> {
                if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    readContacts()
                } else {
                    Toast.makeText(this, "You denied Contacts the permission", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
}

15.3 创建自己的ContentProvider

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.filepersistencetest.provider"
    private var dbHelper: MyDatabaseHelper? = null
​
    private val uriMatcher 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 onCreate(): Boolean {
        context?.let {
            dbHelper = MyDatabaseHelper(it, "BookStore.db", 2)
            return true
        }
        return false
    }
​
    override fun query(
        uri: Uri, projection: Array<String>?, selection: String?,
        selectionArgs: Array<String>?, sortOrder: String?
    ): Cursor? {
        var cursor: Cursor? = null
        dbHelper?.let {
            val db = it.readableDatabase
            cursor = when (uriMatcher.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
            }
        }
        return cursor
    }
​
    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        var uriReturn: Uri? = null
        dbHelper?.let {
            val db = it.writableDatabase
            uriReturn = when (uriMatcher.match(uri)) {
                bookDir, bookItem -> {
                    val newBookId = db.insert("Book", null, values)
                    Uri.parse("content://$authority/book/$newBookId")
                }
                categoryDir, categoryItem -> {
                    val newCategoryId = db.insert("Category", null, values)
                    Uri.parse("content://$authority/category/$newCategoryId")
                }
                else -> null
            }
        }
        return uriReturn
    }
​
    override fun update(
        uri: Uri, values: ContentValues?, selection: String?,
        selectionArgs: Array<String>?
    ): Int {
        var updateRows: Int = 0
        dbHelper?.let {
            val db = it.writableDatabase
            updateRows = when (uriMatcher.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 -> 0
            }
        }
        return updateRows
    }
​
    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
        var deleteRows: Int = 0
        dbHelper?.let {
            val db = it.writableDatabase
            deleteRows = when (uriMatcher.match(uri)) {
                bookDir -> {
                    db.delete("Book", selection, selectionArgs)
                }
                bookItem -> {
                    val bookId = uri.pathSegments[1]
                    db.delete("Book", "id = ?", arrayOf(bookId))
                }
                categoryDir -> {
                    db.delete("Category", selection, selectionArgs)
                }
                categoryItem -> {
                    val categoryId = uri.pathSegments[1]
                    db.delete("Category", "id = ?", arrayOf(categoryId))
                }
                else -> 0
            }
        }
        return deleteRows
    }
​
    override fun getType(uri: Uri): String? {
        var type: String? = null
        type = when (uriMatcher.match(uri)) {
            bookDir -> "vnd.android.cursor.dir/vnd.$authority.book"
            bookItem -> "vnd.android.cursor.item/vnd.$authority.book"
            categoryDir -> "vnd.android.cursor.dir/vnd.$authority.category"
            categoryItem -> "vnd.android.cursor.item/vnd.$authority.category"
            else -> null
        }
        return type
    }
​
​
}

另一个应用程序操作

class MainActivity : AppCompatActivity() {
​
    private lateinit var binding: ActivityMainBinding
    private val bookUriString = "content://com.example.filepersistencetest.provider/book"
    var bookId: String? = null
​
    @RequiresApi(Build.VERSION_CODES.R)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
​
        binding.addData.setOnClickListener {
            addData()
        }
        binding.queryData.setOnClickListener {
            queryData()
        }
        binding.updateData.setOnClickListener {
            updateData()
        }
        binding.deleteData.setOnClickListener {
            deleteData()
        }
    }
​
    fun addData() {
        val uri = Uri.parse(bookUriString)
        val values = contentValuesOf(
            "name" to "A Clash Of Kings",
            "author" to "George Martin",
            "pages" to 1040,
            "price" to 22.85
        )
        val newUri = contentResolver.insert(uri, values)
        bookId = newUri?.pathSegments?.get(1)
    }
    fun queryData() {
        val uri = Uri.parse(bookUriString)
        contentResolver.query(uri, null, null, null, null)?.apply {
            while (moveToNext()) {
                val name = getString(getColumnIndexOrThrow("name"))
                val author = getString(getColumnIndexOrThrow("author"))
                val pages = getInt(getColumnIndexOrThrow("pages"))
                val price = getDouble(getColumnIndexOrThrow("price"))
                Log.d("MainActivity", "info: $name, $author, $pages, $price")
            }
            close()
        }
    }
    @RequiresApi(Build.VERSION_CODES.R)
    fun updateData() {
        bookId?.let {
            val uri = Uri.parse("$bookUriString/$it")
            val values = contentValuesOf(
                "name" to "A Storm Of Swords",
                "pages" to 1216,
                "price" to 25.12
            )
            contentResolver.update(uri, null, null)
        }
    }
    fun deleteData() {
        bookId?.let {
            val uri = Uri.parse("$bookUriString/$it")
            contentResolver.delete(uri, null, null)
        }
    }
}