Android基础总结(5)——数据存储,持久化技术

  • 瞬时数据:指那些存储在内存当中,有可能会因为程序广播或其他原因导致内存被回收而丢失的数据。
  • 数据持久化:指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机或电脑关机的情况下,这些数据仍然不丢失。

  保存在内存中的数据是瞬时数据,保存在手机设备中的数据是处于持久状态的,持久化技术则是提供了一种机制可以让数据在瞬时状态和持久状态之间进行切换。

1、持久化技术有哪些

  Android系统中主要提供了三种方式用于简单地实现数据持久化功能:

  1. 文件存储:是Android中最基本的一种数据存储方式。不对存储内容做任何的格式化处理,所有数据都是原封不动地保存到文件中。因此,这种方式比较适合存储一些文本数据或二进制数据。如果你想使用文件存储的方式来保存比较复杂的文本数据,则需要自己定义一套格式规范,这样方便之后将数据从文件中重新解析出来。
  2. SharedPreferences存储:使用键值对的方式存储数据。也就是说当保存一条数据时,需要给这条数据提供一个对应的键,这样在读取数据时就可以通过这个键把相应的值取出来。此外,SharedPreference还支持多种不同数据类型的存储。
  3. 数据库存储:SQLite是一款轻量级的关系型数据库,运算速度快,占用资源少,通常只需要几百k就可以了。不仅支持标准的SQL语法,还遵循了数据库的ACID事务。

  此外,我们还可以通过将数据存储到SD卡中。不过使用上述三种方式会更加简单一些,而且也更加安全。

2、文件存储

  • 将数据保存到文件:利用Context类中的openFileOutput(String fileName, int mode)方法可以将数据存储到指定的文件中,该方法返回一个FileOutputStream对象,得到该对象之后,使用Java流的方式将内容写入文件中。该方法接收两个参数:
    • 第一个参数是文件名,在文件创建的时候使用的就是这个文件名,注意,这里指定的文件名不可以包含路径,因为所有的文件都默认存储到/data/data/<packageName>/files/目录下的。
    • 第二个参数是文件的操作模式,主要有两种可选模式,MODE_PRIVATE和MODE_APPEND,其中MODE_PRIVATE是默认操作模式,表示当指定同样文件名的时候,所写入的内容会覆盖原文件中的内容,而MODE_APPEND则表示如果该文件已经存在,则将新内容追加到原文件内容的后面。此外,还有两种可选模式,MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE,分别表示允许其他应用程序对我们程序中的文件进行读写操作,不过由于这两种操作模式过于危险,很容易引起安全性漏洞,已经被废弃。
  • 从文件中读取数据:同样利用Context类中的openFileInput(String fileName)方法可以将数据存储到指定的文件中,该方法返回一个FileInputStream对象,得到该对象之后,使用Java流的方式将文件中的内容读出。该方法只接收一个参数,就是文件名,然后系统会自动从/data/data/<packageName>/files/目录下去加载这个文件,并返回一个FileInputStream对象。 

文件存储的方式并不适用于保存一些较为复杂的文本数据。 

