6.5 共享数据

        本节介绍Android的四大组件之一ContentProvider的基本概念和常见用法:首先说明如何使用内容提供器封装内部数据的外部访问接口,然后阐述如何使用内容解析器通过外部接口操作内部数据,最后叙述如何利用内容解析器读写联系人信息,以及如何利用内容观察器监听收到的短信内容。

6.5.1  通过ContentProvider封装数据

        Android提供了四大组件,分别是活动Activity、广播Broadcast、服务Service和内容提供器ContentProvider。其中内容提供器涵盖与内部数据存取有关的一系列组件,完整的内容组件由内容提供器ContentProvider、内容解析器ContentResolver、内容观察器ContentObserver三部分组成。

        ContentProvider给App存取内部数据提供了统一的外部接口,让不同的应用之间得以互相共享数据。像上一章提到的SQLite可操作应用自身的内部数据库,上传和下载功能可操作后端服务器的文件,而ContentProvider可操作当前设备其他应用的内部数据,它是一种中间层次的数据存储形式。

        在实际编码中,ContentProvider只是服务端App存储数据的抽象类,开发者需要在其基础上实现一个完整的内容提供器,并重写下列数据库管理方法。

        ●  onCreate:创建数据库并获得数据库连接。

        ●  insert:插入数据。

        ●  delete:删除数据。

        ●  update:更新数据。

        ●  query:查询数据,并返回结果集的游标。

        ●  getType:获取内容提供器支持的数据类型。

        这些方法看起来是不是很像SQLite?没错,ContentProvider作为中间接口,本身并不直接保存数据,而是通过SQLiteOpenHelper与SQLiteDatabase间接操作底层的数据库。所以要想使用ContentProvider,首先得实现SQLite的数据库帮助器,然后由ContentProvider封装对外的接口。以封装用户信息为例,具体步骤主要分成以下3步。

        1.  编写用户信息表的数据库帮助器

        这个数据库帮助器就是常规的SQLite操作代码,实现过程参见本章的“6.2.3  数据库帮助器SQLiteOpenHelper”,完整代码如下:

