Android ContentProvider基础知识学习笔记

1、动态申请权限
xml 复制代码
<uses-feature
        android:name="android.hardware.telephony"
        android:required="false" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
kotlin 复制代码
package com.jpc.contentprovidertest

import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

/**
 * 运行时权限的核
 * 心就是在程序运行过程中由用户授权我们去执行某些危险操作,程序是不可以擅自做主去执行
 * 这些危险操作的。因此,第一步就是要先判断用户是不是已经给过我们授权了,借助的是
 * ContextCompat.checkSelfPermission()方法。checkSelfPermission()方法接收两
 * 个参数:第一个参数是Context,这个没什么好说的;第二个参数是具体的权限名,比如打电
 * 话的权限名就是Manifest.permission.CALL_PHONE。然后我们使用方法的返回值和
 * PackageManager.PERMISSION_GRANTED做比较,相等就说明用户已经授权,不等就表示用
 * 户没有授权。
 * 如果已经授权的话就简单了,直接执行拨打电话的逻辑操作就可以了,这里我们把拨打电话的
 * 逻辑封装到了call()方法当中。如果没有授权的话,则需要调用
 * ActivityCompat.requestPermissions()方法向用户申请授权。
 * requestPermissions()方法接收3个参数:第一个参数要求是Activity的实例;第二个参数
 * 是一个String数组,我们把要申请的权限名放在数组中即可;第三个参数是请求码,只要是唯
 * 一值就可以了,这里传入了1。
 *
 */
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val makeCall = findViewById<Button>(R.id.btn_make_call)
        makeCall.setOnClickListener {
            if (ContextCompat.checkSelfPermission(this,
                    android.Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this,
                    arrayOf(android.Manifest.permission.CALL_PHONE), 1)
            } else {
                call()
            }
        }

    }

    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, "你拒绝了该权限申请", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
    private fun call(){
        try {
            val intent = Intent(Intent.ACTION_CALL)
            intent.data = Uri.parse("tel:10086")
            startActivity(intent)
        }catch (e: SecurityException){
            e.printStackTrace()
        }
    }
}
2、读取手机联系人
xml 复制代码
<uses-permission android:name="android.permission.READ_CONTACTS"/>
kotlin 复制代码
package com.jpc.contentprovidertest

import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.ContactsContract
import android.widget.ArrayAdapter
import android.widget.Button
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)
        adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, contactsList)
        val listView = findViewById<ListView>(R.id.contacts_view)
        listView.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) {
                    call()
                }else{
                    Toast.makeText(this, "你拒绝了该权限申请", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    private fun readContacts() {
        // 查询联系人数据
        // ContactsContract.CommonDataKinds.Phone类已经帮我们做好了封装,提供了一个
        //CONTENT_URI常量,而这个常量就是使用Uri.parse()方法解析出来的结果
        contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null,null)
            ?.apply {
                while (moveToNext()){
                    // 获取联系人姓名
                    val name = getString(getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
                    val phone = getString(getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER))
                    contactsList.add("$name\n$phone")
                }
                // 通知并刷新ListView
                adapter.notifyDataSetChanged()
                close() // 关闭Cursor对象
            }
    }
}
3、创建自己的ContentProvider
kotlin 复制代码
class MyProvider : ContentProvider() {
 override fun onCreate(): Boolean {
 return false
 }
 override fun query(uri: Uri, projection: Array<String>?, selection: String?,
 selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
 return null
 }
 override fun insert(uri: Uri, values: ContentValues?): Uri? {
 return null
 }
 override fun update(uri: Uri, values: ContentValues?, selection: String?,
 selectionArgs: Array<String>?): Int {
 return 0
 }
 override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
 return 0
 }
 override fun getType(uri: Uri): String? {
 return null
 }
}

(1) onCreate()。初始化ContentProvider的时候调用。通常会在这里完成对数据库的创建和升级等操作,返回true表示ContentProvider初始化成功,返回false则表示失败。

(2) query()。从ContentProvider中查询数据。uri参数用于确定查询哪张表,projection参数用于确定查询哪些列,selection和selectionArgs参数用于约束查询哪些行,sortOrder参数用于对结果进行排序,查询的结果存放在Cursor对象中返回。

(3) insert()。向ContentProvider中添加一条数据。uri参数用于确定要添加到的表,待添加的数据保存在values参数中。添加完成后,返回一个用于表示这条新记录的URI。

(4) update()。更新ContentProvider中已有的数据。uri参数用于确定更新哪一张表中的数据,新数据保存在values参数中,selection和selectionArgs参数用于约束更新哪些行,受影响的行数将作为返回值返回。

(5) delete()。从ContentProvider中删除数据。uri参数用于确定删除哪一张表中的数据,selection和selectionArgs参数用于约束删除哪些行,被删除的行数将作为返回值返回。

