Android系统主要提供了3种方式用于简单地实现数据持久化功能——文件存储、SharedPreference存储以及数据库存储。
文件存储 文件存储是Android中最基本的存储方式,它不对存储内容进行任何的格式化处理,因而比较适合用于存储一些简单的文本数据 或者二进制数据 。
将数据存储到文件 Context类提供了一个openFileOutput()方法,可以用于将数据存储到指定文件,需要两个参数,第一个参数是文件名,纯粹的名称,不可以包含路径,因为所有的文件都是默认存储到/data/data//files/ 目录下;还有个参数是操作模式,主要有两种(其他2种在4.2被废弃了):
MODE_PRIVATE:默认的操作模式,表示当指定同样文件名的时候,所写入的内容将会覆盖原来文件中的内容。
MODE_APPEND:表示如果该文件已经存在,就往文件里面追加内容,不存在就创建新文件。
保存文件的一般如以下代码操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public void save () { String dataStr = "data to save" ; FileOutputStream out = null ; BufferedWriter writer = null ; try { out = openFileOutput("data" ,Context.MODE_PRIVATE); writer = new BufferedWriter(new OutputStreamWriter(out)); writer.write(dataStr); }catch (IOException e){ e.printStackTrace(); }finnaly{ try { if (writer != null ){ writer.close(); }catch (IOException e){ e.printStackTrace(); } } } }
存储成功后,可以通过Android Device Monitor 进入File Explorer标签,在目录中/data/data//files/中就能找到 data 文件。同理,读取存到文件中的代码应如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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(); }finnaly{ try { if (reader != null ){ reader.close(); }catch (IOException e){ e.printStackTrace(); } } } return content.toString(); }
SharedPreference SharedPreference是使用键值对的方式来存储数据的,保存一条数据的时候,需要给这条数据提供一个对应的键,读取数据时通过这个键把对应的值读取出来,SharedPreference文件都是存放在/data/data//shared_prefs目录下。要想存储数据,首先要获取到SharedPreference对象,Android主要提供了3中方式:
Context类中的getSharedPreference()方法:此方法接收两个参数,第一个用于指定文件名称,第二个用于指定操作模式,目前只有MODE_PRIVATE可选(其他的几种在4.2或者6.0版本被废弃了),并且是默认的操作模式,表示只有当前应用程序才可以对这个文件进行读写。
Activity中的getPreferences()方法:和Context类中的getSharedPreference()方法类似,只不过它只接受一个操作模式参数,因为使用这个方法时会自动将当前Activity的类名作为SharedPreference的文件名。
PreferenceManager类中的getDefaultSharedPreferences()方法:它接受一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreference文件。
获取到SharedPreference对象之后,就可以开始存储数据了,主要分为3步实现:
调用SharedPreference对象的edit()方法获取SharedPreference.Editor对象
向SharedPreference.Editor对象添加数据。
调用apply()方法提交,从而完成存储操作。
代码形式应该是这样的:
1 2 3 4 5 6 7 SharedPreferences.Editor editor = getSharedPreferences("data" ,MODE_PRIVATE).edit(); editor.putString("name" ,"Tom" ); editor.apply(); SharedPreferences pref = getSharedPreferences("data" ,MODE_PRIVATE); String name = pref.getString("name" ,"" );
SQLite数据库存储 文件存储和SharedPrefrences存储只适用于保存一些简单的数据和键值对,要存储大量复杂的关系型数据的时候,有点难以应付了。
创建数据库 Android为了让我们更方便地管理数据库,专门提供了一个SQLiteOpenHelper抽象类,要想使用的话,我们就需要创建一个自己的类去继承它,它有两个抽象方法,onCreate和onUpgrade用来创建和升级数据库,其它两个重要的实例方法:getReadableDatabase和getWritableDatabase,他们都可以创建或者打开一个现有的数据库(没有就创建),在数据库不可写入的时候(如磁盘满了),前者以只读的形式打开数据库,后者会出现异常。它有两个构造方法可重写,一般使用哪个参数较少的即可,总共4个参数,第一个context,第二个是数据库名,第三个是自定义的Cursor,一般传null,第四个表示当前的数据库版本号,用于对数据库进行升级操作。一般代码如下图所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class MyDatabaseHelper extends SQLiteOpenHelper { public static final CREATE_BOOK = "create table Book (" + "id integer primary key autoincrement," + "author text," + "price real," + "pages integer," +"name text)" ; @Override public void onCreate (SQLiteDatabase db) { db.exeSQL(CREATE_BOOK); } }
使用的时候应该是这样子的:
1 2 3 dbHelper = new MyDatabaseHelper(this ,"BookStore.db" ,null ,1 ); dbHelper.getWritableDatabase();
上例创建了一个Book表,使用primary key 将id设置为主键,并用autoincrement关键字表示id是自增长的。可以使用
adb shell
命令,之后cd到/data/data//databases/目录下用ls 列出该目录的文件,可以看到BookStore.db文件,以及BookStore.db-journal文件,后者是数据库的临时文件。SQLite没有其他数据库一样有很多繁杂的数据类型,它的数据类型很简单:integer表示整型,real表示浮点型,text表示文本,blob表示二进制类型 ;
升级数据库 此时项目中有一张Book表用于存放输的各种详细数据了,但是如果再想添加一张Category表用于记录图书的分类,如果仅仅直接在MyDatabaseHelper的onCreate中写成:
1 2 3 4 5 @Override public void onCreate (SQLiteDatabase db) { db.exeSQL(CREATE_BOOK); db.exeSQL(CREATE_CATEGORY); }
是行不通的,因为使用的时候先初始化helper:dbHelper = new MyDatabaseHelper(this,”BookStore.db”,null,1) 再获取数据库:dbHelper.getWritableDatabase() ,而由于此时已经存在数据库BookStore.db了,因此不会再执行helper的onCreate方法了。此时清除app数据可以做到创建Category表,但是这在实际应用中不合理,而我们可以用onUpgrade 方法来解决,我们前面构造了MyDatabaseHelper,第4个参数是版本号,我们目前是1,所以只要传入的值大于当前版本号1,onUpgrade方法就可以执行,因此我们可以这样增加Category表:
1 2 3 4 5 6 7 8 9 10 11 12 public class MyDatabaseHelper extends SQLiteOpenHelper { ... @Override public void onUpgrade (SQLiteDatabase db,int oldVersion,int newVersion) { db.exeSQL("drop table if exists Book" ); db.exeSQL("drop table if exists Category" ); onCreate(db); } }
上述代码执行了两条drop语句,发现数据库已经存在Book表和Category表了就删除,然后调用onCreate方法重新创建,因此在onCreate中也得写成:
1 2 3 4 5 @Override public void onCreate (SQLiteDatabase db) { db.exeSQL(CREATE_BOOK); db.exeSQL(CREATE_CATEGORY); }
在使用的时候也得升级版本号:
1 2 3 dbHelper = new MyDatabaseHelper(this ,"BookStore.db" ,null ,2 ); dbHelper.getWritableDatabase();
获取到数据库,接下来可以对其CRUD操作,其中C代表添加(Create),R代表查询(retrieve),U代表更新(Update),D代表删除(Delete)。Android开发者水平参差不齐,并非每一个都会SQL语言,Android提供了一系列的辅助性方法,是的在Android中即使不去编写SQL语句,也能轻松完成所有CRUD操作。getReadableDatabase与getWriteableDatabase方法不仅可以用来创建和升级数据库,他们还会返回一个SQLiteDatabase对象,借助这个对象就可以轻松CRUD:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("name" ,"thinking in java" ); values.put("price" ,16.96 ); values.put("pages" ,512 ); db.insert("Book" ,null ,values); values.clear(); values.put("price" ,20 ); db.update("Book" ,values,"name=?" ,new String[]{"thinking in java" }); db.delete("Book" ,"pages > ?" ,new String[]{"500" }); Cursor cusor = db.query("Book" ,null ,null ,null ,null ,null ,null ); if (cursor.moveToFirst()){ do { String name = cursor.getString(cursor.getColumnIndex("name" )); String pages = cursor.Double(cursor.getColumnIndex("price" )); }while (cursor.moveToNext()); } cusor.close();
当然,可以直接使用SQL语句直接完成上述操作:
1 2 3 4 5 6 7 8 9 10 11 / / 添加db.execSQL("insert into Book (name,pages,price) values(?,?,?)",new String[]{"thinking in java","512","20"}); / / 升级db.execSQL("update Book set price = ? where name = ",new String[]{"20","thinking in java"}); / / 删除db.execSQL("delete from Book where pages > ?",new String[]{"500"}); / / 查询db.execSQL("select * from Book",null );
使用LitePal 略