package com.example.roomdatabase.entity;
//用户信息
public class UserInfo {public long rowid; // 行号public int xuhao; // 序号public String name; // 姓名public int age; // 年龄public long height; // 身高public float weight; // 体重public boolean married; // 婚否public String update_time; // 更新时间public String phone; // 手机号public String password; // 密码public UserInfo() {rowid = 0L;xuhao = 0;name = "";age = 0;height = 0L;weight = 0.0f;married = false;update_time = "";phone = "";password = "";}
}
package com.example.roomdatabase.database;import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;import com.example.roomdatabase.entity.UserInfo;import java.util.ArrayList;
import java.util.List;public class UserDBHelper extends SQLiteOpenHelper {private static final String TAG = "UserDBHelper";private static final String DB_NAME = "user.db"; // 数据库的名称private static final int DB_VERSION = 1; // 数据库的版本号private static UserDBHelper mHelper = null; // 数据库帮助器的实例private SQLiteDatabase mDB = null; // 数据库的实例public static final String TABLE_NAME = "user_info"; // 表的名称private UserDBHelper(Context context) {super(context, DB_NAME, null, DB_VERSION);}private UserDBHelper(Context context, int version) {super(context, DB_NAME, null, version);}// 利用单例模式获取数据库帮助器的唯一实例public static UserDBHelper getInstance(Context context, int version) {if (version > 0 && mHelper == null) {mHelper = new UserDBHelper(context, version);} else if (mHelper == null) {mHelper = new UserDBHelper(context);}return mHelper;}// 打开数据库的读连接public SQLiteDatabase openReadLink() {if (mDB == null || !mDB.isOpen()) {mDB = mHelper.getReadableDatabase();}return mDB;}// 打开数据库的写连接public SQLiteDatabase openWriteLink() {if (mDB == null || !mDB.isOpen()) {mDB = mHelper.getWritableDatabase();}return mDB;}// 关闭数据库连接public void closeLink() {if (mDB != null && mDB.isOpen()) {mDB.close();mDB = null;}}// 创建数据库,执行建表语句@Overridepublic void onCreate(SQLiteDatabase db) {Log.d(TAG, "onCreate");String drop_sql = "DROP TABLE IF EXISTS " + TABLE_NAME + ";";Log.d(TAG, "drop_sql:" + drop_sql);db.execSQL(drop_sql);String create_sql = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("+ "_id INTEGER PRIMARY KEY  AUTOINCREMENT NOT NULL,"+ "name VARCHAR NOT NULL," + "age INTEGER NOT NULL,"+ "height INTEGER NOT NULL," + "weight FLOAT NOT NULL,"+ "married INTEGER NOT NULL," + "update_time VARCHAR NOT NULL"//演示数据库升级时要先把下面这行注释+ ",phone VARCHAR" + ",password VARCHAR"+ ");";Log.d(TAG, "create_sql:" + create_sql);db.execSQL(create_sql); // 执行完整的SQL语句}// 升级数据库,执行表结构变更语句@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {Log.d(TAG, "onUpgrade oldVersion=" + oldVersion + ", newVersion=" + newVersion);if (newVersion > 1) {//Android的ALTER命令不支持一次添加多列,只能分多次添加String alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "phone VARCHAR;";Log.d(TAG, "alter_sql:" + alter_sql);db.execSQL(alter_sql);alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "password VARCHAR;";Log.d(TAG, "alter_sql:" + alter_sql);db.execSQL(alter_sql); // 执行完整的SQL语句}}// 根据指定条件删除表记录public int delete(String condition) {// 执行删除记录动作,该语句返回删除记录的数目return mDB.delete(TABLE_NAME, condition, null);}// 删除该表的所有记录public int deleteAll() {// 执行删除记录动作,该语句返回删除记录的数目return mDB.delete(TABLE_NAME, "1=1", null);}// 往该表添加一条记录public long insert(UserInfo info) {List<UserInfo> infoList = new ArrayList<UserInfo>();infoList.add(info);return insert(infoList);}// 往该表添加多条记录public long insert(List<UserInfo> infoList) {long result = -1;for (int i = 0; i < infoList.size(); i++) {UserInfo info = infoList.get(i);List<UserInfo> tempList = new ArrayList<UserInfo>();// 如果存在同名记录,则更新记录// 注意条件语句的等号后面要用单引号括起来if (info.name != null && info.name.length() > 0) {String condition = String.format("name='%s'", info.name);tempList = query(condition);if (tempList.size() > 0) {update(info, condition);result = tempList.get(0).rowid;continue;}}// 如果存在同样的手机号码,则更新记录if (info.phone != null && info.phone.length() > 0) {String condition = String.format("phone='%s'", info.phone);tempList = query(condition);if (tempList.size() > 0) {update(info, condition);result = tempList.get(0).rowid;continue;}}// 不存在唯一性重复的记录,则插入新记录ContentValues cv = new ContentValues();cv.put("name", info.name);cv.put("age", info.age);cv.put("height", info.height);cv.put("weight", info.weight);cv.put("married", info.married);cv.put("update_time", info.update_time);cv.put("phone", info.phone);cv.put("password", info.password);// 执行插入记录动作,该语句返回插入记录的行号result = mDB.insert(TABLE_NAME, "", cv);if (result == -1) { // 添加成功则返回行号,添加失败则返回-1return result;}}return result;}// 根据条件更新指定的表记录public int update(UserInfo info, String condition) {ContentValues cv = new ContentValues();cv.put("name", info.name);cv.put("age", info.age);cv.put("height", info.height);cv.put("weight", info.weight);cv.put("married", info.married);cv.put("update_time", info.update_time);cv.put("phone", info.phone);cv.put("password", info.password);// 执行更新记录动作,该语句返回更新的记录数量return mDB.update(TABLE_NAME, cv, condition, null);}public int update(UserInfo info) {// 执行更新记录动作,该语句返回更新的记录数量return update(info, "rowid=" + info.rowid);}// 根据指定条件查询记录,并返回结果数据列表public List<UserInfo> query(String condition) {String sql = String.format("select rowid,_id,name,age,height," +"weight,married,update_time,phone,password " +"from %s where %s;", TABLE_NAME, condition);Log.d(TAG, "query sql: " + sql);List<UserInfo> infoList = new ArrayList<UserInfo>();// 执行记录查询动作,该语句返回结果集的游标Cursor cursor = mDB.rawQuery(sql, null);// 循环取出游标指向的每条记录while (cursor.moveToNext()) {UserInfo info = new UserInfo();info.rowid = cursor.getLong(0); // 取出长整型数info.xuhao = cursor.getInt(1); // 取出整型数info.name = cursor.getString(2); // 取出字符串info.age = cursor.getInt(3); // 取出整型数info.height = cursor.getLong(4); // 取出长整型数info.weight = cursor.getFloat(5); // 取出浮点数//SQLite没有布尔型,用0表示false,用1表示trueinfo.married = (cursor.getInt(6) == 0) ? false : true;info.update_time = cursor.getString(7); // 取出字符串info.phone = cursor.getString(8); // 取出字符串info.password = cursor.getString(9); // 取出字符串infoList.add(info);}cursor.close(); // 查询完毕,关闭数据库游标return infoList;}// 根据手机号码查询指定记录public UserInfo queryByPhone(String phone) {UserInfo info = null;List<UserInfo> infoList = query(String.format("phone='%s'", phone));if (infoList.size() > 0) { // 存在该号码的登录信息info = infoList.get(0);}return info;}
}
        2.  编写内容提供器的基础字段类       

        该类需要实现接口BaseColumns,同时加入几个常量定义。详细代码示例如下:

