概述

在 Android 代码中创建和修改 SQLite 数据库,我们可以参考 Android 文档 Save data using SQLite,我们在 Android 中需要采取两个基本步骤来设置 SQLite 就可以和数据库互动了。如下所示:

Define a schema and contract-创建架构和契约类
Create a database using an SQLOpenHelper-使用 SQLiteOpenHelper 创建数据库

完成上面两步后就可以执行创建、读取、更新和删除我们的数据的操作了。
创建架构和契约类

创建数据库架构其实就是需要规划数据库结构。所以我们需要问两个问题:

表格的名称是什么?
表格里列的名称和数据类型是什么?

还记得下面的语句吗?

CREATE TABLE <table_name> (<column_name_1> <data_type_1>, <column_name_2> <data_type_2>, ...);

以宠物收容所场景为例:

绘制表格,规划数据库结构

表格

创建 Contract (契约)类

为什么要使用 Contract 类?

看个创建表格的两个例子:

String makeTableStatement = "CREATE TABLE entry (_id INTEGER PRIMARY KEY, entryid TEXT, title TEXT, subtitle TEXT)";String SQL_CREATE_ENTRIES = "CREATE TABLE" + FeedEntry.TABLE_NAME + "(" + FeedEntry._ID + "INTEGER PRIMARY KEY," + FeedEntry.COLUMN_NAME_ENTRY_ID + "TEXT," + FeedEntry.COLUMN_NAME_TITLE+"TEXT);"

观察上述代码,我们发现第二个创建表格的语句中 表格名称和列名称存储在了常量里。 在生成 SQL 指令时,消除了出现拼写错误的可能性,或者不小心将不应该大写的字母大写了。如果想更改实际的列名称,也只需在一个地方更改下就行了。

总结使用 Contract 类的三个理由:

  • 帮助我们定义架构,规定了去哪里查找数据库常量;
  • 在生成 SQL 指令时,可以帮助我们避免拼写错误;
  • 使我们更容易更新数据库架构
创建 Contract 类

根据之前规划的数据库结构即我们定义的架构,我们在项目的主包下面创建一个包名为 data,然后在改包下创建一个新的 java 类名为 PetContract,将该类用 final 修饰,因为它只是用来提供常量,我们不需要扩展或为此外部类实现任何内容。使用我们定义的架构,为每个表格创建一个内部类,并为每个列标题创建常量,代码如下所示:

public final class PetContract {private PetContract() {}public static final class PetEntry implements BaseColumns {public final static String TABLE_NAME = "pets";public final static String _ID = BaseColumn._ID;public final static String COLUMN_PET_NAME = "name";public final static String COLUMN_PET_BREED = "breed";public final static String COLUMN_PET_GENDER = "gender";public final static String COLUMN_PET_WEIGHT = "weight";public final static int GENDER_UNKONWN = 0;public final static int GENDER_MALE = 1;public final static int GENDER_FORMALE = 2;}
}

使用 SQLiteOpenHelper 创建数据库

在 dada 包中创建一个新的 PetDbHelper 类,该类继承自 SQLiteOpenHelper类。我们需要重写 onCreate() 和 onUpGrade() 方法,并且为数据库名称和版本创建常量,同时别忘了创建该类的构造函数,还要为用来创建表格的 SQLite 指令创建一个字符串常量。

