Android09-数据存储的三种方式

  • 持久化技术

指将用户产生的数据,存储到手机中,即使手机关机数据也不会丢失。Android有文件存储、ShardPreference存储、数据库存储、或者SD卡存储这几种方式来实现数据持久化。但相对来说前三种方式会相对比较简单和安全。

1. 文件存储

Android最基本的数据存储方式,不对存储内容进行任何处理,所有数据原封不动的保存到文件当中,比较适合用于存储一些简单的文本或二进制数据。

1.1 将数据存储到文件中

使用Context类提供的openFileOutput()方法可以将数据存储到指定文件中。默认存储到/data/data/<packagename>/files/目录下
openFileOutput()参数说明:

  1. 第一个:文件名,文件创建时使用的名称,无需包含路径。
  2. 第二个:文件的操作模式,MODE_PRIVATE: 默认操作模式,当指定的文件名已存在的时候,覆盖已存在的内容。MODE_APPEND: 当指定文件已存在的时候就忘里边追加内容。
  3. 该方法返回一个FileOutputStream对象,得到该对象之后就可以对文件进行保存了。
  • 以下展示了将一段文本内容保存到文件中

    public void save() {
        String data = "我是要保存的文本";
        FileOutputStream out = null;
        BufferdWriter writer = null;
        try {
            out = openFileOutput("data", Context.MODE_PRIVATE);
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.writer(data);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    

1.2 从文件中读取数据

使用Context类提供的OpenFileInput()方法,用于从文件中读取数据。
这个方法的使用只需传入要读取的文件名,然后就会从/data/data/<package name>/files目录下去加载这个文件,返回一个FileInputStream()对象。

  • 示例代码如下:
public String load() {
    FileInputStream in = null;
    BufferedReader reader = null;
    StringBuilder content = new StringBuilder();
    try {
        in = openFieInput("data");
        reader = new BufferedReader(new InputStreamReader(in));
        String line = "";
        while ((line = reader.readLine()) != null) {
            content.append(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e){
                e.printStackTrace();
            }
        }
    }
    return content.toString();
}

2.SharedPreferences存储

SharedPreferences是使用键值对的方式来存储数据的,如果存储的数据是整型,读出来的数据也是整形,所以使用SharedPreferences来进行数据存储要比文件方便很多。

2.1 将数据存储到SharedPreferences中

  1. 获取SharedPreferences对象(Android有三种方式可以得到SharedPreferences对象)
  2. Activity类中的getPreferences()方法
  3. PreferenceManager类中的getDefaultSharedPreferences()方法
  4. Context类中的getSharedPreferences()方法
       pref = PreferenceManager.getDefaultSharedPreferences(this);
        pref = getPreferences(MODE_PRIVATE);
        pref = getSharedPreferences("", MODE_PRIVATE);
  1. 调用SharedPreferences对象的edit()方法。
  2. 向SharedPreferences.Editor对象中添加数据。
  3. 调用apply()方法将添加的数据提交。
Button button = (Button) findViewById(R.id.save_data);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //获得SharedPreferences对象
                SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
                //写如要保存的数据
                editor.putString("name", "Tom");
                editor.putInt("age", 28);
                editor.putBoolean("married", false);
                //调用apply()方法
                editor.apply();
            }
        });

2.2 从SharedPrefrences中读取数据

步骤跟存数据类似
需要注意的是,读取数据不需要调用edit()方法。

Button button1 = (Button)findViewById(R.id.restore_data);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE);
    String name = pref.getString("name", "");
    int age = pref.getInt("age", 0);
    boolean married = pref.getBoolean("married", false);
    Log.d("MainActivity","name is " + name);
    Log.d("MainActivity", "age is" + age);
    Log.d("MainActivity", "married is" + married);
    }
});

3.SQLite数据库存储

使用SQLiteOpenHelper类可以非常简单的对数据库进行创建和升级。