3、SharedPreferences存储

  • 通过SharedPreferences存储数据:SharedPreference存储的数据都以xml格式存储,并且都自动保存到/data/data/<packageName>/shared_prefs/目录下。主要步骤如下:
  1. 首先需要获取到SharedPreferences对象。Android中主要提供了三种方法用于得到SharedPreferences对象:
    1. Context类中的getSharedPreferences(String name, int mode)方法:此方法接收两个参数,第一个参数用于指定SharedPreference文件的名称,如果指定的文件不存在则会创建一个。第二个参数指定操作模式,主要有两种模式可选:MODE_PRIVATEMODE_MULTI_PROCESS,MODE_PRIVATE仍然是默认选项,表示只有当前应用程序可以对这个SharedPreference文件进行读写;MODE_MULTI_PROCESS则一般是用于会有多个进程对同一个SharedPreferences进行读写的情况。类似地,MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE也已经被废弃使用了。
    2. Activity类中的getPreferences(int mode)方法:这个方法和第一个很相似,它只接收一个参数,就是操作模式。因为使用该方法会自动将当前活动的类名作为SharedPreferences文件的文件名。
    3. PreferenceManager类中的getDefaultSharedPreferences(Context context)方法:这是一个静态方法,它接收一个context参数,并自动当前应用程序的包名作为前缀名来命名SharedPreferences文件。
  2. 得到SharedPreferences对象之后,我们就可以开始往SharedPreference文件中写数据了,需要按照以下三个步骤来进行:
    1. 调用SharedPreferences对象的edit()方法获取一个SharedPreferences.Editor对象editor
    2. 向上一步获取的SharedPreferences.Editor对象中添加数据,比如添加一个boolean型数据就使用editor.putBoolean(String key, boolean value)方法,添加字符串就使用editor.putString(String key, String value)方法,以此类推。
    3. 最后所有数据添加完成之后调用editor.commit()方法将调价的数据进行提交。如果不进行commit(),则之前添加的数据将不会修改到文件中去
  • 从SharedPreferences中读取数据:从SharedPreferences类中我们可以看到有一系列get方法,显然,我们从SharedPreferences中读取数据是通过这些get方法来完成的。值得注意的是:这些get方法均接收两个参数,第一个是String类型的key,传入存储数据时使用的键就可以得到相应的值,第二个则是我们参数是默认值,即表示当我们传入的键找不到对应的值时,会以什么样的默认值进行返回。具体步骤如下:
    1. 获取SharedPreferences对象
    2. 调用SharedPreferences对象的get方法获取数据,例如读取boolean类型数据就使用getBoolean(String key, boolean defValue) 方法进行获取

4、SQLite数据库存储

  • 创建SQLiteOpenHelper对象:Android为了让我们能够更加方便地管理数据库,专门提供了一个SQLiteOpenHelper帮助类,借助这个类我们可以非常简单地对数据库进行创建和升级了。
  • SQLiteOpenHelper是一个抽象类,使用它时我们自己的类必须实现其两个抽象方法onCreate(SQLiteDatabase db)方法onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)方法,分别实现创建和升级数据库的逻辑。
 1 public class CoolWeatherOpenHelper extends SQLiteOpenHelper {
 2     
 3     /**
 4      *  Province表建表语句
 5      */
 6     public static final String CREATE_PROVINCE = "create table Province ("
 7                 + "id integer primary key autoincrement, " 
 8                 + "province_name text, "
 9                 + "province_code text)";
10     /**
11      *  City表建表语句
12      */
13     public static final String CREATE_CITY = "create table City ("
14                 + "id integer primary key autoincrement, " 
15                 + "city_name text, " 
16                 + "city_code text, " 
17                 + "province_id integer)";
18     /**
19      *  County表建表语句
20      */
21     public static final String CREATE_COUNTY = "create table County ("
22                 + "id integer primary key autoincrement, " 
23                 + "county_name text, " 
24                 + "county_code text, " 
25                 + "city_id integer)";
26 
27     public CoolWeatherOpenHelper(Context context, String name, CursorFactory factory,
28             int version) {
29         super(context, name, factory, version);
30     }
31 
32     @Override
33     public void onCreate(SQLiteDatabase db) {
34         db.execSQL(CREATE_PROVINCE);  // 创建Province表
35         db.execSQL(CREATE_CITY);  // 创建City表
36         db.execSQL(CREATE_COUNTY);  // 创建County表
37     }
38 
39     @Override
40     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
41         //调用db.execSQL()方法执行数据库的更新和升级,
42         
43         onCreate(db) ;  //重新创建数据库
44     }
45 }
  1. 然后利用其构造方法public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version)来创建对象SQLiteOpenHelper对象
    • 其中第一个参数是context,必须要有它才能对数据库进行操作;
    • 第二个参数是数据库名,创建数据库时使用的就是这里指定的名称;
    • 第三个参数是允许我们在查询数据时候返回一个自定义的Cursor,一般都传入null;
    • 第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。当前版本号和之前的版本号不同时,onUpgrade()方法就会得到执行
  • 创建数据库:得到SQLiteOpenHelper对象之后,我们可以利用其public SQLiteDatabase getReadableDatabase() 方法或public SQLiteDatabase getWritableDatabase()方法来创建我们的数据库,并且获得了一个数据库对象,后面我们可以利用这个数据库对象对我们创建的数据库进行各种增删查改操作。
