Android开发(5):数据存储 持久化技术

个人笔记整理

数据存储 持久化技术

文件存储

  • Context 类中提供了一个openFileOutput() 方法,可以用于将数据存储到指定的文件中。
    • 第一个参数为文件名(默认存储到/data/data/<package name>/files/目录下)
    • 第二个参数是文件的操作模式,主要有两种模式可选,MODE_PRIVATE和MODE_APPEND。其中MODE_PRIVATE是默认的操作模式,表示当指定同样文件名的时候,所写入的内容将会覆盖原文件中的内容,而MODE_APPEND则表示如果该文件已存在,就往文件里面追加内容,不存在就创建新文件。
    • 返回的是一个FileOutputStream 对象
  • Context 类中还提供了一个openFileInput() 方法,用于从文件中读取数据。
    • 只接收一个参数,即要读取的文件名

SD卡文件读写

  1. 判断是否手机插入SD卡,并且程序有读写SD卡的权限。

    Environment.getExternalStorageState().equals(android
            .os.Environment.MEDIA_MOUNTED);
    
    
  2. 获得SD卡目录:getExternalFilesDir(“类型" )

    “类型”参数:可为null或右图所列

    • null:表示sdcard/data/android/包名/files
    • Enviroment.DIRECTORY_DCIM(或“DCIM”):表示sdcard/data/android/包名/files/DCIM

手机插入SD卡,在AVD中通过mksdcard命令来创建。

获得读写SD卡的权限需要在AndroidManifest.xml中添加读写权限。

SharedPreferences存储

SharedPreferences文件是使用XML格式来对数据进行管理的。

将数据存储到SharedPreferences中

获取SharedPreferences对象
  • Context 类中的getSharedPreferences() 方法
    • 第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存放在/data/data/<package name>/shared_prefs/目录下
    • 第二个参数用于指定操作模式,目前只有MODE_PRIVATE这一种模式可选,它是默认的操作模式,和直接传入0效果是相同的,表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写。其他几种操作模式均已被废弃
  • Activity 类中的getPreferences() 方法
    • 它只接收一个操作模式参数。会自动将当前活动的类名作为SharedPreferences的文件名。
  • PreferenceManager 类中的getDefaultSharedPreferences() 方法
    • 这是一个静态方法,它接收一个Context 参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件
使用对象进行存储
  1. 调用SharedPreferences 对象的edit() 方法来获取一个SharedPreferences.Editor 对象。
  2. 向SharedPreferences.Editor 对象中添加数据,比如添加一个布尔型数据就使用putBoolean() 方法,添加一个字符串则使用putString() 方法,以此类推。
  3. 调用apply() 方法将添加的数据提交,从而完成数据存储操作。
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
editor.putString("name", "Tom");
editor.putInt("age", 28);
editor.putBoolean("married", false);
editor.apply();

从SharedPreferences中读取数据

获得SharedPreferences对象后调用相应的getString() 、getInt() 和getBoolean()方法来获取

SQLite数据库存储

创建数据库

提供了一个SQLiteOpenHelper帮助类,借助这个类就可以非常简单地对数据库进行创建和升级。SQLiteOpenHelper是一个抽象类

SQLiteOpenHelper中有两个抽象方法,分别是onCreate() 和onUpgrade() ,我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑。

SQLiteOpenHelper中还有两个非常重要的实例方法:getReadableDatabase() 和getWritableDatabase() 。这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。

不同的是,当数据库不可写入的时候getReadableDatabase() 方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase() 方法则将出现异常。

SQLiteOpenHelper构造方法
  • 较少参数构造方法接收4个参数
    • 第一个参数是Context
    • 第二个参数是数据库名,创建数据库时使用的就是这里指定的名称。
    • 第三个参数允许我们在查询数据的时候返回一个自定义的Cursor,一般都是传入null
    • 第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。
    • 构建出SQLiteOpenHelper的实例之后,再调用它的getReadableDatabase() 或getWritableDatabase() 方法就能够创建数据库了,数据库文件会存放在/data/data/ <package name>/databases/目录下。
    • 此时,重写的onCreate() 方法也会得到执行,所以通常会在这里去处理一些创建表的逻辑。

SQLite不像其他的数据库拥有众多繁杂的数据类型,它的数据类型很简单,integer 表示整型,real 表示浮点型,text 表示文本类型,blob 表示二进制类

使用SQLiteOpenHelper实现类

直接new 实现类,当数据库不存在时,onCreate()会被执行;当构造方法版本号大于原先版本号时,onUpgrade()会被执行

获取数据库对象

调用SQLiteOpenHelper的getReadableDatabase() 或getWritableDatabase() 方法是可以用于创建和升级数据库的。

这两个方法还都会返回一个QLiteDatabase 对象,借助这个对象就可以对数据进行CRUD操作了。

或者使用SQLiteDatabase.openOrCreateDatabse(dbName,null)来获取

添加数据

  • SQLiteDatabase 中提供了一个insert() 方法,这个方法就是专门用于添加数据的。
    • 第一个参数是表名,我们希望向哪张表里添加数据,这里就传入该表的名字。
    • 第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL ,一般我们用不到这个功能,直接传入null 即可。
    • 第三个参数是一个ContentValues 对象,它提供了一系列的put() 方法重载,用于向ContentValues 中添加数据,只需要将表中的每个列名以及相应的待
      添加数据传入即可。

更新数据

  • SQLiteDatabase 中也提供了一个非常好用的update() 方法,用于对数据进行更新,这个方法接收4个参数
    • 第一个参数和insert() 方法一样,也是表名,在这里指定去更新哪张表里的数据。
    • 第二个参数是ContentValues 对象,要把更新数据在这里组装进去。
    • 第三、第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行。第三参数相当于where部分,第四参数为new String[]为第三参数的占位符提供内容

删除数据

  • SQLiteDatabase 中提供了一个delete() 方法,专门用于删除数据,这个方法接收3个参数
  • 第一个参数仍然是表名
  • 第二、第三个参数又是用于约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。

查询数据

  • SQLiteDatabase中还提供了一个query() 方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要传入7个参数。
    • 第一个参数,表名,表示我们希望从哪张表中查询数据。
    • 第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列。
    • 第三、第四个参数用于约束查询某一行或某几行的数据,不指定则默认查询所有行的数据。
    • 第五个参数用于指定需要去group by的列,不指定则表示不对查询结果进行groupby操作。
    • 第六个参数用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤。
    • 第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。
query()方法参数 对应SQL部分 描述
table from table_name 指定查询的表名
columns select column1, column2 指定查询的列名
selection where column = value 指定where 的约束条件
selectionArgs 为where 中的占位符提供具体的值
groupBy group by column 指定需要group by 的列
having having column = value 对group by 后的结果进一步约束
orderBy order by column1, column2 指定查询结果的排序方式

调用query() 方法后会返回一个Cursor 对象,查询到的所有数据都将从这个对象中取出。

// 查询Book表中所有的数据
Cursor cursor = db.query("Book", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
    do {
        // 遍历Cursor对象,取出数据并打印
        String name = cursor.getString(cursor.getColumnIndex
            ("name"));
        String author = cursor.getString(cursor.getColumnIndex
            ("author"));
        int pages = cursor.getInt(cursor.getColumnIndex("pages"));
        double price = cur
            ("price"));
    } while (cursor.moveToNext());
}
cursor.close();
Cursor类方法
方法名称 方法描述
getCount() 获得总的数据项数
isFirst() 判断是否第一条记录
isLast() 判断是否最后一条记录
moveToFirst() 移动到第一条记录
moveToLast() 移动到最后一条记录
move(int offset) 移动到指定记录
moveToNext() 移动到下一条记录
moveToPrevious() 移动到上一条记录
getColumnIndexOrThrow(String columnName) 根据列名称获得列索引
getInt(int columnIndex) 获得指定列索引的int类型值
getString(int columnIndex) 获得指定列缩影的String类型值

