Android开发8:数据存储(二)——SQLite数据库和ContentProvider的使用
前言
啦啦啦各位小伙伴们许久不见了~学期末和过年期间自己忙着做其他事没能及时更新Android开发系列课程的博客,实在是罪过罪过~
好啦~废话不多说,进入我们今天的主题。今天我们将和大家学习其他的数据存储的方法,一起来学习SQLite数据库和ContentProvider的使用,复习Android界面编程等知识。
基础知识
SQLite
一. SQLite的介绍
1.SQLite简介
SQLite是一款轻型的数据库,是遵守ACID的关联式数据库管理系统,它的设计目标是嵌入 式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持 Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如Tcl、PHP、Java、C++、.Net等,还有ODBC接口,同样比起 Mysql、PostgreSQL这两款开源世界著名的数据库管理系统来讲,它的处理速度比他们都快。
2.特点:
轻量级——SQLite和C/S模式的数据库软件不同,它是进程内的数据库引擎,因此不存在数据库的客户端和服务器。使用SQLite一般只需要带上它的一个动态 库,就可以享受它的全部功能。而且那个动态库的尺寸也挺小,以版本3.6.11为例,Windows下487KB、Linux下347KB。
不需要"安装"——SQLite的核心引擎本身不依赖第三方的软件,使用它也不需要"安装"。有点类似那种绿色软件。
单一文件——数据库中所有的信息(比如表、视图等)都包含在一个文件内。这个文件可以自由复制到其它目录或其它机器上。
跨平台/可移植性——除了主流操作系统 windows,linux之后,SQLite还支持其它一些不常用的操作系统。
弱类型的字段——同一列中的数据可以是不同类型。
开源
二. SQLiteDatabase的介绍
Android提供了创建和是用SQLite数据库的API。SQLiteDatabase代表一个数据库对象,提供了操作数据库的一些方法。在Android的SDK目录下有sqlite3工具,我们可以利用它创建数据库、创建表和执行一些SQL语句。
1、打开或者创建数据库
在Android 中使用SQLiteDatabase的静态方法openOrCreateDatabase(String path,SQLiteDatabae.CursorFactory factory)打开或者创建一个数据库。它会自动去检测是否存在这个数据库,如果存在则打开,不存在则创建一个数据库;创建成功则返回一个SQLiteDatabase对象,否则抛出异常FileNotFoundException。
下面是创建名为“stu.db”数据库的代码:
openOrCreateDatabase(String path,SQLiteDatabae.CursorFactory factory)
参数1 数据库创建的路径
参数2 一般设置为null就可以了
db=SQLiteDatabase.openOrCreateDatabase("/data/data/com.lingdududu.db/databases/stu.db",null);
2、创建表
创建一张表的步骤很简单:
编写创建表的SQL语句
调用SQLiteDatabase的execSQL()方法来执行SQL语句
下面的代码创建了一张用户表,属性列为:id(主键并且自动增加)、sname(学生姓名)、snumber(学号)
private void createTable(SQLiteDatabase db){
//创建表SQL语句
String stu_table="create table usertable(_id integer primary key autoincrement,sname text,snumber text)";
//执行SQL语句
db.execSQL(stu_table);
}
3、插入数据
插入数据有两种方法:
①SQLiteDatabase的insert(String table,String nullColumnHack,ContentValues values)方法,
参数1 表名称,
参数2 空列的默认值
参数3 ContentValues类型的一个封装了列名称和列值的Map;
②编写插入数据的SQL语句,直接调用SQLiteDatabase的execSQL()方法来执行
第一种方法的代码:
private void insert(SQLiteDatabase db){
//实例化常量值
ContentValues cValue = new ContentValues();
//添加用户名
cValue.put("sname","xiaoming");
//添加密码
cValue.put("snumber","01005");
//调用insert()方法插入数据
db.insert("stu_table",null,cValue);
}
第二种方法的代码:
private void insert(SQLiteDatabase db){
//插入数据SQL语句
String stu_sql="insert into stu_table(sname,snumber) values('xiaoming','01005')";
//执行SQL语句
db.execSQL(sql);
}
4、实现增加、更新和删除这 3 种操作有两种方法:
不管是哪种方法,记得先 getWritableDatabase()
(a)用 execSQL 方法直接执行相应的 SQL 语句,比如增加(如下)。
SQLiteDatabase db = getWritableDatabase();
String insert_sql="INSERT INTO <表名>(<列 1>,<列 2>,…)values(<值 1>,<值 2>,…)";
db.execSQL(insert_sql);
(b)使用相应的 insert、update 和 delete 方法
I.insert 方法需要使用 ContentValues 来存放要添加的数据,见下图
II.update 方法需要使用 ContentValues 和 Where 语句。(下图只是说明性代码)
III、delete 方法需要使用 where 语句。(下图只是说明性代码)
5、查询数据
实现查询操作可以使用 rawQuery 或 query 函数,它们的区别类似于上面,前者直接执行
SQL 语句,后者是通过参数组合产生 SQL 语句。(下图只是说明性代码)
进行查询前,记得先 getReadableDatabase()
rawQuery 或者 query 函数返回的都是 Cursor,关于 Cursor 类的详细介绍请看下面
的链接:
http://www.cnblogs.com/TerryBlog/archive/2010/07/05/1771459.html
三. SQLiteOpenHelper
该类是SQLiteDatabase一个辅助类。这个类主要生成一 个数据库,并对数据库的版本进行管理。当在程序当中调用这个类的方法getWritableDatabase()或者 getReadableDatabase()方法的时候,如果当时没有数据,那么Android系统就会自动生成一个数据库。 SQLiteOpenHelper 是一个抽象类,我们通常需要继承它,并且实现里面的3个函数:
1.onCreate(SQLiteDatabase)
在数据库第一次生成的时候会调用这个方法,也就是说,只有在创建数据库的时候才会调用,当然也有一些其它的情况,一般我们在这个方法里边生成数据库表。
2. onUpgrade(SQLiteDatabase,int,int)
当数据库需要升级的时候,Android系统会主动的调用这个方法。一般我们在这个方法里边删除数据表,并建立新的数据表,当然是否还需要做其他的操作,完全取决于应用的需求。
3. onOpen(SQLiteDatabase):
这是当打开数据库时的回调函数,一般在程序中不是很常使用。
Content Provider 使用
1.适用场景
1) ContentProvider为存储和读取数据提供了统一的接口
2) 使用ContentProvider,应用程序可以实现数据共享
3) android内置的许多数据都是使用ContentProvider形式,供开发者调用的(如视频,音频,图片,通讯录等)
2.ContentProvider简介
当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式。
3.使用 getContentResolver 方法读取联系人列表
Cursor cursor =getContentResolver().query(ContactsContract.Contacts. CONTENT_URI , null, null, null, null);
判断某条联系人的信息中,是否有电话号码。
int isHas =Integer. parseInt (cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts. HAS_PHONE_NUMBER )));
取出该条联系人信息中的电话号码
Cursor c =getContentResolver().query(ContactsContract.CommonDataKinds.Phone. CON TENT_URI , null, ContactsContract.CommonDataKinds.Phone. CONTACT_ID + " = " + id, null, null); while (c.moveToNext()) { number +=c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone. N N UMBER )) + " "; } c.close();
自定义的对话框实现
使用 LayoutInflater 类,如下:
如此之后,我们就可以将一个 layout 的内容(dialoglayout)全都显示在对话框
(builder)里了。之后便可以对 layout 里面的元素进行操作了。
实验内容
实现一个生日备忘簿
技术要求:
1、 使用 SQLite 数据库保存生日的相关信息,使得每次运行程序都可以显示出已经存 储在数据库里的内容;
2、 使用 ContentProvider 来获取对应寿星的电话号码;
功能要求:
1、 主界面包含增加生日条目按钮和生日信息列表;(见图 1,图 2)
2、 点击<增加条目>按钮跳转到次界面;
3、 次界面输入生日相关信息后点击<增加>按钮会返回主界面(同时更新主界面的生日 信息列表),且姓名字段不能为空,姓名字段不能重复 (见图 3,图 4)
4、 主界面中的列表点击事项处理:
a) 单击(查看并可修改该生日条目):(见图 5)
i. 弹出对话框,显示该条目的相关信息,并提供修改。
ii. 同时,显示该生日条目寿星的电话号码;
iii. 点击<保存修改>按钮,更新主界面的生日信息列表
b) 长按(可删除该生日条目):(见图 6)
i. 弹出对话框,显示是否删除;
ii. 点击<是>按钮,删除该生日条目,并更新主界面的生日信息列表
图 1 首次启动 图 2 增加一些条目后
图 3 名字不能重复 图 4 长按处理
图 5 点击处理 图 6 名字不能为空
实验过程
本次实验主要是实现一个联系人生日备忘录,实现数据存储的功能。本次实验主要涉及 SQLite 数据库的使用以及在获取通讯录中联系人电话信息时的 ContentProvider 的使用。
首先,写好几个界面的 XML 布局文件。这里我们需要使用初始界面、增粘条 目界面、自定义对话框界面以及 listview 的 item 界面共四个 xml 布局文件。需 要注意的是,为了保证列表标题栏和列表项能够对齐,这里将标题栏布局和 listview 的 item 界面布局中的三个 TextView 的 layout_weight 属性比例设置 为 1:1:2。
接下来完成 MainActivity.java 类,在初始化控件后,主要完成几个点击按 钮的事件。单击增加条目按钮,我们将跳转到AddActivity.java 的界面中:
并设置了一个退出按钮:
在增加了 listview 的条目之后,单击 listview 我们将获取手机中的通讯录 中的联系人电话信息,并跳转到自定义对话框中进行编辑。
获取手机中的通讯录中的联系人电话信息需要使用 Content Provider。首先我们在 AndroidManifest.xml 文件里声明读取通讯录的权限(这里具体的方 法在“实验中遇到的问题及解决方法”一栏中详述,这里就不加赘述)。然后使 用 getContentResolver 方法读取联系人列表,并判断某条联系人的信息中,是 否有电话号码,若存在相应的联系人号码,则取出该条联系人信息中的电话号码; 如果手机通讯录中没有对应的联系人则将手机设为无:
自定义的对话框实现需要使用 LayoutInflater 类:
之后,我们就可以将一个 layout 的内容(dialoglayout)全都显示在对话框(builder)里,然后便可以对 layout 里面的元素进行操作。
在自定义对话框中,我们需要按照要求进行设置:
除了这些以外,我们还需要实时更新 UI,这里,我们定义一个状态更新的 函数,在需要调用的时候进行调用进行实时更新(同时也进行数据库内容的更新):
在 AddActivity 类中,我们主要还是完成各个按钮的事件。 在增加条目的按钮事件中,我们需要根据需要输出相应的 Toast 信息(当编辑框为空时,输出相应的 Toast 信息;当姓名编辑框与数据库中已存在的数据相 同时,输出相应的 Toast 信息):
还有清除和取消按钮,这里就不加赘述。
在我们这次实验的 主要内容数据库中 ( MyDataBase 类中 ) , 使 用 SQLiteOpenHelper 的子类能更方便实现要求。首先我们需要创建类:
创建数据库,可直接执行创建数据库的 SQl 语句:
在对应的位置使用相应的 insert、update 和 delete 方法,完成实现增加、更新和删除这3种操作 。 这里, 我们先getWritableDatabase(),用 execSQL方法直接执行相应的SQL语句(实现查 询操作使用 rawQuery):
完成实验~
实验截图
知识总结
SQLite 简介:
SQLite 是一个非常流行的嵌入式数据库,它支持 SQL 语言,并且只利用很 少的内存就有很好的性能。此外它还是开源的,任何人都可以使用它。
(1)Android 开发中使用 SQLite 数据库
Activities 可以通过 Content Provider 或者 Service 访问一个数据库。 创建数据库
Android 不自动提供数据库。在 Android 应用程序中使用 SQLite,必须自 己创建数据库,然后创建表、索引,填充数据。Android 提供了 SQLiteOpenHelper 帮助你创建一个数据库,你只要继承 SQLiteOpenHelper 类,就可以轻松的创建
数据库。SQLiteOpenHelper 类根据开发应用程序的需要,封装了创建和更新数 据库使用的逻辑。SQLiteOpenHelper 的子类,至少需要实现三个方法:
a.构造函数
调用父类 SQLiteOpenHelper 的构造函数。这个方法需要四个参数:上下文 环境(例如,一个 Activity),数据库名字,一个可选的游标工厂(通常是 Null), 一个代表你正在使用的数据库模型版本的整数。
b.onCreate()方法
它需要一个 SQLiteDatabase 对象作为参数,根据需要对这个对象填充表和 初始化数据。
c.onUpgrage() 方法
它需要三个参数,一个 SQLiteDatabase 对象,一个旧的版本号和一个新的 版本号,这样你就可以清楚如何把一个数据库从旧的模型转变到新的模型。
(2)调用 getReadableDatabase() 或 getWriteableDatabase() 方法,你可以 得到 SQLiteDatabase 实例,具体调用那个方法,取决于你是否需要改变数据库 的内容:
db=(new DatabaseHelper(getContext())).getWritableDatabase();
return (db == null) ? false : true;
上面这段代码会返回一个 SQLiteDatabase 类的实例,使用这个对象,你就 可以查询或者修改数据库。
当完成了对数据库的操作(例如 Activity 已经关闭),需要调用 SQLiteDatabase 的 Close() 方法来释放掉数据库连接。
(3)创建表和索引
为了创建表和索引,需要调用 SQLiteDatabase 的 execSQL() 方法来执行 DDL 语句。如果没有异常,这个方法没有返回值。
SQLite 会自动为主键列创建索引。 通常情况下,第一次创建数据库时创建了表和索引。如果你不需要改变表的
schema,不需要删除表和索引 . 删除表和索引,需要使用 execSQL() 方法调用 DROP INDEX 和 DROP TABLE 语句。
(4)给表添加数据
有两种方法可以给表添加数据。
像上面创建表一样,你可以使用 execSQL() 方法执行 INSERT, UPDATE, DELETE 等语句来更新表的数据。execSQL() 方法适用于所有不返回结果的 SQL 语 句 。 另 一 种 方 法 是 使 用 SQLiteDatabase 对 象 的 insert(), update(), delete() 方法。
ContentProvider 简介:
当应用继承 ContentProvider 类,并重写该类用于提供数据和存储数据的方 法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但 数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需 要进行文件操作读写数据;采用 sharedpreferences 共享数据, 需要使用 sharedpreferences API 读写数据。而使用 ContentProvider 共享数据的好处是 统一了数据访问方式。
适用场景
1)ContentProvider 为存储和读取数据提供了统一的接口
2)使用 ContentProvider,应用程序可以实现数据共享
3)android 内置的许多数据都是使用 ContentProvider 形式,供开发者调用 的(如视频,音频,图片,通讯录等)
创建ContentProvider
要创建我们自己的Content Provider的话,我们需要遵循以下几步:
a. 创建一个继承了ContentProvider父类的类
b. 定义一个名为CONTENT_URI,并且是public static final的Uri类型的类变量,你必须为其指定一个唯一的字符串值,最好的方案是以类的全名称, 如:
public static final Uri CONTENT_URI = Uri.parse( “content://com.google.android.MyContentProvider”);
c. 定义你要返回给客户端的数据列名。如果你正在使用Android数据库,必须为其定义一个叫_id的列,它用来表示每条记录的唯一性。
d. 创建你的数据存储系统。大多数Content Provider使用Android文件系统或SQLite数据库来保持数据,但是你也可以以任何你想要的方式来存储。
e. 如果你要存储字节型数据,比如位图文件等,数据列其实是一个表示实际保存文件的URI字符串,通过它来读取对应的文件数据。处理这种数据类型的Content Provider需要实现一个名为_data的字段,_data字段列出了该文件在Android文件系统上的精确路径。这个字段不仅是供客户端使用,而且也可以供ContentResolver使用。客户端可以调用ContentResolver.openOutputStream()方法来处理该URI指向的文件资源;如果是ContentResolver本身的话,由于其持有的权限比客户端要高,所以它能直接访问该数据文件。
f. 声明public static String型的变量,用于指定要从游标处返回的数据列。
g. 查询返回一个Cursor类型的对象。所有执行写操作的方法如insert(), update() 以及delete()都将被监听。我们可以通过使用ContentResover().notifyChange()方法来通知监听器关于数据更新的信息。
h. 在AndroidMenifest.xml中使用<provider>标签来设置Content Provider。
源码下载
源码下载点击这里~
注
1、本实验实验环境:
操作系统 Windows 10
实验软件 Android Studio 2.2.1
虚拟设备:Nexus_5X
API:23
2、贴代码的时候由于插入代码框的大小问题,代码格式不太严整,望见谅~