/*** Database helper for Pets app. Manages database creation and version management.*/
public class PetDbHelper extends SQLiteOpenHelper {public static final String LOG_TAG = PetDbHelper.class.getSimpleName();/** Name of the database file */private static final String DATABASE_NAME = "shelter.db";/*** Database version. If you change the database schema, you must increment the database version.*/private static final int DATABASE_VERSION = 1;/*** Constructs a new instance of {@link PetDbHelper}.** @param context of the app*/public PetDbHelper(Context context) {super(context, DATABASE_NAME, null, DATABASE_VERSION);}/*** This is called when the database is created for the first time.*/@Overridepublic void onCreate(SQLiteDatabase db) {// Create a String that contains the SQL statement to create the pets tableString SQL_CREATE_PETS_TABLE =  "CREATE TABLE " + PetEntry.TABLE_NAME + " ("+ PetEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "+ PetEntry.COLUMN_PET_NAME + " TEXT NOT NULL, "+ PetEntry.COLUMN_PET_BREED + " TEXT, "+ PetEntry.COLUMN_PET_GENDER + " INTEGER NOT NULL, "+ PetEntry.COLUMN_PET_WEIGHT + " INTEGER NOT NULL DEFAULT 0);";// Execute the SQL statementdb.execSQL(SQL_CREATE_PETS_TABLE);}/*** This is called when the database needs to be upgraded.*/@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// The database is still at version 1, so there's nothing to do be done here.}
}

写了这么多我们也不知道数据库是否能正常运行,我们可以通过下面的方法进行检查。

查看数据库是否正常运转:

创建 displayDatabaseInfo() 方法,将其放置到我们代码中,只有 SQL 能正常运转时,该方法才能运转。
将数据库下载到本地,通过在终端使用 SQL 语句查看。

@Override
protected void onCreate(Bundle savedInstanceState) {

displayDatabaseInfo()
}

/**
* Temporary helper method to display information in the onscreen TextView about the state of
* the pets database.
*/
private void displayDatabaseInfo() {
// To access our database, we instantiate our subclass of SQLiteOpenHelper
// and pass the context, which is the current activity.
PetDbHelper mDbHelper = new PetDbHelper(this);

    // Create and/or open a database to read from itSQLiteDatabase db = mDbHelper.getReadableDatabase();// Perform this raw SQL query "SELECT * FROM pets"// to get a Cursor that contains all rows from the pets table.Cursor cursor = db.rawQuery("SELECT * FROM " + PetEntry.TABLE_NAME, null);try {// Display the number of rows in the Cursor (which reflects the number of rows in the// pets table in the database).TextView displayView = (TextView) findViewById(R.id.text_view_pet);displayView.setText("Number of rows in pets database table: " + cursor.getCount());} finally {// Always close the cursor when you're done reading from it. This releases all its// resources and makes it invalid.cursor.close();}
}

连接数据库流程

PetDbHelper mDbHelper = new PetDbHelper(this);
SQLiteDatabase db = mDbHelper.getReadableDatabase();

请求数据库,mDbHelper 将检查是否已经存在一个数据库?

如果不存在,则 PetDbHelper 的实例将使用 onCreate() 方法创建一个数据库,接着创建 SQLiteDatabase 的实例对象返回给 Activity。

如果数据库已经存在,PetDbHelper 的实例将不会调用 onCreate() 方法,相反,将创建一个 SQLiteDatabase 的实例对象并关联现有的数据库,然后将该对象返回给请求数据库的 Activity。

最终时帮助我们创建 SQLiteDatabase 对象与 shelter 数据库关联的 SQLiteDatabase 对象,之后我们就可以通过该对象向 shelter 数据库传达 SQLite 指令了。
从设备中提取数据库

了解了数据库的连接流程,我们就可以检测数据库是否正常运转。当然我们还可以通过另一种方式,通过查看设备的文件系统,使我们能够实际地看到 petDbHelper 类在创建数据库,然后当数据库实际创建好后看到 .db 文件出现。

在这之前我们可以打开手机应用信息,将 pets 应用中清楚缓存和存储数据。同时注释掉代码中的 displayDatabaseInfo() 方法。在 Android Studio 中点击 Android Device Monitor 按钮,选择我们当前使用的模拟器,转到 File Explorer,该功能可以让我们浏览设备的文件系统,点开 data 文件夹,再点开里面的 data 文件夹,在里面找到我们应用的包,再打开包,查找文件,我们会发现没有 .db 文件,这时我们的 getReadableDatabase() 方法尚未被调用。

当我们把刚才注释掉的 displayDatabaseInfo() 方法重新恢复,运行代码后,我们再查看文件系统中该包下有了 shelter.db 数据库文件了。再运行一次就不会再出现新的文件了。如果想看 shelter.db 文件中的内容,可以通过 DDMS 下载文件,保存到电脑本地上,通过 SQL 语言在终端查看。


数据库对象及插入数据

在 Android 文档 Put information into a database 中,为我们举出了例子。

// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);// Insert the new row, returning the primary key value of the new row
long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);

ContentValues 中存储了大量的键值对,键是数据库中的列名称,值就是插入的值。举例:

ContentValues values = new ContentValues();
values.put(PetEntry.COLUMN_PET_NAME, "Garfield");
values.put(PetEntry.COLUMN_PET_BREED, "Tabby");
values.put(PetEntry.COLUMN_PET_GENDER, PetEntry.GENDER_MALE);
values.put(PetEntry.COLUMN_PET_WEIGHT, 7);
db.insert(PetEntry.TABLE_NAME, null, values);

注意 insert() 方法会返回新插入的 ID,如果出现错误它会返回 -1。
数据库查询方法

我们可以通过 db.rawquery() 方法来读取数据库,但不建议使用。就像存在 SQLiteDatabase.insert() 方法一样,我们可以使用 SQLiteDatabase.query() 方法来读取数据库,它会帮助我们构建查询,从而避免语法错误。我们看下 Android 文档来了解下:

SQLiteDatabase db = mDbHelper.getReadableDatabase();// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {BaseColumns._ID,FeedEntry.COLUMN_NAME_TITLE,FeedEntry.COLUMN_NAME_SUBTITLE};// Filter results WHERE "title" = 'My Title'
String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
String[] selectionArgs = { "My Title" };// How you want the results sorted in the resulting Cursor
String sortOrder =FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";Cursor cursor = db.query(FeedEntry.TABLE_NAME,   // The table to queryprojection,             // The array of columns to return (pass null to get all)selection,              // The columns for the WHERE clauseselectionArgs,          // The values for the WHERE clausenull,                   // don't group the rowsnull,                   // don't filter by row groupssortOrder               // The sort order);

观察上述代码,我们发现首先定义了一个字符串数组 projection,它其实是指我们想要获取的列名称,就像这样的语句:

SELECT name, breed FROM pets;

定义 projection 使我们能够指定想要获取的具体列,默认则获取所有列,类似于:

SELECT * FROM pets;

projection 大小会影响到性能。

调用 query() 方法,该方法具有大量输入参数,代表了 SELECT 语句的不同部分。第一个参数 projection 我们已经在上面介绍了,接下来 selection 和 selectionArgs 处理的是可选 WHERE 条件。例如根据 ID 选择单个宠物:

SELECT * FROM pets WHERE _id = 1;

使用 selection 和 selectionArgs 属性就是这样的:

// Define 'where' part of query
String selection = PetEntry._ID + "?";
// Specify arguments in placeholder order
String[] selectionArgs = {"1"};

selection 参数是 WHERE 关键字之后的类型为 String 的,这里使用 ? 作为占位符,然后填充为 selectionArgs 参数中的值,它是一个字符串数组,负责替换这里 selection 中的问号,这里将其设为 1。

为什么使用 ? 号和 selectionArgs ,而不是直接写成 1 ?

这里没有区别,我们可以将 selectionArgs 设为 null,将 ? 号改为 1 。但是在某些情况下,选择内容可能来自表格,使用占位符是一种安全措施,可以防止出现 SQL 注入攻击,也就是用户不按套路出牌,编辑一些代码类内容输入,使我们的查询语句出现歧义错误。

调用 query() 方法后会返回一个 Cursor 对象,它是一种可以捕获数据库中所有子集的对象。
什么是 Cursor

简而言之,Cursor 是指 代表数据库中多行内容的对象。假设如下是我们整个数据库的架构:
这里写图片描述

如果我们针对这个数据库中这个表格调用 query() 方法,我们可以指定希望返回的值,然后这些信息就会以 Cursor 对象的形式返回给我们。

如果我们的选择参数是

SELECT * FROM pets

我们获得的 Cursor 对象,其中包含数据库中的各行内容。

如果我们希望进一步指定仅选择 pets 表格中宠物名为 Toto 的行,我们获得的 CUrsor 对象就是其中所有行里的宠物名是 Toto 的行,结果如下:

我们再来看另一个示例,假设从 pets 表格中选择 name 和 breed,也就是将白鸽缩小为仅返回这两列:


也就是将表格缩小为仅返回这两列,返回的 Cursor 对象包含所有行,但是仅包含 name 和 breed 列的详情。

Cursor 包含了是我们能够访问和浏览 Cursor 对象的方法,如果 Cursor 包含多行的话就可以浏览各行。

Cursor.getCount()

Cursor 对象代表了数据库中的行和列,此外,他还提供了这些行中的当前位置,为了能获得特定的数据,我们需要将 Cursor 移动到我们所需要的精确行

当我们第一次获取 Cursor 时,位置从 -1 开始,这是无效的位置,首个可用位置为0,然后逐步递增。

Cursor.moveToFirst

该方法既将 Cursor 中的位置移动到结果中的第一行,这使我们能够访问第一条记录里的数据。

Cursor.moveToLast

该方法会使我们跳到这里的最后一行。

Cursor.moveToPosition(int position)

该方法会将 Cursor 位置移动到指定位置。

上面方法返回的都是 Boolean 类型的值,这样可以帮助我们判断实际是否移动到了该位置。比如我们当前 Cursor 已经移动到了最后一行,再调用向下移动的方法就会返回 false。

我们可以使用不同的 get 方法从数据库中获取特定的值。

使用 getColumnIndex(String columnName)方法,来根据名称来获取列的索引。举例:

对于 Cursor 要注意的一个事项是,使用完毕后,一定要记得调用 cursor.close(),这样会完全清空 Cursor 使其无效。仅在完全操作完毕后调用该方法。不关闭 Cursor 的话,会因内存泄漏而降低性能。

