赞
踩
ContentProvider主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。
目前,使用ContentProvider是Android实现跨程序共享数据的标准方式。
Android系统的权限机制设计得非常简单,就是用户如果认可你所申请的权限,就会安装你的程序,如果不认可你所申请的权限,那么拒绝安装就可以了。
而在Android 6.0系统中新增了运行时权限功能。
现在用户不需要在安装软件的时候一次性授权所有申请的权限,而是可以在软件的使用过程中再对某一项权限申请进行授权。
比如一款相机应用在运行时申请了地理位置定位权限,就算拒绝了这个权限,也应该可以使用这个应用的其他功能,而不是像之前那样直接无法安装它。
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) makeCall.setOnClickListener { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CALL_PHONE), 1) } else { call() } } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<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 the permission", 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() } } }
ContentResolver中的增删改查方法都不接收表名参数,而是使用一个Uri参数代替,这个参数被称为内容URI。
内容URI给ContentProvider中的数据建立了唯一标识符,它主要由两部分组成:authority和path。
内容URI最标准的格式如下:
content://com.example.app.provider/table1
content://com.example.app.provider/table2
得到了内容URI字符串之后,我们只需要调用Uri.parse()方法,就可以将内容URI字符串解析成Uri对象了。
val uri = Uri.parse(“content://com.example.app.provider/table1”)
读取系统联系人
读取系统联系人示例写法如下(省略了申请运行时权限部分):
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) contactsView.adapter = adapter …… readContacts() } …… private fun readContacts() { // 查询联系人数据 contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null)?.apply { while (moveToNext()) { // 获取联系人姓名 val displayName = getString(getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)) // 获取联系人手机号 val number = getString(getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)) contactsList.add("$displayName\n$number") } adapter.notifyDataSetChanged() close() } } }
创建ContentProvider的步骤:
ContentProvider类中有6个抽象方法,我们在使用子类继承它的时候,需要将这6个方法全部重写。
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 } }
创建ContentProvider的步骤:
onCreate() .初始化ContentProvider的时候调用,通常会在这里完成对数据库的创建和升级等操作,返回true表示ContentProvider初始化成功,返回false则表示失败。
query () 从contentrovider中查询数据,uri参数用于确定查询那张表,projection 参数用于确定查询那些列,selection和selectionArgs参数用于约束查询哪些行,sortOrder参数用于对结果进行排序,查询的结果存放在Cursor对象中返回。
insert()。向ContentProvider中添加一条数据。uri参数用于确定要添加到的表,待添加的数据保存在values参数中。添加完成后,返回一个用于表示这条新记录的URI。
update()。更新ContentProvider中已有的数据。uri参数用于确定更新哪一张表中的数据,新数据保存在values参数中,selection和selectionArgs参数用于约束更新哪些行,受影响的行数将作为返回值返回。
delete()。从ContentProvider中删除数据。uri参数用于确定删除哪一张表中的数据,selection和selectionArgs参数用于约束删除哪些行,被删除的行数将作为返回值返回。
getType()。根据传入的内容URI返回相应的MIME类型。
实现跨进程数据共享:
借助UirMatcher 这个类能够实现匹配内容URi的功能。
当调用UriMaticher的match()方法时,可以将一个Uri对象传入,返回值是某个能够匹配这个Uri对象所对应的自定义代码,利用这个代码,就可以判断出调用方期望访问的是哪个表中的数据了:
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表中的单条数据 } } … } … }
实现跨程序数据共享
getType()方法是所有的ContentProvider都必须提供的一个方法,用于获取Uri对象所对应的MIME类型。一个内容URI所对应的MIME字符串主要由3部分组成,Android对这3个部分做了如下格式规定
必须以vnd开头。
如果内容URI以路径结尾,则后接android.cursor.dir/;如果内容URI以id结尾,则后接android.cursor.item/。
最后接上vnd..
所以,对于content://com.example.app.provider/table1这个内容URI,它所对应的MIME类型就可以写成:vnd.android.cursor.dir/vnd.com.example.app.provider.table1
对于content://com.example.app.provider/table1/1这个内容URI,它所对应的MIME类型就可以写成:vnd.android.cursor.item/vnd.com.example.app.provider.table1
实现跨进程数据共享在这里插入代码片
getType()方法中的逻辑,如下所示:
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
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。