# ContentProvider 必会必知
本节给大家带来的是 Android 四大组件中的最后一个 ——ContentProvider (内容提供者),可能部分读者 有疑问了,说到这个 ContentProvider,我们什么时候 会用到他呢?有下面这两种:
- **1.** 我们想在自己的应用中访问别的应用,或者说一些 ContentProvider 暴露给我们的一些数据, 比如手机联系人,短信、相册等!我们想对这些数据进行读取或者修改,这就需要用到 ContentProvider 了!
- **2.** 我们自己的应用,想把自己的一些数据暴露出来,给其他的应用进行读取或操作,我们也可以用到 ContentProvider,另外我们可以选择要暴露的数据,就避免了我们隐私数据的的泄露!
好像好流弊的样子,其实用起来也很简单
# 权限申请
从 android6.0 开始,凡是涉及用户隐私的权限 (读写短信,读写联系人,拍摄,录音等等),都需要运行时申请,弹窗提醒用户是否授权。用户不授权则无法继续操作,而且今年工信部对于违规收集,申请用户权限的 APP 查的非常严格,不定期抽查,抽查有问题的必须按期整改,否则强制下架。
# 首先在 AndroidManifest.xml 中声明读取短信的权限
<uses-permission android:name="android.permission.READ_CONTACTS"/> |
# 运行时动态申请权限,请求用户授权
- ActivityCompat.checkSelfPermission ():检查权限是否已授权,如果没授权则需要向用户申请
- ActivityCompat.requestPermissions ():发起权限申请,会弹出对话框
- ActivityCompat.shouldShowRequestPermissionRationale (): 检查用户是否已经永久拒绝,如果用户已永久拒绝某个权限的申请,即便再调用 ActivityCompat.requestPermissions,系统也不会弹框向用户申请了。此时需要自己弹对话框,引导用户去开启授权
- onRequestPermissionsResult:处理权限授权的结果
- 完整的权限申请案例
class PermissionActivity : AppCompatActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
if (ActivityCompat.checkSelfPermission(this, | |
android.Manifest.permission.READ_SMS | |
) != PackageManager.PERMISSION_GRANTED) { | |
ActivityCompat.requestPermissions( this, arrayOf(android.Manifest.permission.READ_SMS),100) | |
} else { | |
// 如果已授权则可以直接读取通讯录了 | |
getContacts() | |
} | |
} | |
override fun onRequestPermissionsResult( | |
requestCode: Int, | |
permissions: Array<out String>, | |
grantResults: IntArray | |
) { | |
super.onRequestPermissionsResult(requestCode, permissions, grantResults) | |
// 处理权限申请的结果 | |
if (requestCode==100&&permissions[0]== android.Manifest.permission.READ_SMS){ | |
if (grantResults[0]==PackageManager.PERMISSION_GRANTED){ | |
// 如果请求结果是授权了,则可以继续操作 | |
getContacts() | |
}else{ | |
// 如果本次申请权限,未得到授权,则 toast 提示,终止读取的操作。 | |
Toast.makeText(this, "通讯录权限被拒绝,无法读取联系人", Toast.LENGTH_SHORT).show() | |
} | |
} | |
} | |
} |
# 读取通讯录联系人
表名 | 说明 |
---|---|
content://com.android.contacts/data/phones | 读取联系人的表的名字 |
字段 | 说明 |
------------ | ------ |
display_name | 用户名 |
Data1 | 手机号 |
fun getContacts() { | |
//①查询 raw_contacts 表获得联系人的 id | |
val resolver = contentResolver | |
val uri =Uri.parse("content://com.android.contacts/data/phones") | |
// 查询联系人数据 | |
val cursor = resolver.query(uri, null, null, null, null)!! | |
while (cursor.moveToNext()) { | |
// 获取联系人姓名,手机号码 | |
val cName: String = | |
cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)) | |
val cNum: String = | |
cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)) | |
Log.e("ContentProvider", "姓名:$cName") | |
Log.e("ContentProvider", "号码:$cNum") | |
Log.e("ContentProvider", "======================") | |
} | |
cursor.close() | |
} |
运行结果:
姓名:china unicom | |
号码:10010 | |
====================== | |
姓名:zhang san | |
号码:10086 |
# 通信录插入联系人
- AndroidManifest.xml 声明权限
<uses-permission android:name="android.permission.WRITE_CONTACTS"/> |
- 插入联系人
表名 | 说明 |
---|---|
content://com.android.contacts/data/data | 插入联系人的表的名字 |
content://com.android.contacts/data/raw_contacts | 插入联系人的原始表的名字 |
fun insertContact() { | |
val values = ContentValues() | |
/* | |
* 首先向 RawContacts.CONTENT_URI 执行一个空值插入,目的是获得系统返回的 rawContactId | |
* 这时后面插入 data 表的数据,才能使插入的联系人在通讯录里面可见 | |
*/ | |
val rawContactUri: Uri = contentResolver!!.insert(RawContacts.CONTENT_URI, values)!! | |
val rawContactId = ContentUris.parseId(rawContactUri) | |
// 往 data 表里写入姓名数据 | |
values.clear() | |
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId) | |
values.put(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE) // 内容类型 | |
values.put(StructuredName.GIVEN_NAME, "李四") | |
contentResolver.insert(ContactsContract.Data.CONTENT_URI, values) | |
// 往 data 表里写入电话数据 | |
values.clear() | |
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId) | |
values.put(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE) | |
values.put(Phone.NUMBER, "13921009789") | |
values.put(Phone.TYPE, Phone.TYPE_MOBILE) | |
contentResolver.insert(ContactsContract.Data.CONTENT_URI, values) | |
// 往 data 表里写入 Email 的数据 | |
values.clear() | |
values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId) | |
values.put(ContactsContract.Data.MIMETYPE, Email.CONTENT_ITEM_TYPE) | |
values.put(Email.DATA, "lisi@qq.com") | |
values.put(Email.TYPE, Email.TYPE_WORK) | |
contentResolver | |
.insert(ContactsContract.Data.CONTENT_URI, values) | |
} |
# 更新联系人信息
- 根据手机号获取联系人在通讯录的 contact_id
fun getContactIdByPhone(phone:Long): String { | |
val uri =Uri.parse("content://com.android.contacts/data/phones/filter/$phone") | |
val cursor = contentResolver.query( | |
uri, | |
arrayOf(ContactsContract.Data.CONTACT_ID), | |
null, | |
null, | |
null | |
)!! | |
if (cursor.moveToFirst()) { | |
return cursor.getString(0) | |
} | |
return "" | |
} |
- 更新联系人的姓名
fun update(){ | |
val contact_id = getContactIdByPhone(100001) | |
val values = ContentValues() | |
values.put(ContactsContract.Data.MIMETYPE, | |
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE | |
) // 内容类型 | |
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, "lisi") | |
contentResolver.update( | |
Uri.parse("content://com.android.contacts/data"), | |
values, | |
"${ContactsContract.Data.CONTACT_ID}=?", | |
arrayOf(contact_id) | |
) | |
} |
# 删除联系人
- 根据姓名删除联系人
contentResolver.delete(RawContacts.CONTENT_URI,CommonDataKinds.Phone.DISPLAY_NAME+"=?", arrayOf("test")); |
- 根据手机号删除联系人
val contact_id = getContactIdByPhone(1111) | |
contentResolver.delete(RawContacts.CONTENT_URI,CommonDataKinds.Phone.CONTACT_ID+"=?", arrayOf(contact_id)); |
# 读取收件箱所有短信
字段 | 说明 |
---|---|
address | 发件人地址,即手机号,如 + 8613811810000 |
person | 发件人地址,即手机号,如 + 8613811810000 |
date | 日期,long 型,如 1256539465022,可以对日期显示格式进行设置 |
protocol | 协议 0 :SMS_RPOTO 短信,1:MMS_PROTO 彩信 |
read | 是否阅读 0: 未读,1: 已读 |
type | 短信类型 1: 接收到的短信,2: 发出的短信 |
body | 短信具体内容 |
fun getMsgs() { | |
val uri: Uri = Uri.parse("content://sms/") | |
val resolver = contentResolver | |
// 获取的是哪些列的信息 | |
val cursor: Cursor = resolver.query( | |
uri, | |
arrayOf("address", "date", "type", "body"), | |
null, | |
null, | |
null | |
)!! | |
while (cursor.moveToNext()) { | |
val address: String = cursor.getString(0) | |
val date: String = cursor.getString(1) | |
val type: String = cursor.getString(2) | |
val body: String = cursor.getString(3) | |
Log.e("ContentProvider", "地址:$address") | |
Log.e("ContentProvider", "时间:$date") | |
Log.e("ContentProvider", "类型:$type") | |
Log.e("ContentProvider", "内容:$body") | |
Log.e("ContentProvider", "======================") | |
} | |
cursor.close() | |
} |
note
短信相关的其它操作 uri
content://sms/ 所有短信
content://sms/inbox 收件箱
content://sms/sent 已发送
content://sms/draft 草稿
content://sms/outbox 发件箱
content://sms/failed 发送失败
content://sms/queued 待发送列表
运行结果:
收件人:10010 | |
发送时间:Mon Jun 07 22:13:50 GMT+08:00 2021 | |
短信内容:I'm Android ,I want. to. send message to you | |
====================== | |
收件人:10086 | |
发送时间:Mon Jun 07 22:13:53 GMT+08:00 2021 | |
短信内容:this message is send to 10086 by Android |
note
我们能不能运行时往收件箱里写入一条短信呢?
从 5.0 开始第三方 APP 无法插入短信,所以死了这条心吧