Android 访问其他程序中的数据

ContentProvider 的用法一般有两种:

一种是使用现有的ContentProvider 读取和操作相应程序中的数据;

另一种是创建自己的ContentProvider ,给程序的数据提供外部访问接口。

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可以非常清楚地表达我们想要访问哪个程序中哪张表里的数据。也正是因此,ContentResolver 中的增删改查方法才都接收Uri对象作为参数。如果使用表名的话,系统将无法得知我们期望访问的是哪个应用程序里的表。 在得到了内容URI字符串之后,我们还需要将它解析成Uri对象才可以作为参数传入。解析的方 法也相当简单,代码如下所示:

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

只需要调用Uri.parse()方法,就可以将内容URI字符串解析成Uri对象了。

现在我们就可以使用这个Uri对象查询table1 表中的数据了,代码如下所示:

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

这些参数和SQLiteDatabase 中query()方法里的参数很像,但总体来说要简单一些,毕竟这是在访问其他程序中的数据,没必要构建过于复杂的查询语句。

query()方法的参数说明

查询完成后返回的仍然是一个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"))

调用ContentResolver 的delete()方法将这条数据删除掉,代码如下所示:

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

读取系统联系人

修改activity_main.xml 中的代码,如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/contactsView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></ListView>
</LinearLayout> 

修改MainActivity 中的代码,如下所示:

package com.xc.helloworld

import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.provider.ContactsContract
import android.util.Log
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import kotlinx.android.synthetic.main.activity_main.*
import java.util.*

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

    @SuppressLint("Range")
    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()
        }
    }

}

修改 AndroidManifest.xml 中的代码,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

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

创建自己的ContentProvider 

...

 

posted @ 2023-01-19 09:03  草木物语  阅读(196)  评论(0编辑  收藏  举报