1 public class CoolWeatherDB {
 2 
 3     // 数据库名
 4     public static final String DB_NAME = "cool_weather" ;
 6     // 数据库版本
 7     public static final int VERSION = 1;
 8 
 9     private static CoolWeatherDB coolWeatherDB;
10 
11     private SQLiteDatabase db;
12 
13     // 将构造方法私有化
14     private CoolWeatherDB(Context context) {
15         // 创建帮助类对象
16         CoolWeatherOpenHelper dbHelper = new CoolWeatherOpenHelper(context,DB_NAME, null, VERSION);
18          // 创建数据库
19         db = dbHelper.getWritableDatabase();
20     }
21 
22     // 获取CoolWeatherDB的实例。
23     public synchronized static CoolWeatherDB getInstance(Context context) {
24         if (coolWeatherDB == null) {
25             coolWeatherDB = new CoolWeatherDB(context);
26         }
27         return coolWeatherDB;
28     }    
29 } 
  • 对数据库的操作CRUD,即增删查改。这里我们利用上面得到的SQLiteDatabase数据库对象提供的各种方法来进行相关的操作。
    • Create:创建、添加。利用SQLiteDatabase数据库对象insert(String table, String nullColumnHack, ContentValues values)方法实现添加数据:第一个参数是数据库中的表名,第二个一般直接传入null即可,第三个参数是一个ContentValues对象,它提供了一系列的put()方法的重载,用于向ContentValues对象中添加数据,只需将表中的每个列名以及相应的待添加的数据传入即可。ContentValues的底层是一个HashMap集合,键即为对应表中的各列的名称,值是我们传入的数据。
 1 /**
 2  * 将County实例存储到数据库。
 3  */
 4 public void saveCounty(County county) {
 5     if (county != null) {
 6         ContentValues values = new ContentValues();
 7         values.put("county_name", county.getCountyName());
 8         values.put("county_code", county.getCountyCode());
 9         values.put("city_id", county.getCityId());
10         db.insert("County", null, values);
11     }
12 }
  • Update:更新。利用SQLiteDatabase数据库对象提供的update(String table, ContentValues values, String whereClause, String[] whereArgs)方法实现更新数据库中的数据。第一个参数就是表名,第二个是要更新的数据组装成的ContentValues对象,第三个和第四个参数用于约束更新某一行或某几行的数据,不指定的话是默认更新所有行。
 1 /**
 2  * 对存储到数据库的County实例进行更新。
 3  */
 4 public void updateCounty(County county) {
 5     if (county != null) {
 6         ContentValues values = new ContentValues();
 7         values.put("county_name", "beijing");
 8         values.put("county_code", "001");
 9         db.update("County", values, "city_id = ?", new String [] {"010201"}) ;
10     }
11 }
  • 第三个参数对应的是SQL语句中的where部分,表示去更新所有city_id = ?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串组为第三个参数中的每个站位符指定相应的内容。
  • Delete:删除。利用SQLiteDatabase数据库对象提供的delete(String table, String whereClause, String[] whereArgs)方法实现删除数据库中的数据。各项参数前面都已经介绍过,就不一一介绍了。
 1 /**
 2  * 删除存储到数据库中的某些County实例。
 3  */
 4 public void deleteCounty(County county) {
 5  6         db.delete("County", "county_name = ? ", new String [] {"beijing"}) ; 7 }
  • Retrieve:查询。利用SQLiteDatabase数据库对象提供的public Cursor query(String table, String[] columns, String selection,String[] selectionArgs, String groupBy, String having,String orderBy)方法实现查询数据库中的数据。该方法返回一个Cursor游标对象,查询到的所有数据都将从这个对象中取出。

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

指定查询结果的排序方式

 1 /**
 2  * 从数据库读取某城市下所有的县信息。
 3  */
 4 public List<County> loadCounties(int cityId) {
 5     List<County> list = new ArrayList<County>();
 6     Cursor cursor = db.query("County", null, "city_id = ?",
 7             new String[] { String.valueOf(cityId) }, null, null, null);
 8     if (cursor.moveToFirst()) {
 9         do {
10             County county = new County();
11             county.setId(cursor.getInt(cursor.getColumnIndex("id")));
12             county.setCountyName(cursor.getString(cursor
13                     .getColumnIndex("county_name")));
14             county.setCountyCode(cursor.getString(cursor
15                     .getColumnIndex("county_code")));
16             county.setCityId(cityId);
17             list.add(county);
18         } while (cursor.moveToNext());
19     }
20     return list;
21 }
  • 对数据的操作还可以直接使用SQL来操作数据库
 1 public void operateSQL(){
 2     //插入
 3     db.execSQL("insert into Country (country_name, country_code, city_id) values (?,?,?)",new String [] {"beijing","001","111"});
 4     //更新
 5     db.execSQL("update Country set country_name = ? where country_code = ?", new String [] {"beijing","002"});
 6     //删除
 7     db.execSQL("delete from Country where contry_code = ?", new String [] {"002"});
 8     //查询,注意:查询是用的rawQuery()方法,不再是execSQL()方法
 9     db.rawQuery("select * from Country", null) ;    
10 }

