Android系统提供了三种方式用于简单地实现数据持久化功能
1 文件存储
1.1将数据存储在文件中
所有数据都是原封不动地保存在文件当中,比较适合用于存储一些简单的文本数据或二进制数据。
Context类提供了一个openFileOutput()方法,将数据存储在指定文件中,参数是文件名,文件操作模式,两种操作模式,MODE_PRIVATE,MODE_APPEND,openFileOutput()方法返回的是一个FileOutStream对象,得到这个对象就可以使用Java流的方式将数据写入到文件中了。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/edit"
android:hint="type something here"/>
</LinearLayout>
public class MainActivity extends AppCompatActivity {
private EditText edit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edit = (EditText) findViewById(R.id.edit);
}
@Override
protected void onDestroy(){
super.onDestroy();
String inputText = edit.getText().toString();
save(inputText);
}
public void save(String inputText){
FileOutputStream out = null;
BufferedWriter writer = null;
try{
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(inputText);
}catch(IOException e){
e.printStackTrace();
}finally {
try{
if(writer!=null){
writer.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
通过openFileOutput()得到一个FileOutputStream对象,然后再借助它构建出一个OutputStreamWriter对象,接着再使用OutputStreamWriter构造出一个BufferedWriter对象,这样就可以通过BufferedWriter来将文本内容写入到文件中了。
1.2从文件中读取数据
Context类还提供了一个openFileInput()方法获取一个FileInputStream对象,然后借助他构建出了一个InputStreamReader对象,接着再使用InputStreamReader构建出了一个BufferedReader对象,这样就可以通过BufferedReader进行一行一行读取,把文件中的所有文本内容全部读取出来,并存放在一个StringBuilder对象中。
......
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edit = (EditText) findViewById(R.id.edit);
String inputText = load();
if(!TextUtils.isEmpty(inputText)){
edit.setText(inputText);
edit.setSelection(inputText.length());
Toast.makeText(this,"Restoring succeeded",Toast.LENGTH_SHORT).show();
}
}
......
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();
}
onCreate()方法中调用load()方法来读取文件中存储的文本内容,用TextUtils.isEmpty()对字符串进行非空判断,这个方法可以进行两种空值的判断,当传入的字符串等于null或等于空字符串的时候,都会返回true。如果读到的内容不为null,就调用EditText的setText()方法将内容填充到EditText里,并调用setSelection()方法将输入光标移动到文本的末尾置以便继续输入,然后弹出一句还原成功的提示。
2 SharedPreferences 存储
2.1 将数据存储在SharedPreferences中
SharedPreferences 是使用键值对的方式存储
使用SharedPreferences存储数据,首先需要获取到SharedPreferences对象:
1.Context类中的getSharedPreferences()方法:第一个参数文件名,第二个参数操作模式
2.Activity类中的getPreferences()方法:只接受一个参数,操作模式,会将当前活动的类名作为SharedPreference的文件名。
3.PreferenceManager类中的getDefaultSharedPreferences()方法:这是一个静态方法,接受一个参数Context,自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件。
得到对象后(1)调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象。(2)向SharedPreferences.Editor对象中添加数据,putBoolean(),putString()等。(3)调用apply()方法将添加的数据提交,从而完成数据库存储操作。
<LineaLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/save_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Save Button"/>
</LineaLayout>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button saveData = (Button) findViewById(R.id.save_button);
saveData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
editor.putString("name","Tom");
editor.putInt("age",28);
editor.putBoolean("married",false);
editor.apply();
}
});
}
}
2.2从SharedPreferences中读取数据
SharedPreferences对象中提供了一系列的get方法,接收两个参数,第一个参数是键,第二个参数是默认值,当传入的键找不到对应的值时会以什么默认值返回。
Button restoreButton = (Button) findViewById(R.id.restore_button);
restoreButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
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("Main","name is " +name);
Log.d("Main","age is " +age);
Log.d("Main","married is " +married);
}
});
3 SQLite数据库存储
3.1创建数据库
能够更方便地管理数据库,专门提供了一个SQLiteOpenHelper帮助类,是一个抽象类,我们要是想要使用它,就需要创建一个自己的帮助类去继承他,SQLiteOpenHelper中有两个抽象方法,分别是onCreate()和onUpgrade(),我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建,升级数据库的逻辑。
SQLiteOpenHelper中有两个非常重要的实例方法,getReadableDatabase()和getWritableDatabase(),这两个方法都可以创建或打开一个现有的数据库,并返回一个可对数据库进行操作读写的对象,当数据库不可写入(如磁盘空间已满),getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法则出现异常
SQLiteOpenHelper中有两个构造方法可供重写,一般使用参数少一点的构造方法,这个构造方法中接收4个参数,第一个Context,第二个数据库名,第三个参数允许我们在查询数据的时候返回一个自定义Cursor,一般传入null,第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作。构建出SQLiteOpenHelper实例后再调用它的getReadableDatabase()和getWritableDatabase()方法就能够创建数据库了。
创建一个名为BookStore.db的数据库,在这个数据库中新建一张Book表,SQLite数据类型,integer表示整型,real表示浮点型,text表示文本型,blob表示二进制类型,下面使用primary key将id设为主键,并用autoincrement关键字表示id列是增长的。
新建Database Test项目
新建MyDatabaseHelper类继承自SQLiteOpenHelper,
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, SQLiteDatabase.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 Success",Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion){}
}
将建表语句定义成了一个字符串常量,然后在onCreate()方法中又调用了SQLiteDatabase的execSQL()方法去执行这条建表语句,并弹出一个Toast提示,这样就可以保证在数据库创建完成的同时还能成功创建Book表。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/create_database"
android:text="Create Database"/>
</LinearLayout>
最后修改MainActivity中的代码
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dpHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dpHelper = new MyDatabaseHelper(this,"BookStore.db",null,1);
Button createDatabase = (Button)findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dpHelper.getWritableDatabase();
}
});
}
}
在onCreate()方法中构建了一个MyDatabaseHelper对象,并通过构造函数的参数将数据库指名为BookStore.db,版本号为1,然后在按钮的点击事件里调用了getWritableDatabase()方法。
3.2 升级数据库
onUpgrade()方法用于升级数据库,现在再添加一张Category表用于记录图书分类,
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)";
public static final String CREATE_CATEGORY = "create table Category ("
+ "id integer primary key autoincrement, "
+ "category_name text, "
+ "category_code integer )";
private Context mContext;
public MyDatabaseHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version){
super(context,name,factory,version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db){
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
Toast.makeText(mContext,"create success",Toast.LENGTH_SHORT).show();
}
@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(db);
}
}
在onUpgrade()方法中执行了两条DROP语句,如果发现数据库已经存在Book表或Category表,就将这两张表删除掉,然后再调用onCreate()方法重新创建,这里先将已经存在的表删除掉,如果创建表时发现这张表存在,就会直接报错。
之前的版本号是1,现在改成一个比1大的数,改成2,表示对数据库进行了升级。
dpHelper = new MyDatabaseHelper(this,"BookStore.db",null,2);
3.3 添加数据
调用SQLiteOpenHelper的getReadableDatabase()和getWritableDatabase()方法能对数据库进行创建和升级,同时返回一个SQLiteDatabase对象,借助这个对象可以对数据库进行CRUD操作。
添加数据,SQLiteDatabase提供了insert()方法,三个参数,第一个参数是表名,第二个参数用于指定在未添加数据的情况下给某些可为空的列自动赋值NULL,一般直接传入null,第三个参数是一个ContentValues对象,提供了一系列put()方法重载,可用于向ContentValues中添加数据,只需要将表中的每个列明以及相应的待添加数据传入即可。
......
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/add_data"
android:text="Add Data"/>
......
Button addDatabase = (Button) findViewById(R.id.add_data);
addDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dpHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name","Android");
values.put("author","Tom");
values.put("pages",454);
values.put("price",20);
db.insert("Book",null,values);
values.clear();
values.put("name","Android1");
values.put("author","Jack");
values.put("pages",500);
values.put("price",30);
db.insert("Book",null,values);
}
});
......
在添加按钮的点击事件里,先获取到了SQLiteDatabase对象,然后再使用ContentValues来对要添加的,数据进行组装,因为将id列设为自动增长,他的值会在入库时自动生成,不需要手动赋值,然后调用insert()方法将数据添加到表中。
3.4 更新数据
SQLiteDatabase中提供了updata()方法,接收四个参数,第一个参数,表名,第二个参数ContentValues对象,第三第四个参数用于约束更新某一行或某几行中的数据,不指定就是默认所有行。
......
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/up_data"
android:text="Up Data"/>
......
......
Button upDatabase = (Button) findViewById(R.id.up_data);
upDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dpHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price",10);
db.update("Book",values,"name = ?",new String[]{"Android"});
}
});
.......
这里使用了第三第四个参数来指定具体更新哪几行,第三个参数对应SQL语句的where部分,表示更新所有name等于?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每个占位符指定相应的内容。
3.5 删除数据
SQLiteDatabase中提供了一个delete()方法,三个参数,第一个参数表名,第二,三个参数用于约束某一行或某几行的数据
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/delete_data"
android:text="Delete Data"/>
Button deleteDatabase = (Button)findViewById(R.id.delete_data);
deleteDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dpHelper.getWritableDatabase();
db.delete("Book","pages > ?",new String[]{"490"});
}
});
3.6 查询数据
SQLiteDatabase中提供了一个query()方法,七个参数,第一个参数表名,第二个参数用于指定去查询哪几行,第三四用于约束查询某一行或某几行的数据,第五个参数用于指定需要去group by的列,不指定则不对查询结果进行group by操作,第六个参数用于对group by之后的数据进行进一步过滤,不指定则不过滤,第七个参数用于指定查询结果的排序方式。
调用query()方法后会返回一个Cursor对象,查询到的所有数据都将从从这个对象取出。
......
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/query_data"
android:text="Query Data"/>
......
......
Button queryDatabase = (Button) findViewById(R.id.query_data);
queryDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dpHelper.getWritableDatabase();
Cursor cursor = db.query("Book",null,null,null,null,null,null);
if(cursor.moveToFirst()){
do{
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"));
Log.d("MainActivity","book name is "+name);
Log.d("MainActivity","book author is "+author);
Log.d("MainActivity","book pages is "+pages);
Log.d("MainActivity","book price is "+price);
}while(cursor.moveToNext());
}
cursor.close();
}
});
......
查询完之后得到一个Cursor对象,接着我们调用它的moveFirst()方法将数据的指针移动到第一行位置,然后进入到一个循环,去遍历查询每一行数据。在循环中,通过Cursor的getColumnIndex()方法获取到某一列再表中对应的索引,然后将这个索引传入到相应的取值方法中,就可以得到从数据库中读取到的数据了。最后调用close()方法关闭Cursor。
3.7 使用SQL操作数据库
直接使用SQL操作
1.添加数据
db.execSQL("insert into Book(name,author,pages,price) values(?,?,?,?)",
new String[] {"Android","Tom","454","20"});
2.更新数据
db.execSQL("update Book set price = ? where name = ?",new String[] {"10","Android"});
3.删除数据
db.execSQL("delete from Book where pages > ?",new String[] {"500"});
4.查询数据
db.rawQuery("select * from Book",null);