Android - ContentProvider

一、简介

ContentProvider主要用于在不同的应用程序之间实现数据共享的功能,它提供了一套完整的 机制,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。目前,使 用ContentProvider是Android实现跨程序共享数据的标准方式。ContentProvider 可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险。

1.ContentResolver的基本用法

对于每一个应用程序来说,如果想要访问ContentProvider中共享的数据,就一定要借助 ContentResolver类,可以通过Context中的getContentResolver()方法获取该类的实 例。ContentResolver中提供了一系列的方法用于对数据进行增删改查操作,其中insert() 方法用于添加数据,update()方法用于更新数据,delete()方法用于删除数据,query()方 法用于查询数据。有没有似曾相识的感觉?没错,SQLiteDatabase中也是使用这几个方法进 行增删改查操作的,只不过它们在方法参数上稍微有一些区别。 不同于SQLiteDatabase,ContentResolver中的增删改查方法都是不接收表名参数的,而是 使用一个Uri参数代替,这个参数被称为内容URI。内容URI给ContentProvider中的数据建立 了唯一标识符,它主要由两部分组成:authority和path。authority是用于对不同的应用程序 做区分的,一般为了避免冲突,会采用应用包名的方式进行命名。比如某个应用的包名是 com.example.app,那么该应用对应的authority就可以命名为 com.example.app.provider。path则是用于对同一应用程序中不同的表做区分的,通常会添 加到authority的后面。比如某个应用的数据库里存在两张表table1和table2,这时就可以将 path分别命名为/table1和/table2,然后把authority和path进行组合,内容URI就变成了 com.example.app.provider/table1和com.example.app.provider/table2。不过,目前还 很难辨认出这两个字符串就是两个内容URI,我们还需要在字符串的头部加上协议声明。因此, 内容URI最标准的格式如下:

content://com.example.app.provider/table1 
content://com.example.app.provider/table2

在得到了内容URI字符串之后,我们还需要将它解析成Uri对象才可以作为参数传入。解析的方 法也相当简单,代码如下所示:

val uri = Uri.parse("content://com.example.app.provider/table1")

2.使用

val cursor = contentResolver.query(
 uri,
 projection,
 selection,
 selectionArgs,
 sortOrder)

 查询完成后返回的仍然是一个Cursor对象,这时我们就可以将数据从Cursor对象中逐个读取 出来了。读取的思路仍然是通过移动游标的位置遍历Cursor的所有行,然后取出每一行中相应 列的数据,代码如下所示:

 while (cursor.moveToNext()) { 
    val column1 = cursor.getString(cursor.getColumnIndex("column1")) 
    val column2 = cursor.getInt(cursor.getColumnIndex("column2")) 
} 
cursor.close()

掌握了最难的查询操作,剩下的增加、修改、删除操作就更不在话下了。我们先来看看如何向 table1表中添加一条数据,代码如下所示:

val values = contentValuesOf("column1" to "text", "column2" to 1) contentResolver.insert(uri, values)

可以看到,仍然是将待添加的数据组装到ContentValues中,然后调用ContentResolver的 insert()方法,将Uri和ContentValues作为参数传入即可

如果我们想要更新这条新添加的数据,把column1的值清空,可以借助ContentResolver的 update()方法实现,代码如下所示:

val values = contentValuesOf("column1" to "") 
contentResolver.update(uri, values, "column1 = ? and column2 = ?", arrayOf("text", "1")) 

注意,上述代码使用了selection和selectionArgs参数来对想要更新的数据进行约束,以 防止所有的行都会受影响。 最后,可以调用ContentResolver的delete()方法将这条数据删除掉,代码如下所示:

contentResolver.delete(uri, "column2 = ?", arrayOf("1"))

使用

//<uses-permission android:name="android.permission.READ_CONTACTS" />

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
     if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
     != PackageManager.PERMISSION_GRANTED) {
     ActivityCompat.requestPermissions(this,
     arrayOf(Manifest.permission.READ_CONTACTS), 1)
 } else {
     readContacts()
 }
 }
 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) {
     readContacts()
 } else {
     Toast.makeText(this, "You denied the permission",
     Toast.LENGTH_SHORT).show()
 }
 }
 }
 }
 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的使用

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这个参数,这个参数也正是调用ContentResolver的增删改查 方法时传递过来的。而现在我们需要对传入的uri参数进行解析,从中分析出调用方期望访问的 表和数据。

猜你喜欢

转载自blog.csdn.net/m0_59482482/article/details/129718728