安卓开发之 在应用中使用数据库相关推荐

  1. 老牛知点所以然-Linux(Ubuntu)配置安卓开发环境及过程中常见问题解决

    文章目录 Linux安装JDK,配置JDK环境变量 安装SDK和gradle 安装AndroidStudio AndroidStudio创建模拟器报错 安装FireFox 建立Firefox桌面快捷方 ...

  2. 安卓开发替换json字符串中的数据_22个JavaScript开发技巧合集

    作者:kancloud 转发链接:https://www.kancloud.cn/dennis/tgjavascript/241855 开发技巧 1.使用var声明变量 如果给一个没有声明的变量赋值, ...

  3. 安卓开发——JNI——回调java中的方法

    JNI开发中 在C代码中回调java中的方法 package com.example.jnitest2;import android.app.Activity; import android.cont ...

  4. android项目添加图片,安卓开发:往项目中添加并使用图片

    一 往项目中添加图片文件 (1)iOS项目 在iOS项目中,如果往项目中添加的图片是png格式的,就是往Assets.xcassets文件夹中添加. 如果不是png格式,比如jpg格式的图片,就不能往 ...

  5. form字体和颜色java安卓开发_Android 修改App中默认TextView的字体和颜色

    一.别人怎么做 来源 http://stackoverflow.com/questions/3078081/setting-global-styles-for-views-in-android Act ...

  6. 安卓开发找工作现状,惊呆我了

    大家好,今天我想和大家谈谈在杭州找安卓开发工作的现状.在当今竞争激烈的互联网行业,安卓开发岗位也不例外.我有一个朋友曾经找过这个职位,和 HR 聊了些什么.他用英语问 HR:"你好,我对贵公 ...

  7. 安卓开发学习之Room数据库的使用

    闲来无事,总结一下前一阵子对Room数据开源库的学习. 数据库存储是安卓开发中数据存储的方式之一,并且安卓为我们提供了sqlite数据库和SQLiteOpenHelper等等强大的开发支持.而Room ...

  8. 安卓开发:本地数据库SQLite的使用

    上篇文章,讲述了安卓开发中文件存储和SharedPreferences存储方式.这里讲述SQLite数据库存储.我们的通讯录也存在与SQLite数据库中.存放的位置在:/data/data/com.a ...

  9. Android安卓开发中图片缩放讲解

    安卓开发中应用到图片的处理时候,我们通常会怎么缩放操作呢,来看下面的两种做法: 方法1:按固定比例进行缩放 在开发一些软件,如新闻客户端,很多时候要显示图片的缩略图,由于手机屏幕限制,一般情况下,我们 ...

最新文章

  1. 关于HtmlParser中Parser【org.htmlparser.Parser】这个类奇怪的地方...求解释【已获得解释】...
  2. ubuntu更换阿里源
  3. [html] 如何在网页中嵌入公司地址的地图?
  4. JdbcTemplate(操作数据库-修改和删除功能)
  5. 巧妙地在Windows搭建node服务器
  6. 转换运行时获取DTP语义组
  7. php编码机器语言,机器语言使用的编码是
  8. cisco和H3C命令对比
  9. win11如何查看下载速度 Windows11查看下载速度的方法介绍
  10. skywalking调研相关资料整理
  11. 北京林业大学本科毕业论文答辩和论文选题PPT模板
  12. 个推消息推送SDK通知栏铃声功能解析及使用攻略
  13. Android集成FBReader(精简版)指南
  14. 高并发与高可用知识总结
  15. html5微信页面制作,微信上的html5页面是怎么制作出来的?自己怎么制作微信网页链接?...
  16. 一叶知秋:“安全“的野指针、 static函数、成员函数、this 指针、gcc编译器、name mangling...
  17. Python-打印乘法口诀表
  18. 临床试验中的指标敏感性、特异性
  19. 领域驱动设计在美团点评业务系统的实践
  20. 【20221220】Windows通过网线共享网络及小猫咪局域网共享

热门文章

  1. 2021-2027全球与中国触控板市场现状及未来发展趋势
  2. Java基础练习项目【飞机大战】
  3. 【小家Spring】Spring AOP各个组件概述与总结【Pointcut、Advice、Advisor、Advised、TargetSource、AdvisorChainFactory...】
  4. 笨办法学Python笔记2(ex18~ex40)
  5. 信息学奥赛一本通C++语言-----1064:奥运奖牌计数
  6. 周测三,Linux命令如下:
  7. Tech.ED North America 2008 Launch! (June 10-13,2008)
  8. 单片机通过mqtt联网(51单片机和esp01s)
  9. Url跳转和伪静态html解决方案
  10. 【QT网络编程】实现UDP协议通信