3.1 SQLiteOpenHelper类介绍

  1. SQLiteOpenHelper是一个抽象类,如果要使用它,需要创建一个帮助类去继承它,然后重写onCreate()和onUpgrade()这两个抽象方法。
  2. getReadableDatabase()和getWritableDatabase()是两个非常重要的实例方法,可以创建或打开一个现有的数据库(如果数据库已存在直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。注意:当磁盘空间已满时,使用getReadableDatabase()方法返回的为只读数据库,而getWritableDatabase()则将出现异常

3.2创建数据库

SQLiteOpenHelper中有两个构造方法可供重写,一般使用参数少一点方法即可
第一个参数:Context
第二个参数:数据库名
第三个参数:允许我们在查询数据的时候返回一个自定义的Cursor,一般传null
第四个参数:当前数据库的版本号。可用于对数据库进行操作

class MyDatabaseHelper extends SQLiteOpenHelper {
//定义SQL的建表语句
    public static final String CREATE_BOOK2 = "create table Book("
            + "id integer primary key autoincrement, "
            + "author text, "
            + "price real, "
            + "pages integer, "
            + "name text)";
    private Context mContext;
    //重写构造方法
    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        Toast.makeText(context, "创建", Toast.LENGTH_SHORT).show();
        mContext = context;
    }
    //重写onCreate方法
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK2);
        Toast.makeText(mContext, "创建成功", Toast.LENGTH_SHORT).show();
    }
    //重写onUpgrade()方法
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

3.3 升级数据库

升级数据库:在原有的数据库基础上添加新表。
主要通过SQLiteOpenHelper的构造方法的第四个参数(当前版本号)来实现。当出入的版本好为新版本号时会执行onUpgrade方法,在这个方法里边实现升级数据库的操作。

//重写onUpgrade()方法
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    //删除已存在的表
    db.execSQL("drop table if exists Book");
    db.execSQL("drop table if exists Category");
    //调用onCreate()方法,创建要创建的表
    onCreate(db);
}

3.4 添加数据

SQLiteDatabase中提供了insert()方法,专门用于添加数据,它接受三个参数
第一个参数:表名
第二个参数:用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般这个参数传null即可
第三个参数:一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据。只需要将每个列名以及相应的待添加数据传入即可。

//添加数据
Button addData = (Button) findViewById(R.id.add_data);
        addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                //组装第一条数据
                values.put("name", "The Da Vinci Code");
                values.put("author", "Dan Brown");
                values.put("pages", 454);
                values.put("price", 16.69);
                db.insert("Book", null, values);    //插入第一条数据
                values.clear();
                //组装第二条数据
                values.put("name", "The Lost Symbol");
                values.put("author", "Dan Brown");
                values.put("pages", 510);
                values.put("price", 19.95);
                db.insert("Book", null, values);//插入第二条数据
            }
        });

3.5 更新数据

SQLiteDatabase中提供了update()方法用于对数据进行更新,这个方法接受四个参数
第一个参数:表名,和insert()方法一样
第二个参数:ContentValues对象,把要更新的数据在这里组装进去
第三个参数第四个参数;用于约束更新某一行或某几行数据,不指定的话默认更新所有行

//更新数据
        Button updateData = (Button) findViewById(R.id.update_data);
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("price", 1.99);
                //将名字是The Da Vinci Code的书价格改为1.99
                db.update("Book", values, "name = ?",new String[] {"The da Vinci Code"});
            }
        });

3.6 删除数据

SQLiteDatabase提供了delete()方法用于删除数据,该方法接收三个参数
第一个参数:表名,和其他方法一样
第二个第三个参数是用来约束删除某一行或某几行的,跟update()方法的第三第四个参数类似。

//删除数据
Button deleteData = (Button) findViewById(R.id.delete_data);
deleteData.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
      SQLiteDatabase db = dbHelper.getWritableDatabase();
      //删除Book表中page大于500的数据
      db.delete("Book", "page > ?0", new String[] {"500"});
  }
});

3.7 查询数据

