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

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

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

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帮助类,借助这个类我们可以非常简单地对数据库进行创建和升级了。
        1. SQLiteOpenHelper是一个抽象类,使用它时我们自己的类必须实现其两个抽象方法onCreate(SQLiteDatabase db)方法onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)方法,分别实现创建和升级数据库的逻辑。
           public class CoolWeatherOpenHelper extends SQLiteOpenHelper {
          
               /**
          * Province表建表语句
          */
          public static final String CREATE_PROVINCE = "create table Province ("
          + "id integer primary key autoincrement, "
          + "province_name text, "
          + "province_code text)";
          /**
          * City表建表语句
          */
          public static final String CREATE_CITY = "create table City ("
          + "id integer primary key autoincrement, "
          + "city_name text, "
          + "city_code text, "
          + "province_id integer)";
          /**
          * County表建表语句
          */
          public static final String CREATE_COUNTY = "create table County ("
          + "id integer primary key autoincrement, "
          + "county_name text, "
          + "county_code text, "
          + "city_id integer)"; public CoolWeatherOpenHelper(Context context, String name, CursorFactory factory,
          int version) {
          super(context, name, factory, version);
          } @Override
          public void onCreate(SQLiteDatabase db) {
          db.execSQL(CREATE_PROVINCE); // 创建Province表
          db.execSQL(CREATE_CITY); // 创建City表
          db.execSQL(CREATE_COUNTY); // 创建County表
          } @Override
          public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
          //调用db.execSQL()方法执行数据库的更新和升级, onCreate(db) ; //重新创建数据库
          }
          }
    1. 然后利用其构造方法public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version)来创建对象SQLiteOpenHelper对象
      • 其中第一个参数是context,必须要有它才能对数据库进行操作;
      • 第二个参数是数据库名,创建数据库时使用的就是这里指定的名称;
      • 第三个参数是允许我们在查询数据时候返回一个自定义的Cursor,一般都传入null;
      • 第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。当前版本号和之前的版本号不同时,onUpgrade()方法就会得到执行
  • 创建数据库:得到SQLiteOpenHelper对象之后,我们可以利用其public SQLiteDatabase getReadableDatabase() 方法或public SQLiteDatabase getWritableDatabase()方法来创建我们的数据库,并且获得了一个数据库对象,后面我们可以利用这个数据库对象对我们创建的数据库进行各种增删查改操作。
     public class CoolWeatherDB {
    
         // 数据库名
    public static final String DB_NAME = "cool_weather" ;
    // 数据库版本
    public static final int VERSION = 1; private static CoolWeatherDB coolWeatherDB; private SQLiteDatabase db; // 将构造方法私有化
    private CoolWeatherDB(Context context) {
    // 创建帮助类对象
    CoolWeatherOpenHelper dbHelper = new CoolWeatherOpenHelper(context,DB_NAME, null, VERSION);
    // 创建数据库
    db = dbHelper.getWritableDatabase();
    } // 获取CoolWeatherDB的实例。
    public synchronized static CoolWeatherDB getInstance(Context context) {
    if (coolWeatherDB == null) {
    coolWeatherDB = new CoolWeatherDB(context);
    }
    return coolWeatherDB;
    }
    }
  • 对数据库的操作CRUD,即增删查改。这里我们利用上面得到的SQLiteDatabase数据库对象提供的各种方法来进行相关的操作。
    • Create:创建、添加。利用SQLiteDatabase数据库对象insert(String table, String nullColumnHack, ContentValues values)方法实现添加数据:第一个参数是数据库中的表名,第二个一般直接传入null即可,第三个参数是一个ContentValues对象,它提供了一系列的put()方法的重载,用于向ContentValues对象中添加数据,只需将表中的每个列名以及相应的待添加的数据传入即可。ContentValues的底层是一个HashMap集合,键即为对应表中的各列的名称,值是我们传入的数据。
       /**
      * 将County实例存储到数据库。
      */
      public void saveCounty(County county) {
      if (county != null) {
      ContentValues values = new ContentValues();
      values.put("county_name", county.getCountyName());
      values.put("county_code", county.getCountyCode());
      values.put("city_id", county.getCityId());
      db.insert("County", null, values);
      }
      }
    • Update:更新。利用SQLiteDatabase数据库对象提供的update(String table, ContentValues values, String whereClause, String[] whereArgs)方法实现更新数据库中的数据。第一个参数就是表名,第二个是要更新的数据组装成的ContentValues对象,第三个和第四个参数用于约束更新某一行或某几行的数据,不指定的话是默认更新所有行。
       /**
      * 对存储到数据库的County实例进行更新。
      */
      public void updateCounty(County county) {
      if (county != null) {
      ContentValues values = new ContentValues();
      values.put("county_name", "beijing");
      values.put("county_code", "001");
      db.update("County", values, "city_id = ?", new String [] {"010201"}) ;
      }
      }

      第三个参数对应的是SQL语句中的where部分,表示去更新所有city_id = ?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串组为第三个参数中的每个站位符指定相应的内容。

    • Delete:删除。利用SQLiteDatabase数据库对象提供的delete(String table, String whereClause, String[] whereArgs)方法实现删除数据库中的数据。各项参数前面都已经介绍过,就不一一介绍了。
       /**
      * 删除存储到数据库中的某些County实例。
      */
      public void deleteCounty(County county) {
      db.delete("County", "county_name = ? ", new String [] {"beijing"}) ; }
    • 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 指定查询结果的排序方式
       /**
      * 从数据库读取某城市下所有的县信息。
      */
      public List<County> loadCounties(int cityId) {
      List<County> list = new ArrayList<County>();
      Cursor cursor = db.query("County", null, "city_id = ?",
      new String[] { String.valueOf(cityId) }, null, null, null);
      if (cursor.moveToFirst()) {
      do {
      County county = new County();
      county.setId(cursor.getInt(cursor.getColumnIndex("id")));
      county.setCountyName(cursor.getString(cursor
      .getColumnIndex("county_name")));
      county.setCountyCode(cursor.getString(cursor
      .getColumnIndex("county_code")));
      county.setCityId(cityId);
      list.add(county);
      } while (cursor.moveToNext());
      }
      return list;
      }
  • 对数据的操作还可以直接使用SQL来操作数据库
     public void operateSQL(){
    //插入
    db.execSQL("insert into Country (country_name, country_code, city_id) values (?,?,?)",new String [] {"beijing","001","111"});
    //更新
    db.execSQL("update Country set country_name = ? where country_code = ?", new String [] {"beijing","002"});
    //删除
    db.execSQL("delete from Country where contry_code = ?", new String [] {"002"});
    //查询,注意:查询是用的rawQuery()方法,不再是execSQL()方法
    db.rawQuery("select * from Country", null) ;
    }

5、数据库的事务特性

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

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