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
}
}