package com.example.roomdatabase.entity;import android.net.Uri;
import android.provider.BaseColumns;import com.example.roomdatabase.database.UserDBHelper;public class UserInfoContent implements BaseColumns {// 这里的名称必须与AndroidManifest.xml里的android:authorities保持一致public static final String AUTHORITIES = "com.example";//  内容提供器的外部表名public static final String TABLE_NAME = UserDBHelper.TABLE_NAME;// 访问内容提供器的URIpublic static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITIES + "/user");// 下面是该表的各个字段名称public static final String USER_NAME = "name";public static final String USER_AGE = "age";public static final String USER_HEIGHT = "height";public static final String USER_WEIGHT = "weight";public static final String USER_MARRIED = "married";// 默认的排序方法public static final String DEFAULT_SORT_ORDER = "_id desc";
}
        3.  通过右键菜单创建内容提供器

        右击App模块的包名目录,在弹出的右键菜单中依次选择New→Other→Content Provider,打开如图所示的组件创建对话框。

        在创建对话框的Class Name一栏填写内容提供器的名称,比如UserInfoProvider;在URI Authorities一栏填写URI的授权串,比如“com.example”,注意这个授权串要跟 UserInfoContent里的一样;然后单击对话框右下角的Finish按钮,完成提供器的创建操作。

       上述创建过程会自动修改App模块的两处地方,一处是往AndroidManifest.xml添加内容提供器的注册配置,配置信息示例如下:

        <providerandroid:name=".entity.UserInfoProvider"android:authorities="com.example"android:enabled="true"android:exported="true" ></provider>

        另一处是在包名目录下生成名为UserInfoProvider.java的代码文件,打开一看发现该类继承了ContentProvider,并且提示重写onCreate、insert、delete、query、update、getType等方法,以便对数据进行增删改查等操作。这个提供器代码显然只有一个框架,还需补充详细的实现代码,为此重写onCreate方法,在此获取用户信息表的数据库帮助器实例,其他insert、delete、query等方法也要加入对应的数据库操作代码,修改之后的内容提供器代码如下:

package com.example.roomdatabase.entity;import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;import com.example.roomdatabase.database.UserDBHelper;public class UserInfoProvider extends ContentProvider {private final static String TAG = "UserInfoProvider";private UserDBHelper userDB; // 声明一个用户数据库的帮助器对象public static final int USER_INFO = 1; // Uri匹配时的代号public static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);static { // 往Uri匹配器中添加指定的数据路径uriMatcher.addURI(UserInfoContent.AUTHORITIES, "/user", USER_INFO);}public UserInfoProvider() {}// 根据指定条件删除数据@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {// Implement this to handle requests to delete one or more rows.int count = 0;if (uriMatcher.match(uri) == USER_INFO) { // 匹配到了用户信息表// 获取SQLite数据库的写连接SQLiteDatabase db = userDB.getWritableDatabase();// 执行SQLite的删除操作,并返回删除记录的数目count = db.delete(UserInfoContent.TABLE_NAME, selection, selectionArgs);db.close(); // 关闭SQLite数据库连接}return count;}// 获取Uri支持的数据类型,暂未实现@Overridepublic String getType(Uri uri) {// TODO: Implement this to handle requests for the MIME type of the data// at the given URI.throw new UnsupportedOperationException("Not yet implemented");}// 插入数据@Overridepublic Uri insert(Uri uri, ContentValues values) {// TODO: Implement this to handle requests to insert a new row.if (uriMatcher.match(uri) == USER_INFO) { // 匹配到了用户信息表// 获取SQLite数据库的写连接SQLiteDatabase db = userDB.getWritableDatabase();// 向指定的表插入数据,返回记录的行号long rowId = db.insert(UserInfoContent.TABLE_NAME, null, values);if (rowId > 0) { // 判断插入是否执行成功// 如果添加成功,就利用新记录的行号生成新的地址Uri newUri = ContentUris.withAppendedId(UserInfoContent.CONTENT_URI, rowId);// 通知监听器,数据已经改变getContext().getContentResolver().notifyChange(newUri, null);}db.close(); // 关闭SQLite数据库连接}return uri;}// 创建ContentProvider时调用,可在此获取具体的数据库帮助器实例@Overridepublic boolean onCreate() {// TODO: Implement this to initialize your content provider on startup.userDB = UserDBHelper.getInstance(getContext(), 1);return true;}// 根据指定条件查询数据库@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {Cursor cursor = null;if (uriMatcher.match(uri) == USER_INFO) { // 匹配到了用户信息表// 获取SQLite数据库的读连接SQLiteDatabase db = userDB.getReadableDatabase();// 执行SQLite的查询操作cursor = db.query(UserInfoContent.TABLE_NAME,projection, selection, selectionArgs, null, null, sortOrder);// 设置内容解析器的监听cursor.setNotificationUri(getContext().getContentResolver(), uri);}return cursor; // 返回查询结果集的游标}// 更新数据,暂未实现@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {// TODO: Implement this to handle requests to update one or more rows.throw new UnsupportedOperationException("Not yet implemented");}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/708416.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

10_Vue

文章目录 Vue快速入门Vue的指令Vue的插值表达式V指令v-bind&#xff08;单向绑定&#xff09;v-model&#xff08;双向绑定&#xff09;v-on&#xff08;事件监听&#xff09;v-for&#xff08;循环&#xff09;v-text、v-htmlv-show&#xff08;显示/隐藏&#xff09;v-if&…

了解GPT:ChatGPT的终极指南

在人工智能&#xff08;AI&#xff09;的世界里&#xff0c;有一颗冉冉升起的新星正在革命性地改变我们与机器的交互方式&#xff1a;ChatGPT。在本文中&#xff0c;我们将深入研究什么是ChatGPT&#xff0c;为什么底层技术GPT如此强大&#xff0c;以及它是如何实现其卓越功能的…

excel导出标准化

虽然标题叫标准化&#xff0c;只不过是我自己的习惯&#xff0c;当一件事情变得流程标准化之后&#xff0c;开发程序就会飞快&#xff0c;开发评估工作总是 搞个1~2天&#xff0c;实则前端后端一起开发&#xff0c;1个小时就可以搞定。 1 前端 const exportXls async () >…

C++重点---STL简介

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、STL简介 STL&#xff08;Standard Template Library&#xff09;是C标准库中的一个重要组成部分&#xff0c;它提供了…

iOS中卡顿产生的主要原因及优化思路

卡顿本质上是一个UI体验上的问题&#xff0c;而UI的渲染及显示&#xff0c;主要涉及CPU和GPU两个层面。若 CPUGPU渲染耗时超过16.7ms&#xff0c;就会在屏幕vsync信号到来时无法更新屏幕内容&#xff0c;进而导致卡顿。 iOS中UI渲染主要包含Layout->Draw->Prepare->Co…

MySQL5.7.44版本压缩包在Win11系统快速安装

一.背景 主要还是为了公司的带徒弟任务。我自己也喜欢MySQL的绿色版本。 1.软件版本说明 MySQL版本&#xff1a;5.7.44 压缩包版本&#xff0c;相当于绿色版。当然&#xff0c;你也可以使用window系统的Installer版本去安装。 操作系统&#xff1a;Win11家庭版 二.MySQL软…

electron-release-server部署electron自动更新服务器记录

目录 一、前言 环境 二、步骤 1、下载上传electron-release-server到服务器 2、宝塔新建node项目网站 3、安装依赖 ①npm install ②安装并配置postgres数据库 ③修改项目配置文件 ④启动项目 ⑤修改postgres的认证方式 ⑥Cannot find where you keep your Bower p…

spring6学习笔记

1.环境准备 1.idea建立一个空项目&#xff0c;jdk要求是17 2.Maven配置&#xff08;和mybatis里一样&#xff09; 3.新建一个模块 2.ocp原则 3.依赖倒置原则&#xff08;DIP&#xff09; 什么是依赖倒置原则? 1.面向接口编程&#xff0c;面向抽象编程&#xff0c;不要面向…

【React 报错】—Remove untracked files, stash or commit any changes, and try again.

【React 报错】—Remove untracked files, stash or commit any changes, and try again. 在react项目中通过.less文件进行样式定义&#xff0c;先暴露webpack配置文件&#xff0c;执行命令&#xff1a;yarn eject 或 npm run eject&#xff0c;报错如下&#xff1a; 原因是因…

【清理mysql数据库服务器二进制日志文件】

清理前后比对 清理前占用 86% &#xff1a; 清理后占用 29% &#xff1a; 排查占用磁盘较大的文件 检测磁盘空间占用 TOP 10 # 检测磁盘空间占用 TOP 10 $ sudo du -S /var/log/ | > sort -rn | # -n选项允许按数字排序。-r选项会先列出最大数字&#xff08;逆序&#x…

vue中 input disable后无法触发点击事件

问题&#xff1a;input标签为disabled后&#xff0c;点击事项无效&#xff1b;当点击文字**“请选择”**时无法触发点击事件&#xff0c;其父标签的其余位置均可触发 解决&#xff1a;只需要在input标签中添加 style“pointer-events:none” 即可 pointer-events: none 作用是…

如何刷新 DNS 缓存 (macOS, Linux, Windows)

如何刷新 DNS 缓存 (macOS, Linux, Windows) Unix Linux Windows 如何刷新 DNS 缓存 (macOS, FreeBSD, RHEL, CentOS, Debian, Ubuntu, Windows) 请访问原文链接&#xff1a;https://sysin.org/blog/how-to-flush-dns-cache/&#xff0c;查看最新版。原创作品&#xff0c;转载…

字节面试问题

实现三列布局的方法 第一种&#xff1a;可以使用浮动margin 第二种&#xff1a;浮动BFC <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, in…

密码学及其应用(应用篇15)——0/1背包问题

1 问题背景 背包问题是一个经典的优化问题&#xff0c;在计算机科学和运筹学中有着广泛的应用。具体到你提到的这个问题&#xff0c;它是背包问题中的一个特例&#xff0c;通常被称为0/1背包问题。这里&#xff0c;我们有一系列的正整数 &#xff0c;以及一个正整数&#xff0c…

k8s.gcr.io/pause:3.2镜像丢失解决

文章目录 前言错误信息临时解决推荐解决onetwo 前言 使用Kubernetes&#xff08;k8s&#xff09;时遇到了镜像拉取的问题&#xff0c;导致Pod沙盒创建失败。错误显示在尝试从k8s.gcr.io拉取pause:3.2镜像时遇到了超时问题&#xff0c;这通常是因为网络问题或者镜像仓库服务器的…

科技赋能,MTW400A为农村饮水安全打通“最后一公里”

日前&#xff0c;山东省政府纵深推进国家省级水网先导区建设&#xff0c;持续深化“水网”行动&#xff0c;着力构筑水安全保障网、水民生服务网、水生态保护网&#xff0c;建设水美乡村示范带、内河航运示范带、文旅融合示范带、绿色发展示范带&#xff0c;推动形成“三网四带…

kubesphere jenkins 流水线 未运行(解决方案)

场景&#xff1a; 在kubesphere 中运行 流水线 devops 结果&#xff0c;显示未运行 但是用 admin 账户是可以运行成功的。 问题解决 1- 查日志&#xff1a; 然后 Caused: org.acegisecurity.userdetails.UsernameNotFoundException: org.springframework.security.core.…

网站数据加密之Hook通用方案

文章目录 1. 写在前面2. 请求分析3. 编写Hook4. 其他案例 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff1a;对JS逆向感兴趣的朋…

SpringBoot+Vue全栈开发-刘老师教编程(b站)(二)

创建SpringBoot项目 1.配置maven 出现bug java: 无法访问org.springframework.boot.SpringApplication 错误的类文件: /D:/maven/repository/org/springframework/boot/spring-boot/3.0.0/spring-boot-3.0.0.jar!/org/springframework/boot/SpringApplication.class 类…

FPGA高端项目:FPGA基于GS2971的SDI视频接收转HDMI输出,提供3套工程源码和技术支持

目录 1、前言免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案的SDI图像缩放应用本方案的SDI纯verilog图像缩放视频拼接应用本方案的SDI HLS图像缩放视频拼接应用本方案的SDI视频编码动态字符叠加输出应用本方案的SDI视频编码多路视频融合视频叠加应用本方案的SDI视频…