Android 使用 ContentProvider 简单操作数据库
ContentProvider 可以用来原生读写 Android 自带的数据库 SQLite。
使用 Studio 创建一个 ContentProvider, 名字叫 TestContentProvider 。
AndroidManifest.xml 文件:
<manifest
...>
<application
...>
<provider
android:name=".TestContentProvider"
android:authorities="cn.wx2020.contentprovidertest.database"
android:enabled="true"
android:exported="false"/>
...
</application>
</manifest>
其中,
authorities
是 Uri 结构中的一部分,类似于 Url 中的域名部分,其唯一识别一个ContentProvider
。由于 ContentProvider 是抽象类,按照继承抽象类的要求,需要实现以下几个方法:
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public 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");
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO: Implement this to handle requests to insert a new row.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public boolean onCreate() {
// TODO: Implement this to initialize your content provider on startup.
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO: Implement this to handle query requests from clients.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public 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");
}
- 本文实际只实现了
onCreate()
、insert()
、query()
这三个方法。
- onCreate():负责初始化数据库帮助类
SQLiteOpenHelper
,通过此类可以进行数据库创建、升级等操作。private TestSQLiteOpenHelper mOpenHelper;
... @Override
public boolean onCreate() {
mOpenHelper = new TestSQLiteOpenHelper(getContext());
return false;
}其中,
TestSQLiteOpenHelper
为 继承SQLiteOpenHelper
后自定义的实现类,将在后文给出说明。- 此方法的返回值意义不大,可以忽略。
- insert():负责在最内部一层将传入的数据插入数据库中。
public static final String AUTHORITY = "cn.wx2020.contentprovidertest.database"; private static final UriMatcher MATCHER; static {
MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
MATCHER.addURI(AUTHORITY, "user", 1);
} private String uriMatcher(Uri uri) {
switch (MATCHER.match(uri)) {
case 1:
return "user";
default:
return "";
}
}
... @Override
public Uri insert(Uri uri, ContentValues values) {
String tableName = uriMatcher(uri);
if (TextUtils.isEmpty(tableName)) {
return null;
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowName = db.insert(tableName, null, values);
if (rowName >= 0) {
Uri returnUri = ContentUris.withAppendedId(uri, rowName);
getContext().getContentResolver().notifyChange(uri, null);
return returnUri;
}
return null;
}其中,
- 方法
uriMatcher()
使用静态的UriMatcher
,在静态块中初始化、添加匹配当前ContentProvider
的authorities
、数据库表名 和 与数据库表名绑定的返回码。uriMatcher()
方法中,使用MATCHER.match(uri)
拿到返回码后,根据返回码再返回与其匹配的表名。 - 使用
SQLiteOpenHelper
的对象调用getWritableDatabase()
方法拿到可写的数据库后,对数据库db
调用insert()
方法插入传入的属性值,属性值由ContentValues
代理生成。插入成功后,insert()
函数会返回一个rowId
;如果插入失败,rowId
为-1
;若其他情况则插入成功。 - 插入成功时,返回一个
Uri
对象,上层根据这个Uri
对象,判断是否插入成功。
- 方法
- query():负责查询数据库中的数据。
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
String tableName = uriMatcher(uri);
if (TextUtils.isEmpty(tableName)) {
return null;
} Cursor cursor = null;
try {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(tableName);
cursor = builder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
} catch (SQLException e) {
Log.e(TAG, "query SQLiteException");
} if (cursor != null) {
cursor.setNotificationUri(getContext().getContentResolver(), uri);
} else {
Log.e(TAG, "cursor = null");
}
return cursor;
}
其中,
- 拿到可读的数据库后,使用
SQLiteQueryBuilder
的对象builder
构造查询。使用builder.setTables()
设定要查询的数据库表名。使用builder.query()
执行查询,按照查询条件依次输入7个参数,其中第一个参数为数据库对象db
。 - 查询得到的游标
Cursor
对象如果不为null
, 说明查询成功,否则则为查询失败。返回Cursor
对象给调用方。
- 拿到可读的数据库后,使用
- 上文中还提到了一个类
SQLiteOpenHelper
,其内部有两个方法:onCreate()
在数据库创建后调用;onUpgrade()
升级数据库版本时使用。
onCreate()
:@Override
public void onCreate(SQLiteDatabase db) {
createDatabaseTable(db);
} private void createDatabaseTable(SQLiteDatabase db) {
String userSQL = "CREATE TABLE IF NOT EXISTS user (" +
" _id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +
" user_name TEXT NOT NULL," +
" age TEXT," +
" phone_number INTEGER" +
");";
db.execSQL(userSQL);
}
此方法中,要使用
SQL
语句在指定数据库创建表。使用String
构造好建表语句后,调用数据库db
的execSQL()
执行SQL
语句。
- 除了以上两个类,还可以在实际调用 ContentProvider 之前,在数据库表的维度中再抽象出一层
UserTable
,用于外界进行实际查询时调用。由于 ContentProvider 只实现了insert()
和query()
方法,相对应地,UserTable
也实现两个方法insert()
和queryAll()
。
- insert():用于实际调用 ContentProvider,插入数据。
private static Uri getContextUri() {
return TestContentProvider.URI_TEST_USER;
} public static Uri insert(Context context, User user) {
if (TextUtils.isEmpty(user.getUserName())) {
return null;
}
ContentValues contentValues = new ContentValues();
contentValues.put("user_name", user.getUserName());
contentValues.put("age", user.getAge());
contentValues.put("phone_number", user.getPhoneNumber());
Uri uri = null;
try {
uri = context.getContentResolver().insert(getContextUri(), contentValues);
} catch (SQLException e) {
Log.e(TAG, "insert SQLiteException");
}
if (uri == null) {
Log.e(TAG, "insert uri is null");
}
return uri;
}
其中,
- 静态方法
getContextUri()
用于返回与数据库当前表对应的Uri
,操作 ContentProvider 也就是通过Uri
操作的。 - 方法参数中,实体类
User
的对象中的属性值正是插入数据库时所使用的字段值,不过需要将实体类对象转换为ContentValues
对象后,使其作为ContentResolver
类对象的insert()
方法参数之一进行插入。
- 静态方法
- queryAll(): 用于查询数据库表
user
中的所有数据。public static ArrayList<User> queryAll(Context context) {
ArrayList<User> result = new ArrayList<>();
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(getContextUri(), null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
User user = new User();
user.setId(cursor.getInt(cursor.getColumnIndexOrThrow("_id")));
user.setUserName(cursor.getString(cursor.getColumnIndexOrThrow("user_name")));
user.setAge(cursor.getInt(cursor.getColumnIndexOrThrow("age")));
user.setPhoneNumber(cursor.getLong(cursor.getColumnIndexOrThrow("phone_number")));
result.add(user);
}
} catch (SQLException e) {
Log.e(TAG, "queryAll SQLiteException");
} finally {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
return result;
}
其中,
- 使用
ContentResolver
类对象的query()
方法,除了 Uri 以外不传任何参数,这样获取的 cursor 就是
在整个链上连续且整体代表全部表数据的 cursor 。 - 使用
cursor != null && cursor.moveToNext()
作为循环条件,判断链上是否有下一个数据。若没有下一个数据可以终止循环。 - 每一行的数据是从 cursor 中取得的,其中 cursor 要先通过
getColumnIndexOrThrow()
传入字段名,拿到代表这一字段的 index ,再通过对 index 调用cursor.getInt()
或cursor.getString()
拿到这一 index 代表的字段的 INT 或者 TEXT 值。 - 每拿到一个 cursor 对应的数据后,将数据加入 ArrayList 中,最后将 ArrayList 对象作为整体返回。
- 使用
部分源码
- TestContentProvider 源码:
package cn.wx2020.contentprovidertest;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import java.util.Optional;
public class TestContentProvider extends ContentProvider {
public static final String TAG = "TestContentProvider";
public static final String AUTHORITY = "cn.wx2020.contentprovidertest.database";
public static final Uri URI_TEST_USER = Uri.parse("content://" + AUTHORITY + '/' + "user");
private static final UriMatcher MATCHER;
static {
MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
MATCHER.addURI(AUTHORITY, "user", 1);
}
private String uriMatcher(Uri uri) {
switch (MATCHER.match(uri)) {
case 1:
return "user";
default:
return "";
}
}
private TestSQLiteOpenHelper mOpenHelper;
public TestContentProvider() {
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public 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");
}
@Override
public Uri insert(Uri uri, ContentValues values) {
String tableName = uriMatcher(uri);
if (TextUtils.isEmpty(tableName)) {
return null;
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowName = db.insert(tableName, null, values);
if (rowName >= 0) {
Uri returnUri = ContentUris.withAppendedId(uri, rowName);
getContext().getContentResolver().notifyChange(uri, null);
return returnUri;
}
return null;
}
@Override
public boolean onCreate() {
mOpenHelper = new TestSQLiteOpenHelper(getContext());
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
String tableName = uriMatcher(uri);
if (TextUtils.isEmpty(tableName)) {
return null;
}
Cursor cursor = null;
try {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(tableName);
cursor = builder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
} catch (SQLException e) {
Log.e(TAG, "query SQLiteException");
}
if (cursor != null) {
cursor.setNotificationUri(getContext().getContentResolver(), uri);
} else {
Log.e(TAG, "cursor = null");
}
return cursor;
}
@Override
public 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");
}
}
- TestSqLiteOpenHelper 源码:
package cn.wx2020.contentprovidertest;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class TestSQLiteOpenHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "test.db";
private static final int DB_VERSION = 1;
public TestSQLiteOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
createDatabaseTable(db);
}
private void createDatabaseTable(SQLiteDatabase db) {
String userSQL = "CREATE TABLE IF NOT EXISTS user (" +
" _id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," +
" user_name TEXT NOT NULL," +
" age TEXT," +
" phone_number INTEGER" +
");";
db.execSQL(userSQL);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
- UserTable源码:
package cn.wx2020.contentprovidertest;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
public class UserTable {
private static final String TAG = "UserTable";
private static Uri getContextUri() {
return TestContentProvider.URI_TEST_USER;
}
public static Uri insert(Context context, User user) {
if (TextUtils.isEmpty(user.getUserName())) {
return null;
}
ContentValues contentValues = new ContentValues();
contentValues.put("user_name", user.getUserName());
contentValues.put("age", user.getAge());
contentValues.put("phone_number", user.getPhoneNumber());
Uri uri = null;
try {
uri = context.getContentResolver().insert(getContextUri(), contentValues);
} catch (SQLException e) {
Log.e(TAG, "insert SQLiteException");
}
if (uri == null) {
Log.e(TAG, "insert uri is null");
}
return uri;
}
public static ArrayList<User> queryAll(Context context) {
ArrayList<User> result = new ArrayList<>();
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(getContextUri(), null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
User user = new User();
user.setId(cursor.getInt(cursor.getColumnIndexOrThrow("_id")));
user.setUserName(cursor.getString(cursor.getColumnIndexOrThrow("user_name")));
user.setAge(cursor.getInt(cursor.getColumnIndexOrThrow("age")));
user.setPhoneNumber(cursor.getLong(cursor.getColumnIndexOrThrow("phone_number")));
result.add(user);
}
} catch (SQLException e) {
Log.e(TAG, "queryAll SQLiteException");
} finally {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
return result;
}
}
- MainActivity 源码:
Button queryAllButton = findViewById(R.id.button_1);
TextView textView = findViewById(R.id.text_1);
queryAllButton.setOnClickListener(v -> {
ArrayList<User> users = UserTable.queryAll(this);
if (users.isEmpty()) {
Log.e(TAG, "users is empty");
Toast.makeText(this, "没有数据", Toast.LENGTH_SHORT).show();
return;
}
StringBuilder sb = new StringBuilder();
for (User user: users) {
sb.append(user.toString()).append("\n");
}
textView.setText(sb.toString());
});
Button insertButton = findViewById(R.id.button_2);
insertButton.setOnClickListener(v -> {
User user = new User();
int userBack = (int) (Math.random() * 99 + 1);
user.setUserName("wx2020" + userBack);
user.setPhoneNumber(110);
user.setAge((int) (Math.random() * 30 + 23));
Uri insertUri = UserTable.insert(this, user);
if (insertUri == null) {
Toast.makeText(this, "插入失败", Toast.LENGTH_SHORT).show();
return;
}
Log.d(TAG, "insertUri = " + insertUri);
Toast.makeText(this, "插入成功", Toast.LENGTH_SHORT).show();
});
Android 使用 ContentProvider 简单操作数据库的更多相关文章
- Spring_boot简单操作数据库
Spring_boot搭配Spring Data JPA简单操作数据库 spring boot 配置文件可以使用yml文件,默认spring boot 会加载resources目录的下的applica ...
- android: SQLite使用 SQL 操作数据库
虽然 Android 已经给我们提供了很多非常方便的 API 用于操作数据库,不过总会有一些 人不习惯去使用这些辅助性的方法,而是更加青睐于直接使用 SQL 来操作数据库.这种人 一般都是属于 SQL ...
- php 简单操作数据库
<?php header("content-type:text/html;charset=utf-8"); /*//造一个连接 $connect = @mysql_conne ...
- pymysql 简单操作数据库
#!/usr/bin/env python #-*- coding:utf-8 -*- # author:leo # datetime:2019/4/24 15:22 # software: PyCh ...
- Android使用命令行操作数据库
所有的应用程序本地文件都存放在/data/data/目录下 C:\Users\nicole>adb shell * daemon not running. starting it now on ...
- mybatis_02简单操作数据库
模糊查询用户信息 <!-- [${}]:表示拼接SQL字符串 [${value}]:表示要拼接的是简单类型参数. 注意: 1.如果参数为简单类型时,${}里面的参数名称必须为value 2.${ ...
- SQLiteDatabase里面的简单操作数据库的方法
1.使用insert方法插入记录SQLiteDatabase的insert方法的签名为long insert(String table,String nullColumnHack,ContentVal ...
- spring框架整合hibernate框架简单操作数据库
1.配置文件: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http:/ ...
- pyqt5最简单操作数据库
要先安一个包才能使用QtSql通过新立得安装 import PyQt5.QtSql as sql db=sql.QSqlDatabase.addDatabase('QMYSQL') db.setDat ...
- spring-boot-route(七)整合jdbcTemplate操作数据库
在一部分内容中,我们学习了Restful接口的编写,及接口文档的生成.我们需要将接口数据进行持久化存储,这一部分我们主要学习几种持久化框架将数据进行存储.本部分内容中,我们都将使用mysql为例来做为 ...
随机推荐
- Linux下定时清空某个文件
问题 在一台单点机器部署完成且运行一段时间后,发现页面接口报错,登上机器发现磁盘满了.通过du -lh --max-depth=1 和 du -sh * 找出是哪个文件. 发现是 项目中 stdout ...
- 好用工具:Wappalyzer
说明 这个插件可以检测到网站使用的技术栈,是一个好玩的技术嗅探插件 安装 使用
- 手动安装vur-router并引用
安装并引用 安装 npm install vue-router 引用 步骤一:在src路径下,创建router文件夹, 其下创建index.js // router/index.js import V ...
- 使用 FastGPT 构建高质量 AI 知识库
作者:余金隆.FastGPT 项目作者,Sealos 项目前端负责人,前 Shopee 前端开发工程师 FastGPT 项目地址:https://github.com/labring/FastGPT/ ...
- CSSRelated
CSS 几种常用的清除浮动方法 ️️️ 父级 div 定义伪类:after 和 zoom; /* 这个class名指的是需要清除浮动的父级 */ .clearfloat:after { display ...
- tcp3次握手
tcp3次握手 1,三次握手流程图 2,三握手过程 当pc1想和pc2建立起连接时 pc1将连接信息写入报文 2.1,报文的序号(seq=x) 同步位(请求建立连接关系: SYN=1 ACK=0 控制 ...
- WPF实现跳动的字符效果
本文将介绍一个好玩但实际作用可能不太大的动画效果:跳动的字符.为了提高动画效果的可重用性以及调用的灵活性,通过Behavior实现跳动的字符动画.先看下效果: 技术要点与实现 通过TextEffect ...
- 知识图谱(Knowledge Graph)- Neo4j 5.10.0 Desktop & GraphXR
下载地址:https://neo4j.com/download/ 安装 下载时会产生激活码(保存下来) 下载完成后安装 运行后,输入激活码 进入主页面 运行自带的电影知识谱图测试是否安装成功 安装 G ...
- 原生CSS嵌套简介
嵌套是使用Sass等CSS预处理器的核心原因之一.现在,该功能已经以类似的语法出现在标准浏览器CSS中.你能否在构建系统时放弃对预处理器的依赖? CSS嵌套可以节省输入时间,并使语法更易于阅读和维护. ...
- DDD 架构分层,MQ消息要放到那一层处理?
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 本文的宗旨在于通过简单干净实践的方式教会读者,使用 Docker 配置 RocketMQ 并在 ...