当前位置: 首页 > news >正文

DataBase in Android

DataBase in Android

这篇文章是我在学习 Udacity 上的 Android 课程时, 写的知识总结.
可能会有一些内容来自 Udacity, 也有一些例子来自 Android 的开发文档.
总结好了想给大家分享, 如果我学到了新的东西, 我也会后续更新

我们知道, 如果我们的数据仅仅是存储在变量中的, 那么其生命周期可能只是和 Activity 一样长, 在我们推出 APP 之后, 我们的数据就会丢失. 我们需要数据持久化(data persistence). 我们可以将数据存储在文件或者数据库中. 这里我们讨论的是数据库的方式.

计算机存储

计算机有两大主要的存储设备, 一类是内存, 一类是外存. 内存(Memory), 是一种临时存储器, 读写速度快, 断电数据丢失, 所以不能长期保存数据. 外存, 包括硬盘, 软盘, 光盘等等, 读写速度慢, 但是可以长期保存数据, 断电不丢失. 我们平时所称的安装 APP, 是安装在外存中, 只有当程序运行的时候, 才会被加载到内存中(也必须被加载到内存中才能运行). 我们在程序中使用的变量, 他们的生命周期是随着程序的结束而结束的, 在程序启动时被创建和使用, 在程序结束时被系统销毁, 所以不能作为长久保存数据的工具.

Android 中的几种不同的数据存储选择

我们这里所讨论的内容, 是将数据存储到我们的 Android 设备中, 不包括存储到云端等方式.

  • Files, 文件
  • Shared Preferences
  • SQLite Databases, SQLite 数据库

Files, 保存到文件中

将保存为文件, 常见的有图片, 音乐, 视频的存储等.

Shared Preferences(首选项)

数据以键值对的形式来保存, (Key - Unique string, Value - Primitive types and Strings), 即包括一个唯一的字符串 key 和其对应的值, 值可以是各种原始数据类型或者是 String.

Shared Preferences 不适合存储大量的用户数据, 适合存储少量的关键的用户信息.

SQLite Databases

SQLLite 是一种数据库, 数据库简单来讲, 是一种有组织的数据结构.

SQLite 的基本组成简单来讲, 可以理解为表, SQLite 数据库由一张张的表来组成.

数据库可以让我们轻松的分享大量相关的结构化数据, 同时, 这些数据可能随着使用而不断增长. 比如我们的通讯录, 里面存储的每一个联系人的各种信息, 可以理解为一张表, 记录各种的属性, 比如姓名, 手机, 邮箱等等. 这些联系人之间, 属性和属性之间是相关的, 他们都是大致相同的格式化数据. 属性之间是相关的, "张三" 的手机号是 "130XXXXXXXX" 而不是其他. 同时, 通讯录还会因为我们的联系人的增多而添加内容, 还可以修改和删除内容. 对于这些数据, 我们可以轻松的检索到我们需要的信息.

SQLite

安装 SQLite

最新版的 MacOS 中已经预装了 SQLite, 打开终端输入 sqlite3, 可以检查是否有 SQLite.

SQL

当我们输入 sqlite3 命令进入 SQLite 之后会出现一下界面.

dcrdeMacBook-Pro:~ Dcr$ sqlite3
SQLite version 3.16.0 2016-11-04 19:09:39
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite>

这里我们可以使用 SQL(Structured query language, 结构化查询语言).

SQLite 中的类型

SQLite 中的 Storage Class:

  • NULL
  • INTEGER: 有符号整数
  • REAL: 浮点数
  • TEXT: 字符串
  • BLOB: 原始数据, 比如图片或者二进制数据.

对于 booleans, 我们可以使用 INTEGER 来代替:

  • 0 - false
  • 1 - true

语法

下面介绍 SQL 的基本语法.

CRUD

CRUD 是 Create, Read, Update, Delete 的缩写. 是数据库中的四项重要操作.

CREATE
Create a database

在 SQLite 中, 创建一个新的数据库, 可以在命令行中使用 sqlite3 <database_name>.db 来创建. 或者在进入 sqlite 之后, 使用 .open <database_name>.db 来创建.

Create a table

创建一张新的表需要指定表名和列名和对应的数据类型. 语法如下:

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

上面是创建表的基本语法, 我们创建时, 还可以给每列对应的参数添加属性, 基本的属性有:

  • PRIMARY KEY: 主键, 每张表只能有一个主键.
  • AUTOINCREMENT: 添加行时, 该属性自动加一, 可以用于指定每行的 ID.
  • NOT NULL: 非空, 表示添加行时, 该元素必须有值.
  • DEFAULT <value>: 默认值, 如果创建时没有提供值, 那么以默认值填充.
INSERT data in a table

向表中添加行:

INSERT INTO <table_name> (
    <column_name_1>,
    <column_name_2>,
    ...) VALUES (
    <values_1>,
    <values_2>,
    ...
);
READ

读操作使用的是 SELECT 关键字, 基本操作如下:

SELECT <columns>
FROM <table_name>;

SELECT 后跟的列项可以用 * 表示所有列.

执行上面的操作, 是查询一个表的所有行, 我们要查询我们所需要的行时, 可以使用 WHERE 关键字.

SELECT <columns>
FROM <table_name>
WHERE <condition>;

我们还可以指定结果的排序方式, 通过 ORDER BY 关键字.

SELECT <columns>
FROM <table_name>
WHERE <condition>
ORDER BY <ASC|DESC>
  • ASC: 升序
  • DESC: 降序
UPDATE

UPDATE 用于更新表中的数据.

UPDATE <table_name>
SET <column_name> = <value>
WHERE <condition>;

同样, 使用 WHERE 指定所要修改的行.

DELETE
删除行

删除满足 WHERE 条件的行.

注意: 使用 DELETE 一定要非常小心, 比如, DELETE FROM table_name 就会删除所有行.

DELETE FROM <table_name>
WHERE <condition>;
删除表

删除表使用 DROP TABLE 关键字:

DROP TABLE <table_name>;

SQLite 中的命令

  • .shema <table_name>: 显示创建表的命令.
  • PRAGMA TABLE_INFO(<table_name>);: 显示表的列信息

SQLite in Android

[Why is a database always represented with a cylinder?](https://stackoverflow.com/que...
)

确定数据库的架构

  1. 确定表的名称
  2. 每一列数据的名称和数据类型

TABLE_NAME = "pets"

Column nameData typesAttributes
_idINTEGERPRIMARY KEY AUTOINCREEMENT
nameTEXTNOT NULL
breedTEXT
genderINTEGERNOT NULL DEFAULT 0
weightINTEGERNOT NULL DEFAULT 0

Contract

我们在使用数据库时, 会用到很多的变量, 为了方便和不出错, 我们可以将这些变量单独拿出来, 这就是 Contract 类.

Contract 类中, 定义了我们将用到的许多常量, 比如, URI, 表名, 列名以及一些有特殊意义的整数值等等. 下面是一个简单的例子: 例子中, 重写了私有的构造函数, 因为我们并不需要该类的对象.

public final class FeedReaderContract {
    // To prevent someone from accidentally instantiating the contract class,
    // make the constructor private.
    private FeedReaderContract() {}

    /* Inner class that defines the table contents */
    public static class FeedEntry implements BaseColumns {
        public static final String TABLE_NAME = "entry";
        public static final String COLUMN_NAME_TITLE = "title";
        public static final String COLUMN_NAME_SUBTITLE = "subtitle";
    }
}

有了 Contract 类之后, 我们就可以写出我们常用的 SQL 语句了. 比如, 下面就是我们常用的 CREATE TABLEDROP TABLE 语句.

private static final String SQL_CREATE_ENTRIES =
    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
    FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedEntry.COLUMN_NAME_TITLE + " TEXT," +
    FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";

private static final String SQL_DELETE_ENTRIES =
    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

通过 SQLHelper 创建数据库

在 Android 中, 数据库默认存储在应用的私有区域的, 这一区域默认情况下其他应用是无法访问的, 所以是安全的.

我们可以使用 SQLiteOpenHelper 类来获取数据库的引用, 此时, 系统会在需要的时候才会执行 creating 和 update 数据库这些费时的操作, 而不是在应用启动的时候, 我们需要做的就是去调用 getWritableDatabase() 或者 getReadableDatabase() 方法.

注意: 因为这样的操作可能是长时间操作, 所以, 务必请在后台线程调用 getWritableDatabase() 或者 getReadableDatabase() 方法.

使用时, 我们可以继承 SQLiteOpenHelper 类重写 onCreate(), onUpgrade(), 和 onOpen() 回调方法, 在需要时, 也可以重写 onDowngrade() 方法. 比如:

public class FeedReaderDbHelper extends SQLiteOpenHelper {
    // If you change the database schema, you must increment the database version.
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "FeedReader.db";

    public FeedReaderDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_ENTRIES);
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // This database is only a cache for online data, so its upgrade policy is
        // to simply to discard the data and start over
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        onUpgrade(db, oldVersion, newVersion);
    }
}

我们在需要数据库的时候, 使用下面的语句.

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

在数据库中存入数据

我们在将数据存入数据库中时, 可以使用 ContentValues 对象来进行.

ContentValues 对象就是键值对组, 操作起来也比较简单. 操作步骤如下:

// 1. 获取 SQLiteDatabase 对象
SQLiteDatabase db = mDbHelper.getWritableDatabase();

// 2. 创建值的 map, keys 是列名
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);

// 3. 加入到数据库中去
long newRowId = db.insert(FeedEntry.Table_name, null, values);

从数据库中读取数据

method query()

首先我们可以先了解一下 query() 方法.

SQLiteDatabase.query():

Cursor query (String table,
                String[] columns,
                String selection,
                String[] selectionArgs,
                String groupBy,
                String having,
                String orderBy,
                String limit)