5、数据库的事务特性

  •  数据库的事务特性就是 ACID.
    1. Atomicity:原子性
    2. Consistancy:一致性
    3. Isolation:隔离性
    4. Durabolity:持久性
  • SQLite是支持事务特性的,事务特性可以保证让某一系列的操作要么全部完成,要么一个都不完成。其标准用法是利用SQLiteDatabase数据库对象提供的public void beginTransaction()方法和public void endTransaction()方法进行开启和结束一个事务,然后该事物中的所有关于数据库的操作就都在开启和结束中间完成。此外, public void setTransactionSuccessful()方法表示事务已经执行成功。
 1 public void operateSQL() {
 2     db.beginTransaction() ;
 3     try {
 4         // 插入
 5         db.execSQL("insert into Country (country_name, country_code, city_id) values (?,?,?)",
 6                 new String[] { "beijing", "001", "111" });
 7         // 更新
 8         db.execSQL("update Country set country_name = ? where country_code = ?", new String[] { "beijing", "002" });
 9         // 删除
10         db.execSQL("delete from Country where contry_code = ?", new String[] { "002" });
11         // 查询,注意:查询是用的rawQuery()方法,不再是execSQL()方法
12         db.rawQuery("select * from Country", null) ;
13         //如果在endTransaction()执行之前没有执行setTransactionSuccessful()方法的话表明事务执行失败,
14         //所以所有操作一条都不完成
15         db.setTransactionSuccessful() ;
16     } catch (Exception e) {
17         // TODO: handle exception
18     }finally{
19         db.endTransaction() ;
20     }    
21 }

如果在endTransaction()执行之前没有执行setTransactionSuccessful()方法的话表明事务执行失败,所以所有操作一条都不完成。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java呓语

DataBinding·常用注解说明

Observable接口提供给开发者添加/移除监听者的机制。为了使开发更便捷,我们创建了BaseObservable类,它已经实现了Observable接口中的...

14640
来自专栏IT技术精选文摘

ZooKeeper 分布式锁实现

17920
来自专栏菩提树下的杨过

jboss EAP 6.2+ 通过代码控制JNDI数据源

通过Jboss提供的API,可以操控JBoss,效果跟在管理控制台手动操作完全一样,下面是示例代码: 一、pom.xml添加依赖项 <dependency> ...

22060
来自专栏MasiMaro 的技术博文

ATL模板库中的OLEDB与ADO

上次将OLEDB的所有内容基本上都说完了,从之前的示例上来看OLEDB中有许多变量的定义,什么结果集对象、session对象、命令对象,还有各种缓冲等等,总体上...

15120
来自专栏Android知识点总结

3-AIV--使用ContentProvider获得所有图片路径

19920
来自专栏Java学习网

Java Web Response对象的27个方法及状态码

response表示HttpServletResponse对象,主要将JSP容器处理后的结果传回到客户端。 ? 网络配图 1、void addCookie(...

50670
来自专栏向治洪

Android ClassLoader详解

我们知道不管是插件化还是组件化,都是基于系统的ClassLoader来设计的。只不过Android平台上虚拟机运行的是Dex字节码,一种对class文件优化的产...

332100
来自专栏码匠的流水账

聊聊spring cloud gateway的ForwardedHeadersFilter

本文主要研究一下spring cloud gateway的ForwardedHeadersFilter

10320
来自专栏Python研发

Django之Model世界

django为使用一种新的方式,即:关系对象映射(Object Relational Mapping,简称ORM)

13820
来自专栏青蛙要fly的专栏

项目需求讨论-摆脱EditText内容规则的枯燥判断

      大家好,又到了新的一期的项目需求讨论。我想大家在开发APP,肯定会有很多需要填入EditText内容的界面,比如注册界面,修改密码界面。这些界面都会...

7910

扫码关注云+社区

领取腾讯云代金券