(6) getType()。根据传入的内容URI返回相应的MIME类型。

一个标准的内容URI写法是:

kotlin 复制代码
content://com.example.app.provider/table1

这就表示调用方期望访问的是com.example.app这个应用的table1表中的数据。

除此之外,我们还可以在这个内容URI的后面加上一个id,例如:

kotlin 复制代码
content://com.example.app.provider/table1/1

这就表示调用方期望访问的是com.example.app这个应用的table1表中id为1的数据。

内容URI的格式主要就只有以上两种,以路径结尾表示期望访问该表中所有的数据,以id结尾表示期望访问该表中拥有相应id的数据。我们可以使用通配符分别匹配这两种格式的内容URI,规则如下。

  • *表示匹配任意长度的任意字符。
  • #表示匹配任意长度的数字。

所以,一个能够匹配任意表的内容URI格式就可以写成:

kotlin 复制代码
content://com.example.app.provider/*

一个能够匹配table1表中任意一行数据的内容URI格式就可以写成:

kotlin 复制代码
content://com.example.app.provider/table1/#

再借助UriMatcher这个类就可以轻松地实现匹配内容URI的功能。UriMatcher中提供了一个addURI()方法,这个方法接收3个参数,可以分别把authority、path和一个自定义代码传进去。这样,当调用UriMatcher的match()方法时,就可以将一个Uri对象传入。

kotlin 复制代码
class MyProvider : ContentProvider() {
 private val table1Dir = 0
 private val table1Item = 1
 private val table2Dir = 2
 private val table2Item = 3
 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<String>?, selection: String?,
 selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
 when (uriMatcher.match(uri)) {
 table1Dir -> {
 // 查询table1表中的所有数据
 }
 table1Item -> {
 // 查询table1表中的单条数据
 }
 table2Dir -> {
 // 查询table2表中的所有数据
 }
 table2Item -> {
 // 查询table2表中的单条数据
 }
 }
 ...
 }
 ...
}
kotlin 复制代码
class MyProvider : ContentProvider() {
 ...
 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.table2"
 else -> null
 }
}
4、实战
kotlin 复制代码
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
 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() = context?.let {
 dbHelper = MyDatabaseHelper(it, "BookStore.db", 2)
 true
 } ?: false
www.blogss.cn
 override fun query(uri: Uri, projection: Array<String>?, selection: String?,
 selectionArgs: Array<String>?, sortOrder: String?) = dbHelper?.let {
 // 查询数据
 val db = it.readableDatabase
 val 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
 }
 cursor
 }
 override fun insert(uri: Uri, values: ContentValues?) = dbHelper?.let {
 // 添加数据
 val db = it.writableDatabase
 val 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
 }
 uriReturn
 }
 override fun update(uri: Uri, values: ContentValues?, selection: String?,
 selectionArgs: Array<String>?) = dbHelper?.let {
 // 更新数据
 val db = it.writableDatabase
 val updatedRows = 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
 }
 updatedRows
 } ?: 0
 override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?)
 = dbHelper?.let {
 // 删除数据
www.blogss.cn
 val db = it.writableDatabase
 val deletedRows = 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
 }
 deletedRows
 } ?: 0
 override fun getType(uri: Uri) = when (uriMatcher.match(uri)) {
 bookDir -> "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book"
 bookItem -> "vnd.android.cursor.item/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
 }
}
相关推荐
蒸鱼Yuzheng5 小时前
设备端性能工件可靠导出:断点续传、哈希、manifest 与失败恢复
android·自动化测试·python·adb·数据完整性
差不多的周周5 小时前
《骨架上下文:基于零镜头骨架的动作识别的骨架侧上下文提示学习》论文核心部分详解
学习
Century_Dragon6 小时前
从挂图到VR:汽修专业的一堂新能源原理课可以怎么上~
学习
美狐美颜sdk6 小时前
直播APP接入美颜SDK后出现卡顿怎么办?从性能瓶颈寻找解决方案
android·人工智能·音视频·美颜sdk·直播美颜sdk
知识分享小能手6 小时前
C++ 学习教程,从入门到精通,C++对象和类 — 详细知识点总结(10)
开发语言·c++·学习
美狐美颜SDK开放平台7 小时前
直播APP开发实战:从摄像头调用到视频美颜sdk集成
android·人工智能·计算机视觉·音视频·直播美颜sdk
New农民工7 小时前
倒立摆训练秘籍:理解训练参数
学习
alexhilton7 小时前
生命周期感知的多模块启动模式
android·kotlin·android jetpack
L@ncor8 小时前
第五章 基于低代码平台的智能体搭建 · 学习笔记(Coze / Dify / FastGPT / n8n)
笔记·学习·低代码·agent·prompt工程
知潮网8 小时前
被嫌弃的五仁月饼,一吨千元在养猪场翻了身
笔记