一 ContentProvider简介
ContentProvider主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。
- Android现在将常用的权限大致归成了两类:普通权限、危险权限
- 普通权限:
不会直接威胁到用户的隐私和安全的权限,对于这部分权限的申请,系统会自动帮我们进行授权,不需要用户手动操作,只需要在AndroidManifest.xml中声明。 - 危险权限:
可能会触及用户隐私或者对设备安全性造成影响的权限,如获取设备联系人信息、定位设备的地理位置等,对于这部分权限申请,必须由用户手动授权才可以,否则程序就无法使用相应的功能。大致危险权限如下:
Android危险权限
编号 权限组 权限
1.CALENDAR
READ_CALENDAR
WRITE_CALENDAR
2.CAMERA
CAMERA
3.CONTACTS
READ_CONTACTS
、WRITE_CONTACTS
、GET_ACCOUNTS
4.LOCATION
ACCESS_FINE_LOCATION
、ACCESS_COARSE_LOCATION
5.MICROPHONE
RECORD_AUDIO
6.PHONE
READ_PHONE_STATE CALL_PHONE READ_CALL_LOG
、WRITE_CALL_LOG ADD_VOICEMAIL USE_SIP
、PROCESS_OUTGOING_CALLS
7.SENSORS
BODY_SENSORS
8.SMS
SEND_SMS
、RECEIVE_SMS
、READ_SMS
、RECEIVE_WAP_PUSH
、RECEIVE_MMS
9.STORAGE
READ_EXTERNAL_STORAGE
、WRITE_EXTERNAL_STORAGE
二 ContentResolve
- URI
内容URI给Contentprovider中的数据建立了唯一标识符,它主要有两部分组成:authority和path。authority是用于对不同的应用程序做区分的,一般为了避免冲突,会采用应用包名的方式命名。比如某个应用的包名是com.example.app,那么该应用对应的authority就可以命名为com.example.app.provider。path则是用于对同一应用程序中不同的表做区分的,通常会添加到authority后面。比如某个应用的数据库里面有两张表table1和table2,这时就可以将path分别命名为/table1和/table2,然后把authority和path进行组合,标准如下:
content://com.example.app.provider/table1
content://com.example.app.provider/table2
很清楚地表达了想要访问哪个程序中哪张表里的数据
- 得到内容URI字符串后,需要将它解析成Uri对象才可以作为参数传入
val uri = Uri.parse("content://com.example.app.provider/table1")
- 增删改查与sqlite类似
- 读取系统联系人
创建自己的ContentProvider
- 一个标准的URI写法:
content://com.example.app.provider/table1:
表示调用方期望访问的是com.example.app这个应用的table1表中的数据。
除此之外,我们还可以在这个内容uri后面加上一个id,例如:
content://com.example.app.provider/table1/1:
表示调用方期望访问的是com.example.app这个应用的table1表中id为1的数据 - 我们还可以使用通配符分别匹配这两种格式的内容URI,规则如下:
(*)表示匹配任意长度的任意字符
(#)表示匹配任意长度的数字
一个能够匹配任意表的内容URI格式:
content://com.example.app.provider/*
一个能够匹配table1表中任意一行数据的内容URI格式:
content://com.example.app.provider/table1/*
接着,再借助UriMatcher这个类就可以轻松地实现匹配内容URI的功能。UriMatcher中提供了一个addURI()方法,它接收三个参数,可以分别把authority、path和一个自定义代码传进去。
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.kotlin_contentprovider.provider", "table1", table1Dir)
uriMatcher.addURI("com.example.kotlin_contentprovider.provider", "table1/#", table1Item)
uriMatcher.addURI("com.example.kotlin_contentprovider.provider", "table2", table2Dir)
uriMatcher.addURI("com.example.kotlin_contentprovider.provider", "table2/#", table2Item)
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
TODO("Not yet implemented")
}
override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?): Cursor? {
when(uriMatcher.match(uri)){
table1Dir -> {
//查询table1表中的所有数据
}
table1Item -> {
//查询table1表中的单条数据
}
table2Dir -> {
//查询table2表中的所有数据
}
table2Item -> {
//查询table2表中的单条数据
}
}
}
override fun onCreate(): Boolean {
TODO("Not yet implemented")
}
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int {
TODO("Not yet implemented")
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
TODO("Not yet implemented")
}
override fun getType(uri: Uri): String? =
when(uriMatcher.match(uri)){
table1Dir -> "vnd.android.cursor.dir/vnd.com.example.kotlin_contentprovider.provider.table1"
table1Item -> "vnd.android.cursor.item/vnd.com.example.kotlin_contentprovider.provider.table1"
table2Dir -> "vnd.android.cursor.dir/vnd.com.example.kotlin_contentprovider.provider.table2"
table2Item -> "vnd.android.cursor.item/vnd.com.example.kotlin_contentprovider.provider.table2"
else -> null
}
}
getType()方法,它是所有的ContentProvider都必须提供的一个方法,用于获取Uri对象所对应的MIME类型,一个内容URI所对应的MIME字符串主要由3部分组成,Android对这3个部分做了如下规定:
- 必须以vnd开头
- 如果内容URI以路径结尾,则后接android.cursor.dir/;如果内容URI以id结尾,则后接android.cursor.item/。
- 最后接上vnd.<authority>.<path>
所以,对于com.example.kotlin_contentprovider.provider.table1这个内容uri,它所对应的MIME类型就可以写成: - vnd.android.cursor.dir/vnd.com.example.kotlin_contentprovider.provider.table1
实现跨程序数据共享
class DatabaseProvider : ContentProvider() {
private val bookDir = 0
private val bookItem = 1
private val categoryDir = 2
private val categoryItem = 3
private val authority = "com.example.kotlin_sqlite.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
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 query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String?)=
dbHelper?.let {
val db = it.writableDatabase
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 update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?) = dbHelper?.let {
val db = it.writableDatabase
val 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
}
updateRows
} ?: 0
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?) = dbHelper?.let {
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.kotlin_sqlite.provider.book"
bookItem -> "vnd.android.cursor.item/vnd.com.example.kotlin_sqlite.provider.book"
categoryDir -> "vnd.android.cursor.dir/vnd.com.example.kotlin_sqlite.provider.category"
categoryItem -> "vnd.android.cursor.item/vnd.com.example.kotlin_sqlite.provider.category"
else -> null
}
}
- 综合利用Getter方法语法糖、?.操作符、let函数、?:操作符以及单行代码函数语法糖。
- 访问自定义ContentProvider的程序
注意:要在访问其他程序的ContentProvider的程序Manifest中加入访问哪个程序的包名 - Manifest.xml
<queries>
<package android:name="com.example.kotlin_sqlite"/>
</queries>
class MainActivity : AppCompatActivity() , View.OnClickListener{
private lateinit var addData: Button
private lateinit var queryData:Button
private lateinit var updateData: Button
private lateinit var deleteData:Button
private val mAuthority = "content://com.example.kotlin_sqlite"
private var bookId: String ?= null
private val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
}
private fun initView() {
addData = findViewById(R.id.addData)
queryData = findViewById(R.id.queryData)
updateData = findViewById(R.id.updateData)
deleteData = findViewById(R.id.deleteData)
addData.setOnClickListener(this)
queryData.setOnClickListener(this)
updateData.setOnClickListener(this)
deleteData.setOnClickListener(this)
}
override fun onClick(v: View?) {
when(v?.id){
R.id.addData -> add()
R.id.queryData -> select()
R.id.updateData -> change()
R.id.deleteData -> delete()
}
}
private fun delete() {
//删除数据
bookId?.let {
val uri = Uri.parse("$mAuthority/book/$it")
contentResolver.delete(uri, null,null)
}
}
private fun change() {
//更新数据
bookId?.let {
val uri = Uri.parse("$mAuthority/book/$it")
val values = contentValuesOf("name" to "Yan", "pages" to 1314520)
contentResolver?.update(uri,values,null,null)
}
}
private fun select() {
//查询数据
val uri = Uri.parse("$mAuthority/book")
contentResolver.query(uri,null,"name = ?", arrayOf("Yan"),null,null)
?.apply {
while (moveToNext()){
val name = getString(getColumnIndex("name"))
val author = getString(getColumnIndex("author"))
val pages = getString(getColumnIndex("pages"))
val price = getString(getColumnIndex("price"))
Log.e(TAG, "name is $name")
Log.e(TAG, "author is $author")
Log.e(TAG, "pages is $pages")
Log.e(TAG, "price is $price")
}
close()
}
}
private fun add() {
//添加数据
val uri = Uri.parse("$mAuthority/book")
val values = contentValuesOf("name" to "Yan", "author" to "Lvsu", "pages" to 1314, "price" to 1314)
val newUri = contentResolver.insert(uri, values)
bookId = newUri?.pathSegments?.get(1)
}
}