Parameters
tableString: 要查询的表
columnString[]: 要查询的列的数组。
selectionString: 对应 SQL 中的 WHERE 语句部分(不包括 WHERE 本身).
selectionArgsString[], 在 selection 参数中, 可能包含 ? 作为参数的占位符, 在这个参数中给出.
groupByString: 对应 SQL 语句中的 GROUP BY 语句(不包括 GROUP BY 本身).
havingString: 对应 HAVING 语句(不包括 HAVING 本身).
orderByString: SQL ORDER BY 语句(不包括 ORDER BY 本身)
limitString: 返回的行数, LIMIT 分句的格式
SQLiteDatabase db = mDbHelper.getReadableDatabase();

// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    FeedEntry._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 query
    projection,                               // The columns to return
    selection,                                // The columns for the WHERE clause
    selectionArgs,                            // The values for the WHERE clause
    null,                                     // don't group the rows
    null,                                     // don't filter by row groups
    sortOrder                                 // The sort order
    );

Cursor

我们的数据库查询语句返回一个 Cursor 对象, 那么什么是 Cursor 呢? Cursor 是查询结果中行的集合.

首先, 顾名思义, Cursor 有一个 position 的指针, 指向的就是当前的行. 行的编号是从 0 开始的, 初始 position = -1.

其工作模式就是, 通过一系列 move 方法, 来选中行, 再通过不同的 get 方法来获取我们需要的数据.

在确定列的时候, 我们需要知道列名, 然后通过 getColumnIndex() 或者 getColumnIndexOrThrow() 来获取 column index. 有了 column index, 我们就可以通过 getInt() getFloat() 等一系列方法来获取具体的数据.

  • getCount(): 返回 cursor 的行数.
  • getType(int columnIndex): 返回给定的列的类型
  • getDouble(int columnIndex) getFloat(int columnIndex) getInt(int columnIndex) getLong(int columnIndex) getString(int columnIndex) getShort(int columnIndex): 返回对应类型的值.

所有的 move 方法返回一个 boolean 值, 表示能否移动到该行.

  • moveToFirst(): 移动到第一行
  • moveToLast(): 移动到最后一行
  • moveToNext(): 移动到下一行
  • moveToPosition(int position): 移动到指定位置
  • moveToPrevious(): 移动到前一次移动的行

相关文章:

  • 日志收集方式总结(转载)
  • 编译脚本支持(Build script support)
  • Docker 后台进程参数-------更改Docker运行根目录的方法
  • Python 爬虫入门3种方法
  • GSSAPIAuthentication=no
  • 红帽推 Ansible Tower 3.1 扩展 DevOps自动化功能
  • 从边缘计算看移动化与云计算的融合之道
  • 卡巴斯基:Duqu2.0病毒使用的数字证书窃取自富士康
  • 网络攻击检测:五大致命弱点和解决办法
  • Arbor Networks发布新安全平台 通过内部企业网络连接互联网上的攻击
  • 中国大数据利用率仅0.4%?行业大咖教你如何用好大数据
  • Spring IoC 学习(4)
  • 为什么要点两下才能删除一个li节点 原来是空白节点作怪
  • 《中国人工智能学会通讯》——3.30 改革性成就
  • spring+SpringMVC+mybati整合JBPM
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 【面试系列】之二:关于js原型
  • Angular4 模板式表单用法以及验证
  • CentOS学习笔记 - 12. Nginx搭建Centos7.5远程repo
  • JS变量作用域
  • Laravel深入学习6 - 应用体系结构:解耦事件处理器
  • maven工程打包jar以及java jar命令的classpath使用
  • Node.js 新计划:使用 V8 snapshot 将启动速度提升 8 倍
  • NSTimer学习笔记
  • Rancher如何对接Ceph-RBD块存储
  • vue的全局变量和全局拦截请求器
  • 基于Volley网络库实现加载多种网络图片(包括GIF动态图片、圆形图片、普通图片)...
  • 配置 PM2 实现代码自动发布
  • 浅谈Golang中select的用法
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 做一名精致的JavaScripter 01:JavaScript简介
  • Java数据解析之JSON
  • #《AI中文版》V3 第 1 章 概述
  • (BFS)hdoj2377-Bus Pass
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (PHP)设置修改 Apache 文件根目录 (Document Root)(转帖)
  • (pojstep1.1.2)2654(直叙式模拟)
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (博弈 sg入门)kiki's game -- hdu -- 2147
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (附源码)springboot家庭装修管理系统 毕业设计 613205
  • (亲测有效)解决windows11无法使用1500000波特率的问题
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (顺序)容器的好伴侣 --- 容器适配器
  • (四)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
  • (转)JAVA中的堆栈
  • (转)菜鸟学数据库(三)——存储过程
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • (轉貼) 蒼井そら挑戰筋肉擂台 (Misc)
  • .gitignore文件设置了忽略但不生效
  • .NET 常见的偏门问题
  • .NET开发不可不知、不可不用的辅助类(一)
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复