11、数据存储
将数据存储到文件中
openFileOutput ()方法,可以用于将数据存储到指定的文件中。
这个方法接收两个参数,第一个参数是文件名,在文件创建的时候使用的就是这个名称,注意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data/<packagename>/files/ 目 录 下 的 。
第 二 个 参 数 是 文 件 的 操 作 模 式 , 主 要 有 两 种 模 式 可 选 ,MODE_PRIVATE 和 MODE_APPEND
MODE_PRIVATE 是默认的操作模式,
表示当指定同样文件名的时候,所写入的内容将会覆盖原文件中的内容,
MODE_APPEND
则表示如果该文件已存在就往文件里面追加内容,不存在就创建新文件。
public void save() { String data = "Data to save"; FileOutputStream out = null; BufferedWriter writer = null; try { out = openFileOutput("data", Context.MODE_PRIVATE); writer = new BufferedWriter(new OutputStreamWriter(out)); writer.write(data); } catch (IOException e) { e.printStackTrace(); } finally { try { if (writer != null) { writer.close(); } } catch (IOException e) { e.printStackTrace(); } } }
从文件中读取数据
openFileOutput()只接收一个参数,即要读取的文件名,然后系统会自动到/data/data/<package name>/files/目录下去加载这个文件,并返回一个FileInputStream 对象,得到了这个对象之后再通过 Java 流的方式就可以将数据读取出来了。
public String load() { FileInputStream in = null; BufferedReader reader = null; StringBuilder content = new StringBuilder(); try { in = openFileInput("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(); }
【SharedPreferences 存储】
使用键值对的方式来存储数据
获取到SharedPreferences对象
三种方式:
1、Context 类中的 getSharedPreferences()方法
此方法接收两个参数,第一个参数用于指定 SharedPreferences 文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences 文件都是存放在/data/data/<packagename>/shared_prefs/目录下的。
第二个参数用于指定操作模式,主要有两种模式可以选择,MODE_PRIVATE 和 MODE_MULTI_PROCESS。
MODE_PRIVATE 仍然是默认的操作模式,和直接传入 0 效果是相同的,表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写。
MODE_MULTI_PROCESS 则一般是用于会有多个进程中对同一个 SharedPreferences 文件进行读写的情况。
2、Activity 类中的 getPreferences()方法
这个方法和 Context 中的 getSharedPreferences()方法很相似,不过它只接收一个操作模式参数,因为使用这个方法时会自动将当前活动的类名作为 SharedPreferences 的文件名。
3、PreferenceManager 类中的 getDefaultSharedPreferences()方法
这是一个静态方法,它接收一个 Context 参数,并自动使用当前应用程序的包名作为前缀来命名 SharedPreferences 文件。
向 SharedPreferences 文件中存储数据了,
主要可以分为三步实现:
1. 调用 SharedPreferences 对象的 edit()方法来获取一个 SharedPreferences.Editor 对象。
2. 向 SharedPreferences.Editor 对象中添加数据,
比如添加一个布尔型数据就使用putBoolean 方法,添加一个字符串则使用 putString()方法,以此类推。
3. 调用 commit()方法将添加的数据提交,从而完成数据存储操作
SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit(); editor.putString("name", "Tom"); editor.putInt("age", 28); editor.putBoolean("married", false); editor.commit();
生成了一个 data.xml文件
从SharedPreferences 中读取数据
SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE); String name = pref.getString("name", ""); int age = pref.getInt("age", 0); boolean married = pref.getBoolean("married", false);
【SQLite 数据库存储】
Android 系统内置了数据库的!
SQLite 是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百 K 的内存就足够了,因而特别适合在移动设备上使用。
SQLite不仅支持标准的 SQL 语法,还遵循了数据库的 ACID 事务
创建数据库
integer 表示整型,real 表示浮点型,text 表示文本类型,blob 表示二进制类型
SQLiteOpenHelper 帮助类,借助这个类就可以非常简单地对数据库进行创建和升级
SQLiteOpenHelper 是一个抽象类,SQLiteOpenHelper 中有两个抽象方法,分别是onCreate()和 onUpgrade()
SQLiteOpenHelper 中 还 有 两 个 非 常 重 要 的 实 例 方 法 , getReadableDatabase() 和getWritableDatabase()。
这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。
不同的是,当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而 getWritableDatabase()方法则将出现异常。
构造方法中接收四个参数,
第一个参数是 Context,这个没什么好说的,必须要有它才能对数据库进行操作。
第二个参数是数据库名,创建数据库时使用的就是这里指定的名称。
第三个参数允许我们在查询数据的时候返回一个自定义的 Cursor,一般都是传入 null。
第 四 个 参 数 表 示 当 前 数 据 库 的 版 本 号 , 可 用 于 对 数 据 库 进 行 升 级 操 作 。
数据库文件会存放在/data/data/<package name>/databases/目录下
public class MyDatabaseHelper extends SQLiteOpenHelper { public static final String CREATE_BOOK = "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, CursorFactory factory, int version) { super(context, name, factory, version); mContext = context; } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } } //MainActivity: dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1); Button createDatabase = (Button) findViewById(R.id.create_database); createDatabase.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { dbHelper.getWritableDatabase(); } });
输入 adb shell,就会进入到设备的控制台,使用 cd 命令进行到/data/data/com.example.databasetest/databases/目录下,并使用 ls 命令查看到该目录里的文件
打开数据库:
sqlite3 数据库全名(sqlite3 Book.db)
查看数据库表:
键入.table 命令
查看建表语句
.schema
退出数据库的编辑
键入.exit 或.quit
升级数据库
1、
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //这种方式会让老用户数据丢失 db.execSQL("drop table if exists Book"); db.execSQL("drop table if exists Category"); //增加版本号判断 switch (oldVersion) { case 1: db.execSQL(CREATE_CATEGORY); case 2: db.execSQL("alter table Book add column category_id integer"); default: } onCreate(db); }
细节:
switch 中每一个 case 的最后都是没有使用 break 的,为什么要这么做呢?这是为了保证在跨版本升级的时候,每一次的数据库修改都能被全部执行到。
2、让 onUpgrade()方法执行
还记得 SQLiteOpenHelper 的构造方法里接收的第四个参数吗?它表示当前数据库的版本号,之前我们传入的是 1,现在只要传入一个比 1 大的数,就可以让 onUpgrade()方法得到执行了
添加数据:
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.96); 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); // 插入第二条数据 或: db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)", new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" });
更新数据:
ContentValues values = new ContentValues(); values.put("price", 10.99); db.update("Book", values, "name = ?", new String[] { "The DaVinci Code" }); db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99", "The Da Vinci Code" });
删除数据:
SQLiteDatabase db = dbHelper.getWritableDatabase(); db.delete("Book", "pages > ?", new String[] { "500" }); 或 db.execSQL("delete from Book where pages > ?", new String[] { "500" });
查询数据:
query()方法用于对数据进行查询。
这个方法的参数非常复杂,最短的一个方法重载也需要传入七个参数。
第一个参数不用说,当然还是表名,表示我们希望从哪张表中查询数据。
第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列。
第三、第四个参数用于去约束查询某一行或某几行的数据,不指定则默认是查询所有行的数据。
第五个参数用于指定需要去 group by 的列,不指定则表示不对查询结果进行 group by 操作。
第六个参数用于对 group by 之后的数据进行进一步的过滤,不指定则表示不进行过滤。
第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。
SQLiteDatabase db = dbHelper.getWritableDatabase(); 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 = cursor.getDouble(cursor.getColumnIndex("price")); } while (cursor.moveToNext()); } cursor.close();
db.rawQuery("select * from Book", null);
可以看到,除了查询数据的时候调用的是 SQLiteDatabase 的 rawQuery()方法,其他的操作都是调用的 execSQL()方法。
事务:
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", "George Martin"); values.put("pages", 720); values.put("price", 20.85); db.insert("Book", null, values); db.setTransactionSuccessful(); // 事务已经执行成功 } catch (Exception e) { e.printStackTrace(); } finally { db.endTransaction(); // 结束事务 } }
。。