【Android】学习ContentProvider和ContentResolver,写个CRUD案例

2023-11-02

前言

  • 学到了contentProvider,记录一下学到的知识,写一个CRUD的案例
  • 案例内容(Provider负责CRUD的持久层操作,Resolver对Provider返回的Uri或者Cursor数据进行解析

1.Provider的代码逻辑

(1)创建数据库连接DBHelper类

  • 使用到了SQLite,因此需要继承SQLiteOpenHelper
  • 构造函数中,删除多余的传参,自己手动传一个表名,factory=null,version=1
  • 然后再DBHelper启动的时候,onCreate中执行两段sql语句,建立person表,并先插入一条记录。
public class DBHelper extends SQLiteOpenHelper {
    private static final String TAG = "DBHelper";

    public DBHelper(@Nullable Context context) {
        super(context, "wang.db", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        Log.e(TAG, "onCreate()");
        //建表
        sqLiteDatabase.execSQL("create table person(_id integer primary key autoincrement, name varchar)");
        //插入初始化数据
        sqLiteDatabase.execSQL("insert into person (name) values ('Tom')");

    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}

(2)创建一个自定义的Provider类

  • 需要继承ContentProvider类,并重写其方法,方法中包括增删改查等方法。
  • Provider并不需要和任何视图进行绑定。
  • 记得在清单中对这个Provider进行注册!注册的时候需要写上intent-filter,因为需要别的app调用
  • 下面代码我尽量注释地详细点。
/**
 * 操作Person的provider
 */
public class PersonProvider extends ContentProvider {
    //存放合法URI的容器,构造函数㤇传一个默认值,也就是不匹配的值-1
    private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    //引入DBHelper
    private DBHelper dbHelper;

	//matcher是一个Uri的容器,我们需要先对其添加一些URI规则,
	//在后面的时候才可以对传来的URI根据matcher中的URI规则进行判断,如下,不带参数的为1,带参数的为2,
	//写在静态代码块中,以便一开始matcher就有这些规则
    static {
        matcher.addURI("com.wang.ch07_provider.personprovider", "/person", 1);
        matcher.addURI("com.wang.ch07_provider.personprovider", "/person/#", 2);//#匹配任意的数字
    }

	//log的打印TAG
    private static final String TAG = "PersonProvider";

    public PersonProvider() {
        Log.e(TAG, "PersonProvider()");
    }

    @Override
    public boolean onCreate() {
        Log.e(TAG, "onCreate()");
        //PersonProvider创建的时候回调onCreate方法就会赋值一个DBHelper给这里的成员变量。
        //方便之后获取数据库连接
        dbHelper = new DBHelper(getContext());
        return false;
    }

    /**
     * 查询包括根据id和不根据id查询
     * 还需要判断URI是否合法,也就是在不在Matcher中需要判断
     */
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs,
                        @Nullable String sortOrder) {
        Log.e(TAG, "query()");
        //1.匹配URI,返回code,如果合法那么进行查询,不合法抛出异常
        int code = matcher.match(uri);
        //得到数据库连接对象
        SQLiteDatabase database = dbHelper.getReadableDatabase();
        if (code == 1) {
            //不根据ID查询
            Cursor cursor = database.query("person", projection, selection, selectionArgs,
                    null, null, null);
            return cursor;
        } else if (code == 2) {
            //根据ID进行查询
            long id = ContentUris.parseId(uri);
            Cursor cursor = database.query("person", projection, "_id=?",
                    new String[]{id + ""}, null, null, null);
            return cursor;
        } else {
            throw new RuntimeException("查询URI不合法");
        }
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
        Log.d(TAG, "insert()");
        //首先匹配Uri,通过code判断是哪种操作
        int code = matcher.match(uri);
        SQLiteDatabase database = dbHelper.getReadableDatabase();
        //插入不可以带id参数。。因此不存在code==2,只有code==1合法
        if (code == 1) {
            long id = database.insert("person", null, contentValues);
            //这里因为返回的是Uri,因此可以将数据库连接关闭
            database.close();
            //需要将这个id添加到Uri中,使用工具类
            return ContentUris.withAppendedId(uri, id);
        } else {
            database.close();
            throw new RuntimeException("插入URI不合法");
        }
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        Log.d(TAG, "delete()");
        int code = matcher.match(uri);
        SQLiteDatabase database = dbHelper.getReadableDatabase();
        int deleteCount = -1;
        if (code == 1) {
            deleteCount = database.delete("person", selection, selectionArgs);
        } else if (code == 2) {
            deleteCount = database.delete("person", "_id=?", new String[]{ContentUris.parseId(uri) + ""});
        } else {
            database.close();
            throw new RuntimeException("delete Uri 非法");
        }
        database.close();
        return deleteCount;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String selection, @Nullable String[] selectionArgs) {
        Log.d(TAG, "update()");
        int code = matcher.match(uri);
        int updateCount = -1;
        SQLiteDatabase database = dbHelper.getReadableDatabase();
        if (code == 1) {
            //如果不根据id进行更新就是将所有的记录进行更新
            updateCount = database.update("person", contentValues, selection, selectionArgs);
        } else if (code == 2) {
            updateCount = database.update("person", contentValues, "_id=?", new String[]{ContentUris.parseId(uri) + ""});
        } else {
            database.close();
            throw new RuntimeException("update Uri 非法");
        }
        database.close();
        return updateCount;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }
}

(3)在清单中注册该PersonProvider

  • name就取全限定名称就好,具有唯一性
  • authorities就取全限定名称的全小写
  • exported为true,意味着别的app可以调用该provider
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.wang.ch07_provider">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Provider">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <provider
            android:name="com.wang.ch07_provider.PersonProvider"
            android:authorities="com.wang.ch07_provider.personprovider"
            android:exported="true" />
    </application>

</manifest>

2.Resolver的代码逻辑

(1)界面布局xml

  • 来四个键就好,每个键都有一个onClick绑定的事件方法在MainActivity中进行监听。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="insert"
        android:text="Insert" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="update"
        android:text="Update" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="delete"
        android:text="Delete" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="query"
        android:text="Query" />

</LinearLayout>
  • 长这样
    在这里插入图片描述

(2)MainActivity代码逻辑

  • 首先在onCreate中加载布局
  • 其次每个监听方法中的逻辑基本上一致:(1)获取resolver;(2)使用resolver,通过Uri和其他参数,来调用对应Provider中的对应方法,并处理返回的Uri或者数据库Cursor。
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void insert(View view) {
        //1.得到contentResolver对象
        ContentResolver resolver = getContentResolver();
        Uri uri = Uri.parse("content://com.wang.ch07_provider.personprovider/person/");
        ContentValues contentValues = new ContentValues();
        contentValues.put("name", "wang");
        Uri insertUri = resolver.insert(uri, contentValues);
        Toast.makeText(this, "添加学生1,姓名为:" + contentValues.get("name") +
                "\n返回的uri为:" + insertUri.toString(), Toast.LENGTH_SHORT).show();

    }

    public void update(View view) {
        //1.获取contentResolver
        ContentResolver resolver = getContentResolver();
        Uri uri = Uri.parse("content://com.wang.ch07_provider.personprovider/person/1");
        ContentValues contentValues = new ContentValues();
        contentValues.put("name", "update-Tom");
        int updateRow = resolver.update(uri, contentValues, null, null);
        Toast.makeText(this, "update row = " + updateRow, Toast.LENGTH_SHORT).show();
    }

    public void delete(View view) {
        //1.获取
        ContentResolver resolver = getContentResolver();
        //删除id为2的学生
        Uri uri = Uri.parse("content://com.wang.ch07_provider.personprovider/person/2");
        int deleteRow = resolver.delete(uri, null, null);
        Toast.makeText(this, "update row = " + deleteRow, Toast.LENGTH_SHORT).show();
    }

    public void query(View view) {
        //1.得到contentResolver对象
        ContentResolver resolver = getContentResolver();
        //2.执行contentResolver对象,调用query方法得到cursor
        Uri uri = Uri.parse("content://com.wang.ch07_provider.personprovider/person/1");//根据id查询
        //下面query参数含义,uri,查询那些字段,条件字段,条件字段的参数..
        Cursor cursor = resolver.query(uri, null, null, null, null);
        //3.取出cursor中的数据,并显示
        if (cursor.moveToNext()) {
            int id = cursor.getInt(0);
            String name = cursor.getString(1);
            Toast.makeText(this, "学生id为:" + id + "\n学生姓名为:" + name, Toast.LENGTH_SHORT).show();
        }
    }
}

完成

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Android】学习ContentProvider和ContentResolver,写个CRUD案例 的相关文章

随机推荐

  • 解决AttributeError: module ‘torchtext.data‘ has no attribute ‘Field‘问题(Datasets,Example等)

    1 torchtext安装版本对应 torch版本 1 x y torchtext版本 1 x 1 y 2 解决AttributeError module torchtext data has no attribute Field 问题 t
  • 关于GNS3中GRE隧道的建立

    步骤1 路由ip配置 省略 步骤2 路由配置 路由r1 R1 config ip route 0 0 0 0 0 0 0 0 10 1 1 2 路由r3 R3 config ip route 0 0 0 0 0 0 0 0 20 1 1 1
  • 为智能汽车打造安全堡垒—QNX系统中的软件安全保护

    在当今的智能信息时代 智能汽车已成为现代交通的代表 然而 随着技术的进步 安全问题也日益受到重视 QNX操作系统与CodeMeter技术的结合 为智能汽车提供了坚固的软件安全防线 确保了驾驶的安全与可靠 QNX系统 打造智能汽车的软件基石
  • esp-idf4.4 esp32s3 usb_cdc和u盘使用

    本文章前提是已经搭建好esp idf4 4的环境 1 在esp idf或者电脑任意目录克隆esp iot solution git clone b usb add usb solutions recursive https github c
  • opencv图像平滑

    图像噪声 由于图像采集 处理 传输等过程不可避免的会受到噪声的污染 妨碍 们对图像 理解及分析处理 常 的图像噪声有 斯噪声 椒盐噪声等 椒盐噪声 椒盐噪声也称为脉冲噪声 是图像中经常 到的 种噪声 它是 种随机出现的 点或者 点 可能是亮
  • 使用vue.js调用摄像头示例

  • linux 下 packet_mmap 前篇 (抓包实现)

    一 概述 本人 linux下 tcpdump 详解前中后 分析过抓包的流程 具体篇章对应如下 前篇了解了libpcap库如何实现抓包 中篇深入内核底层讲述了抓包的原理 后篇自己实现过抓包过程 tcpdump 篇章中讲述的只是原始的抓包流程
  • node:internal/modules/cjs/loader:1024 throw err; ^Error:

    笔者出现的报错情况截图如下 Error Cannot fi1 nd module rtsp2web 安装的库不同 但是不影响解决方法 1 重新安装相对应的文件 npm i rtsp2web 笔者这边 但是还是运行不起来 2 之后可以查看自己
  • rem和mod的区别

    Matlab 如果你不仔细区分的话 可以把rem和mod都当作是求余数的命令 比如 gt gt mod 3 2 ans 1 gt gt rem 3 2 ans 1 可是 通过看他们的帮助文件可以知道 这两个数的符号一致时的结果是一样的 但是
  • nginx 代理访问 springboot项目

    使用的docker部署 不能直接通过 localhost port 访问 容器间之间默认不能互通 修改网络模式 或者 用一下方法 docker inspect format NetworkSettings IPAddress springb
  • Websocket协议原理及Ws服务器代码实现

    一 什么是 WebSocket WebSocket 是 HTML5 开始提供的一种 建立在单个 TCP 连接上的全双工的网络通信协议 WebSocket 协议在2008年诞生 2011年成为国际标准 现在最新版本浏览器都已经支持了 它的最大
  • 趣谈网络协议学习笔记[计算机网络]

    在极客时间上学习刘超老师的趣谈网络协议这门课 听看的时候嗯 觉得自己已经很懂了 理解得也还行 但是当自己整理时会发现有好多小知识点并没有完全理解 这篇博客呢就是在听课后再加上谷歌搜索整理得来 权当学习笔记了 顺便巩固记忆 以后忘了还可以再翻
  • 逆时针打印矩阵

    逆时针打印矩阵 输入矩阵 1 2 38 9 47 6 5 输出 1 2 3 4 5 6 7 8 9 思路 在矩阵外面砌一堵墙 如下 1 1 1 1 1 1 1 2 3 1 1 8 9 4 1 1 7 6 5 1 1 1 1 1 1 遇到 1
  • Idea同步失败Unresolved dependency的解决办法

    下载一些开源的库 经常会碰到一些同步问题 本文就该系列问题单独开一篇文章 和大家缕一缕这样的问题怎么解决 文章在实践过程中会保持同步更新 大家可以点击收藏以便于下次遇到类似问题可以快速找到解决办法 IDEA的同步问题多半为网络环境问题 少部
  • 面试题汇总---深度学习(图像识别,NLP内容)

    文章目录 1 基本概念 1 1 为什么神经网络中深度网络的表现比广度网络表现好 1 2 推导BP算法 1 3 什么是梯度消失和梯度爆炸 1 4 常用的激活函数有哪些 1 5 常用的参数更新方法有哪些 1 6 解决过拟合的方法 数据层面 模型
  • linux系统下启停nginx的命令

    linux 下面启动nginx 和关闭nginx 查看linux 开放的所有端口netstat ntpl 重启服务命令 service network restart 1 进入到安装的目录里面 whereis nginx 2 进入该路径 c
  • python实现名片管理操作系统

    根据黑马程序员演练的python实现名片管理操作系统 一共分为两个文件第一个card main py表示主题搭建的框架 第二个是card tools py表示往框架中填写的主要内容 代码实现在liunx ubuntu 中 实现的是终端操作
  • VIM中的保存和退出命令

    1 打开命令 vim a log 注意后缀名要加上 2 退出命令 按ESC键 跳到命令模式 然后输入 q 不保存退出 或者 wq 保存退出 3 注意 以 和 开头的命令都有历史纪录 可以首先键入 或 然后按上下箭头来选择某个历史命令 w 保
  • cmd高级教程

    CMD高级使用教程 目录 第一章 批处理基础 第一节 常用批处理内部命令简介 1 REM 和 2 ECHO 和 3 PAUSE 4 ERRORLEVEL 5 TITLE 6 COLOR 7 mode 配置系统设备 8 GOTO 和 9 FI
  • 【Android】学习ContentProvider和ContentResolver,写个CRUD案例

    文章目录 前言 1 Provider的代码逻辑 1 创建数据库连接DBHelper类 2 创建一个自定义的Provider类 3 在清单中注册该PersonProvider 2 Resolver的代码逻辑 1 界面布局xml 2 MainA