Android ContentProvider——访问其他程序的数据

ContentProVider

ContentProvider有两种用法,第一种是用现成的ContentProvider,另一种是创建自己的。

ContentResolver 基本用法

Insert、 update、 delete、 query
不同与SQLiteDataBase,ContentResolver的四种基本操作是不接受参数的,而是统一使用一个Uri参数代替
Uri由两部分组成:authority和path,authority是用于区分不同的包名,path是用于区分不同表:
比如此时当前包名是com.example.app.provider
则Uri参数可以是
content://com.example.app.provider/table1
content://com.example.app.provider/table2
代表着表table1和表table2,开头的content:是协议声明。

val uri = Uri.parse("content://com.example.app.provider/table1")
val cursor = contentResolver.query(
	uri,
	projection,//指定查询的列名
	selection,//指定where的约束条件
	selectionArgs,//为where的占位符提供具体的值
	sortOrder)//指定排序
while(cursor.moveToNext()){
	val column1 = cursor.getString(cursor.getColumnIndex("column1"))
	val column2 = cursor.getInt(cursor.getColumnIndex("column2"))
	}
	cursor,close()

这是添加

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

芝士更新

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

芝士删除

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

读取系统联系人

修改activity_main.xml中的代码

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:orientation="vertical"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent">  
  
   <ListView       android:layout_width="match_parent"  
       android:layout_height="match_parent"  
       android:id = "@+id/contactview"/>  
  
</LinearLayout>

简单放了一个ListView。

package com.example.contactstest  
  
import android.Manifest  
import android.annotation.SuppressLint  
import android.content.pm.PackageManager  
import androidx.appcompat.app.AppCompatActivity  
import android.os.Bundle  
import android.provider.ContactsContract  
import android.widget.Adapter  
import android.widget.ArrayAdapter  
import android.widget.Toast  
import androidx.core.app.ActivityCompat  
import androidx.core.content.ContextCompat  
import com.example.contactstest.databinding.ActivityMainBinding  
  
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)  
        val binding = ActivityMainBinding.inflate(layoutInflater)  
        setContentView(binding.root)  
        binding.contactview.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<out String>,  
        grantResults: IntArray  
    ) { 
    ...
    }  
    @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()  
        }  
    }  
}

创建自己的ContentProvider

可以通过新建一个类去继承ContentProvider,那么需要重写以下几个函数

class MyProvider:ContentProvider() {  
    override fun onCreate(): Boolean {  
        return false  
    }  
  
    override fun query(  
        uri: Uri,  
        projection: Array<out String>?,  
        selection: String?,  
        selectionArgs: Array<out String>?,  
        sortOrder: String?  
    ): Cursor? {  
       return null  
    }  
  
    override fun getType(uri: Uri): String? {  
        return null  
    }  
  
    override fun insert(uri: Uri, values: ContentValues?): Uri? {  
       return null  
    }  
  
    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {  
        return 0  
    }  
  
    override fun update(  
        uri: Uri,  
        values: ContentValues?,  
        selection: String?,  
        selectionArgs: Array<out String>?  
    ): Int {  
        return 0  
    }  
}
  1. onCreate:初始化时调用,对数据库的初始化和升级等操作,返回true则表示初始化成功。
  2. query:查询功能,参数格式前面有讲过。
  3. insert:value存放插入的值,uri表示要插入的表,结果返回一条URI。
  4. update:selections和selectionArgs用于确定更新的行数。
  5. delete:同上。
  6. getType:根据传入的Uri内容返回对应的MIME类型。
    这里回顾一下标准的Uri写法是:

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

除此之外还可以加上一个id

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

表示这个应用表table1中的id为1的数据。
通配符:

  • *表示任意长度的任意字符
  • #表示任意长度的数

识别Uri可以使用到UriMatcher类,其提供一个addURI()的方法,分别接收三个参数,一个是authority,一个是path,另一个是自定义字符。这样当调用match方法时,就可以辨认出是哪一个自定义的字符了。

class MyProvider:ContentProVider(){
private val table1Dir = 1  
private val table1Item = 2  
private val table2Dir = 3  
private val table2Item =4  
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<out String>?,  
    selection: String?,  
    selectionArgs: Array<out String>?,  
    sortOrder: String?  
): Cursor? {  
    when(uriMatcher.match(uri))  
    {  
        table1Dir ->{  
            //  
        }  
        table1Item ->{  
            //      
}  
        table2Dir -> {  
            //  
        }  
        table2Item -> {  
            //  
        }  
    }  
..
...}
}

getType(),前面说到用于获取Uri对象的MIME类型。
说明一下什么是MIME字符串,主要包括三部分。

  • 必须以vnd开头
  • 如果内容URI以路径结尾,则后接android.cursor.dir/,若是id结尾则后接android.cursor.item/
  • 最后接上vnd.< authority >.< path >
    例如对于 content://com.example.app.provider/table1的MIME类型是:
    vnd.android.cursor.dir/vnd.com.example.app.provider.table1
    修改getType代码如下:
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.table1"  
    else -> null  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值