使用SQLiteDatabase提供querry()方法除数据逆行查询。该方法较为复杂,最短的一个方法重载也要传入七个参数,并返回一个Cursor对象

  1. 第一个参数:表名
  2. 第二个参数:用于指定查询哪几列,如果不指定默认查询所有列
  3. 第三第四个参数:用于约束查询某一行或者某几行的数据,不指定则默认查询所有行
  4. 第五个参数:用于指定需要去group by的列,不指定则表示不进行group by操作。
  5. 第六个参数:用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤。
  6. 第七个参数:用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。
  7. 注:querry()方法会返回一个Cursor对象,查询的所有数据都是从这个对象取出
2017-04-11_15-12-06.png
//查询数据
Button querryData = (Button) findViewById(R.id.querry_data);
querryData.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
          SQLiteDatabase db = dbHelper.getWritableDatabase();
          //查询Book表中的所有数据
          Cursor cursor = db.query("Book", null, null, null, null, null, null);
          //调用moveToFirst()方法将数据指针移动到第一行位置
          if (cursor.moveToFirst()) {
              do {
                  //遍历Cursor对象,取出数据并打印
                  String name = cursor.getString(cursor.getColumnIndex("name"));   //getColumnIndex()方法可以获得某一列在表中对应的位置索引
                  String author = cursor.getString(cursor.getColumnIndex("author"));
                  int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                  double price = cursor.getDouble(cursor.getColumnIndex("price"));
                  Log.d("MainActivity", "book name is + " + name);
                  Log.d("MainActivity", "book author is + " + author);
                  Log.d("MainActivity", "book page is + " + pages);
                  Log.d("MainActivity", "book price is + " + price);
              } while (cursor.moveToNext());
          }
          cursor.close();
      }
    });
}

3.8 使用SQL语句操作数据库

Android还可以不借助SQLiteDatabase的情况下进行数据库的增删查改功能,具体使用方法如下

//添加数据的方法如下:
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);

3.9 SQLite使用事务

SQLite的特性是支持事务的,事务的特性可以保证让某一系列的操作要么全部一起完成,要么一个都不完成。
比如有这么一个需求,需要将数据库中的旧书替换成新书,这时候就需要把旧书删除,然后把新书添加进去,但是要保证旧书删除之后新书必须添加成功,这个时候就可以使用事务来实现了。

//替换数据
Button replaceData = (Button) findViewById(R.id.replace_data);
replaceData.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
      SQLiteDatabase db = dbHelper.getWritableDatabase();
      //开启事务
      db.beginTransaction();
      try {
          db.delete("Book", null, null);
          if (true) {
              //在这里手动跑出一个异常,演示当添加不成功时,事务失败
              throw new NullPointerException();
          }
          ContentValues values = new ContentValues();
          values.put("name", "Game of Thrones");
          values.put("author", "me");
          values.put("pages", 750);
          values.put("price", 20.22);
          db.insert("Book", null, values);
          db.setTransactionSuccessful();//事务执行成功
      } catch (Exception e) {
          e.printStackTrace();
      } finally {
          //结束事务
          db.endTransaction();
      }        
  }
});

3.10 升级数据库的最佳写法

在3.3的时候已经有演示了升级数据库的写法,但是那种方法是通过把原有的数据库删除,然后重新创建一个的方式来进行升级的,这样的话原有数据库的信息也会随着升级而丢失,所以有时候这种方法是不可取的。
如果想要进行数据库升级,并保留原有的数据,需要为每个数据库版本号赋予它各自改变的内容,然后在onUpgrade()方法中去执行更新操作。

//重写onUpgrade()方法
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
   //删除已存在的表
//        db.execSQL("drop table if exists Book");
//        db.execSQL("drop table if exists Category");
//        //调用onCreate()方法,创建要创建的表
//        onCreate(db);
   switch (oldVersion) {
       //对旧版本进行判断,执行新版本要更新的内容。这里需要注意,case后面不加break,以保证用户直接从第一版升级至第三版时case1和case2都会执行。
       case 1:
           db.execSQL(CREATE_GATEGORY);
       case 2:
           db.execSQL("alter table Book add colum category_id integer");
       default:
   }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容