使用SQL操作数据库

  • 添加数据的方法如下:

    db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
                new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" });
    db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
                new String[] { "The Lost Symbol", "Dan Brown", "510", "19.95" });
    
  • 更新数据的方法如下:

    db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99", "The Da Vinci Code" });
    
  • 删除数据的方法如下:

    db.execSQL("delete from Book where pages > ?", new String[] { "500" });
    
  • 查询数据的方法如下:

    db.rawQuery("select * from Book", null);
    

可以看到,除了查询数据的时候调用的是SQLiteDatabase的rawQuery() 方法,其他的操作都是调用的execSQL() 方法。

使用LitePal操作数据库

LitePal是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式,并将我们平时开发最常用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成各种建表和増删改查的操作。LitePal的项目主页上也有详细的使用文档,地址是:guolindev/LitePal: An Android library that makes developers use SQLite database extremely easy. (github.com)

配置LitePal

1. 添加LitePal依赖

就是编辑app/build.gradle文件,在dependencies闭包中添加如下内容

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.2.0'
    testCompile 'junit:junit:4.12'
    compile 'org.litepal.android:core:1.4.1'
}
2. 配置litepal.xml文件

右击app/src/main目录→NewDirectory,创建一个assets目录,然后在assets目录下再新建一个litepal.xml文件,接着编辑litepal.xml文件中的内容

