android 内容提供者

android中的四大组件之一----内容提供者

      用于不同app之间的数据共享,比如我在A应用程序中有个数据库,而应用B需要用到这个数据库的某个表信息,由于权限问题我们是无法直接操作的,我们就可以在A应用中用ContentProvider来提供这个表的查询接口,这样也就做到了app之间的数据共享,还能让用户自定义接口提高安全性,避免了进程间调用,权限和开发成本问题。

先来复习一下Uri

Uri 通用资源标志符,Web上可用的每种资源 -HTML文档、图像、视频片段、程序等可由一个Uri进行定位
构由以下几个部分组成
scheme、authority、path、query和fragment组成
[scheme:][//host:port][path][?query][#fragment]——最详细的划分形式
以这种形式的字符串转换成Uri即可通过Uri类中的方法来获取各个部分,而ContentProvider就是用Uri来标识访问的是那个应用程序的那个表的哪个操作,如:content://com.example.databasetest.provider/book/2,android提供了一个UriMatch类进行Uri的匹配,这样其他应用程序就不能为所欲为的进行操作了

使用

接下来我们开始使用
这个例子我们用LitePal来进行数据库操作
创建model:

class Book : LitePalSupport() {
    var author: String? = null;
    var price: Int? = null;
    var pages: Int? = null;
    var name: String? = null;
}
class Category  : LitePalSupport() {
    var category_name : String? = null;
    var category_code : Int? = null;
}

配置文件

<litepal>
    <!--配置数据库名-->
    <dbname value="demo"></dbname>
    <!--配置版本-->
    <version value="1"></version>
    <!--加入模型类声明-->
    <list>
        <mapping class="com.example.contentprovider.Book"></mapping>
        <mapping class="com.example.contentprovider.Category"></mapping>
    </list>
</litepal>

然后先添加了几个数据方便后面使用

val connector = Connector()
book.author="Elizabeth3"
book.name="Learn3"
book.pages=190
book.price=100
println("haha${book.save()}")

创建内容提供者:
在这里插入图片描述
在这里插入图片描述
生成自定义内容提供者,可见实现几个方法都是针对于数据库操作的

class MyContentProvider : ContentProvider() {

    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
        TODO("Implement this to handle requests to delete one or more rows")
    }

    override fun getType(uri: Uri): String? {
        TODO(
            "Implement this to handle requests for the MIME type of the data" +
                    "at the given URI"
        )
    }

    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        TODO("Implement this to handle requests to insert a new row.")
    }

    override fun onCreate(): Boolean {
        TODO("Implement this to initialize your content provider on startup.")
    }

    override fun query(
        uri: Uri, projection: Array<String>?, selection: String?,
        selectionArgs: Array<String>?, sortOrder: String?
    ): Cursor? {
        TODO("Implement this to handle query requests from clients.")
    }

    override fun update(
        uri: Uri, values: ContentValues?, selection: String?,
        selectionArgs: Array<String>?
    ): Int {
        TODO("Implement this to handle requests to update one or more rows.")
    }
}

清单文件自动注册

<provider
            android:name=".MyContentProvider"
            android:authorities="con.example.contentprovider"
            android:enabled="true"
            android:exported="true"></provider>

内容提供者是根据Uri进行匹配的,可见在实现的增删改查方法上都传入了一个Uri对象,这个对象时其他应用程序在使用内容提供者的某个方法时传入的uri,因此我们得自己定义过滤条件防止滥操作:

companion object{
        val QUERY=1
        val DELETE=2
        val INSERT=3
        val UPDATE=4
        val AUTHORITY = "con.example.contentprovider"
        var mUriMatcher:UriMatcher? = null
        init {
            //定义匹配器 参数指定匹配不成功时的返回值
            mUriMatcher = UriMatcher(UriMatcher.NO_MATCH)
            //添加规则 第一个参数是刚刚配置的authorities,第二个参数不固定可自定义,第三个参数表示匹配成功的返回值
            mUriMatcher?.addURI(AUTHORITY, "queryAll", QUERY)
        }
    }

重写query方法,在里边进行Uri的匹配过滤

override fun query(
    uri: Uri, projection: Array<String>?, selection: String?,
    selectionArgs: Array<String>?, sortOrder: String?
): Cursor? {
    //先进行匹配,决定要走query方法的哪个逻辑
    val match = mUriMatcher?.match(uri)
    var cusor: Cursor? = null
    if (match == QUERY){
        cusor = LitePal.findBySQL("select * from book")
    }else{
        throw Exception("路径不匹配")
    }
    return cusor
}

应用程序二:

fun danxuan(view: View) {
        //定义Uri
        val uri = Uri.parse("content://con.example.contentprovider/queryAll")
        //定义内容解析者, 指定了执行内容提供者的query方法, 参数对应于内容提供者中对应方法的参数
        val cusor = contentResolver.query(uri, null, null, null, null)
        while (cusor!!.moveToNext()){
            val author= cusor.getString(1)
            val price = cusor.getInt(2)
            val pages = cusor.getInt(3)
            val name = cusor.getString(4)
            println("${author}-${price}-${pages}-${name}")
        }
    }

Uri的其他用法:

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

实例:
添加uri匹配:

mUriMatcher?.addURI(AUTHORITY, "queryAll/*", QUERYBYNAME)

query方法:

override fun query(
        uri: Uri, projection: Array<String>?, selection: String?,
        selectionArgs: Array<String>?, sortOrder: String?
    ): Cursor? {
        //先进行匹配
        val match = mUriMatcher?.match(uri)
        var cusor: Cursor? = null
        if (match == QUERY){
            cusor = LitePal.findBySQL("select * from book")
        } else if (match == QUERYBYNAME){
            val s = "select * from book where name like \"${uri.pathSegments[1]}\""
            cusor = LitePal.findBySQL(s)
        }
        else{
            throw Exception("路径不匹配")
        }

        return cusor
    }

应用二中内容解析者传入的uri:

val uri = Uri.parse("content://con.example.contentprovider/queryAll/Learn2")

内容观察者

用于监听内容提供者的修改
需要在清单文件添加权限:
<uses-permission android:name="android.permission.READ_SMS" />

inner class MyContentObserver(handler: Handler?) : ContentObserver(handler) {
    //当观察的内容发生改变的时候调用
    override fun onChange(selfChange: Boolean) {
        Toast.makeText(this@MainActivity, "数据库变化", Toast.LENGTH_SHORT).show()
        super.onChange(selfChange)
    }
}
//参数true代表要查询uri及其子集
contentResolver.registerContentObserver(uri, true, MyContentObserver(Handler()))

记得在内容提供者对应中操作完成后进行通知:

//第二个参数为null不知道具体观察者,所以会通知所有观察者
context.contentResolver.notifyChange(uri, null)
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值