Android四大组件——Content Provider

一、概述

Content Provider(内容提供者)是安卓四大组件之一,用于在不同应用之间共享数据的机制。它封装了数据源,并提供了一套标准的接口,允许其他应用以一种统一的方式访问和操作这些数据。其特点如下:

1.数据共享:Content Provider允许不同的应用共享数据。例如,一个应用可以通过Content Provider访问另一个应用的联系人、图片、视频等数据。

2.数据封装:Content Provider封装了底层的数据存储实现,可以是SQLite数据库、文件系统、网络资源等。应用只需通过Content Provider接口进行数据操作,而不需要关心数据的具体存储方式。

3.标准化接口:Content Provider使用一套标准的CRUD(Create、Read、Update、Delete)操作接口来访问数据,包括insert()、query()、update()和delete()方法。这些方法类似于SQL操作,开发者可以通过这些接口对数据进行增删改查。

使用Content Provider(内容提供者)可以让外部应用以安全的方式访问数据。它不仅允许程序访问另一个程序中的数据,也可以规定该程序能够访问哪些数据,因此能够避免隐私泄露。

但是,程序不能直接操作Content Provider,而是借助名为Content Resolver的类,如下图所示。通过Content Resolver B程序才能访问A程序暴露给Content Provider的数据。

除了Content Resolver外,使用Content Provider还涉及数据模型和URI。

1.数据模型

Content Provider 使用基于数据库模型的简单表格来提供需要共享的数据,在该表格中,每一行表示一条记录,而每一列代表特定类型和含义的数据,并且其中每一条数据记录都包含一个名为“_ID”的字段类标识每条数据。

2.URI

数据模型是一种类似于数据表的数据结构,是Content Provider的数据组织形式。因此,显然也存在一种增删改查机制,对数据进行处理。Content Resolver通过URI实现增删改查。URI(统一资源标识符,Uniform Resource Identifier)是一种用于标识互联网资源的字符串。它提供了一种标准化的方法来表示和访问网络资源。在这里,URI用于为Content Provider中的数据提供唯一标识。

二、创建Content Provider

可以通过Android Studio 创建Content Provider,类似于其它组件

在创建时必须指定URI,一般选取包名即可。(包名一般是公司域名取反,因此可确保唯一)

Content Provider的表现形式依然是安卓中的类。某个内容提供者需继承自Content Provider,并且要实现不同的方法。

on Create:创建内容提供者时触发

insert delete update query:当Content Resolver根据URI实现增删改查时触发的回调

get Type:返回某URI的MIME,即数据类型,例如txt、jpg等

三、访问其他应用程序

通过构建好的Content Provider获取数据; 

(1)A程序通过Content Provider暴露自己的数据

(2)B程序通过Content Resolver对暴露的数据进行操作

实例:通过Content Provider获取联系人信息,假设有位联系人叫做“张三”,现在通过Content Provider获取其联系人信息

首先,创建一个Activity,当点击其中的按钮后,获取联系人信息

因为要读取手机中的联系人,所以需要获取对手机联系人的读取权限

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Demo01Activity">

    <Button
        android:id="@+id/btn_get"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="获取联系人"
        android:textSize="26sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>

在主配置文件中声明联系人读取权限

在程序中也需要动态地获取读取权限

首先在点击按钮时,动态地获取联系人读取权限

package com.example.review05

import android.Manifest
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.ContactsContract
import android.widget.Button
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

class Demo01Activity : AppCompatActivity() {

    @RequiresApi(Build.VERSION_CODES.M)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_demo01)

        val btn = findViewById<Button>(R.id.btn_get)
        btn.setOnClickListener {
            // 检查是否已获得权限
            if (ContextCompat.checkSelfPermission(
                    this, Manifest.permission.READ_CONTACTS
                ) == PackageManager.PERMISSION_GRANTED) {
                // 权限已经被授予
                getContacts()
            } else {
                // 权限没有被授予,请求权限
                requestPermissions(arrayOf(Manifest.permission.READ_CONTACTS), 1)
            }
        }
    }

    // 处理权限请求的回调
    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<String>, grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)

        if (requestCode == 1) {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限被授予
                getContacts()
            } else {
                // 权限被拒绝
                // 可以提示用户权限被拒绝
            }
        }
    }

    // 获取联系人数据的逻辑
    private fun getContacts() {
        // 这里可以编写获取联系人信息的逻辑
    }

    //读取联系人信息
    fun readContacts() {
        val uri = Uri.parse("content://com.android.contacts/contacts")
        val resolver = contentResolver
        val cursor = resolver.query(uri,null,null,null,null)

        if(cursor != null) {
            while (cursor.moveToNext()) {
                //处理查询结果
                val idValue = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID))
                val name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME))
                println("id=${idValue}\tname=${name}")
            }
            cursor.close()
        }
    }
}

运行:

点击“获取联系人”之后:

四、内容观察者

目前,我们已经可以通过Content Resolver获取Content Provider中的数据。但是该过程是主动地读取数据。有没有一种方法,可以被动地读取数据,即数据变化时主动通知呢?        

比如,我们开发了一款联系人管理工具,在联系人被修改时能够及时同步联系人信息。这时候就需要内容观察者 Content Observer。

举例:A程序通过Content Provider插入一条数据,并通知B程序

1)创建内容提供者 使用Android Studio创建一个内容提供者,并实现下述回调

创建内容提供者SQLite Helper类定义如下

2)在Activity中,向Content Provider插入数据

3)创建Content Observer

内容提供者提供了不同应用(进程)间分享数据的机制。并且定义了一套规范的数据操作接口。例如,用户只需知道有一个insert方法可以插入数据,并且向该方法传递待插入的数据即可,其并不关心数据是如何插入的,也不关心数据的具体组织形式。因此,使用内容提供者封装了数据操作的具体细节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值