<?xml version="1.0" encoding="utf-8"?>
<litepal>
    <dbname value="BookStore" ></dbname>
    <version value="1" ></version>
    <list>
    </list>
</litepal>
  • <dbname>标签用于指定数据库名
  • <version> 标签用于指定数据库版本号
  • <list>标签用于指定所有的映射模型
3. 配置LitePalApplication
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.litepaltest">
    <application
        android:name="org.litepal.LitePalApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...
    </application>
</manifest>

将项目的application配置为org.litepal.LitePalApplication,这样才能让LitePal的所有功能都可以正常工作

创建和升级数据库

创建Book类,定义好相关属性(表的列名)

litepal.xml<list>标签内添加<mapping class="com.example.litepaltest.Book"></mapping>

使用LitePal添加数据

LitePal进行表管理操作时不需要模型类有任何的继承结构,但是进行CRUD操作时就不行了,必须要让 相关表类 继承自DataSupport 类才行,如Book表需要让Book类继承DataSupport类,然后调用.save()方法将数据存入数据库

使用LitePal更新数据

对于LitePal来说,对象是否已存储就是根据调用model.isSaved() 方法的结果来判断的,返回true 就表示已存储,返回false 就表示未存储。

方法一:对已存储的对象,重新调用.save()方法进行更新

方法二:更新从数据库中查到的对象

{
    Book book = new Book();
    book.setPrice(14.95);
    book.setPress("Anchor");
    book.updateAll("name = ? and author = ?", "The Lost Symbol", "Dan
        Brown");
}

**想把一个字段的值更新成默认值时,是不可以使用上面的方式来set 数据的。**将为数据更新成默认值的操作,LitePal统一提供了一个setToDefault() 方法,然后传入相应的列名就可以实现了。

Book book = new Book();
book.setToDefault("pages");
book.updateAll();

使用LitePal删除数据

  • 方法一

    调用过save() 方法的对象,或者是通过LitePal提供的查询API查出来的对象,都是可以直接使用delete() 方法来删除数据的。

  • 方法二

    DataSupport.deleteAll(Book.class, "price < ?", "15");
    

    这里调用了DataSupport.deleteAll() 方法来删除数据,其中deleteAll() 方法的第一个参数用于指定删除哪张表中的数据,Book.class就意味着删除Book表中的数据,后面的参数用于指定约束条件。

使用LitePal查询数据

@TODO

上一篇:python3.6 连接mysql数据库


下一篇:js+css实现骰子的随机转动