Android 数据持久化技术(即数据存储方式)
来源:互联网 发布:查看端口占用进程 编辑:程序博客网 时间:2024/05/13 17:26
在讨论数据持久化技术之前我们先了解几个概念?
什么是瞬时数据:存储在内存当中,有可能会因为程序的关闭或其他原因导致内存被收回而丢失的数据。
为什么采用数据持久化技术:为了保证关键数据在程序退出时不被丢失。
什么是数据持久化技术:将内存中的瞬时数据保存到存储设备中,保证手机在关机的情况下数据仍然不会丢失。
安卓提供了三种方式用于简单的数据持久化功能:文件储存,SharedPreference存储,数据库储存。
文件储存
用于保存一些简单的文本数据或二进制数据。
使用到的方法:Context类中提供了openFileOutput()方法 和 openFileInput()方法
openFileOutput()方法 拥有两个参数 第一个是文件名 第二个是文件的操作方式
默认的存储到data/data/<package name> files目录下
文件的操作方式: MODE_PRIVATE当指定同样文件名时会覆盖原文件中的内容
MODE_APPEND当该文件已存在时就往文件中追加内容,不会创建新文件
文件存储使用:java流
demo:在文本框中输入数据,点击button保存,当程序退出,或者activity销毁时,还能回显保存的数据
代码:
布局文件
<EditText
android:id=
"@+id/et"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
/>
<Button
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:text=
"save"
android:onClick=
"click"
/>
public
class
MainActivity extends Activity {
private
EditText et;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et = (EditText) findViewById(R.id.et);
String inputText = save();<br>
//回显数据
if
(!TextUtils.isEmpty(inputText)) {
et.setText(save());
}
}
//读取保存数据
public
String save() {
BufferedReader reader =
null
;
try
{
FileInputStream
in
= openFileInput(
"save"
);
reader =
new
BufferedReader(
new
InputStreamReader(
in
));
StringBuffer buffer =
new
StringBuffer();
String len =
""
;
while
((len = reader.readLine()) !=
null
) {
buffer.append(len);
}
return
buffer.toString();
}
catch
(FileNotFoundException e) {
e.printStackTrace();
}
catch
(IOException e) {
e.printStackTrace();
}
finally
{
try
{
if
(reader!=
null
){
reader.close();
}
}
catch
(IOException e) {
e.printStackTrace();
}
}
return
""
;
}
//点击保存数据
public
void
click(View v) {
BufferedWriter writer =
null
;
String text = et.getText().toString();
if
(!TextUtils.isEmpty(text)) {
try
{
FileOutputStream
out
= openFileOutput(
"save"
, MODE_PRIVATE);
writer =
new
BufferedWriter(
new
OutputStreamWriter(
out
));
writer.write(text);
}
catch
(FileNotFoundException e) {
e.printStackTrace();
}
catch
(IOException e) {
e.printStackTrace();
}
finally
{
if
(writer !=
null
) {
try
{
writer.close();
}
catch
(IOException e) {
e.printStackTrace();
}
}
}
}
}
}
SharedPreferences储存
sharedPreferences是采用键值对的方式存储数据的,它的储存方式比文件储存简单易用。
使用到的方法:getSharedPreferences() 此方法接受两个参数,
第一个参数是文件名,如果文件不存在则会创建一个。
默认的储存路径是:data/data/<package name>/shared_prefs/下
第二个参数是指定操作模式:MODE_PRIVATE和MODE_MULTI_PROCESS
MODE_PRIVATE表示只有当前应用程序可以对sharedPreferences文件读写。
MODE_MULTI_PROCESS 用于会有多个进程中对同一个sharedPreferences文件读取。
demo:通过sharePreferences来实现记住密码的功能,当我们点击登录时,如果勾选checkbox记住密码,则会保存密码,下次启动应用会回显应用的用户名密码,当我们不勾选checkbox时,不会保存用户名密码,同时如果保存的有用户名密码的话,会删除保存的用户名密码。(这里不做验证密码的操作)
<EditText
android:id=
"@+id/et_username"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:hint=
"请输入用户名"
/>
<EditText
android:id=
"@+id/et_password"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:inputType=
"textPassword"
android:hint=
"请输入密码"
/>
<CheckBox
android:id=
"@+id/cb_remember"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:text=
"记住密码"
/>
<Button
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:text=
"登陆"
android:onClick=
"land"
/>
public
class
MainActivity extends Activity {
private
EditText et_username;
private
EditText et_password;
private
SharedPreferences sPref;
private
CheckBox cb_remember;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_username = (EditText) findViewById(R.id.et_username);
et_password = (EditText) findViewById(R.id.et_password);
cb_remember = (CheckBox) findViewById(R.id.cb_remember);
sPref = getSharedPreferences(
"save"
, MODE_PRIVATE);
String username = sPref.getString(
"username"
,
""
);
String password = sPref.getString(
"password"
,
""
);
//如果保存的有用户名密码的话回显
if
(!TextUtils.isEmpty(username) && !TextUtils.isEmpty(password)) {
et_username.setText(username);
et_password.setText(password);
}
}
//登陆
public
void
land(View view) {
String username = et_username.getText().toString();
String password = et_password.getText().toString();
//判断用户名密码不能为空
if
(TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
Toast.makeText(
this
,
"用户名或密码为空"
, 0).show();
}
else
{
//checkbox勾上时记录密码
if
(cb_remember.isChecked()) {
Toast.makeText(
this
,
"登陆成功"
, 0).show();
sPref.edit().putString(
"username"
, username).commit();
sPref.edit().putString(
"password"
, password).commit();
}
else
{
//checkbox不勾上时清清除用户名密码
Toast.makeText(
this
,
"登陆成功"
, 0).show();
sPref.edit().remove(
"username"
).commit();
sPref.edit().remove(
"password"
).commit();
}
}
}
}
在真实的保存用户名密码的时候我们可以使用md5加密算法,对用户名密码进行加密,再下一个博客中来展示如何使用md5
数据库储存
当我们需要储存大量复杂的关系型数据的时候,前两种方法就有点力不从心了,例如保存短息,联系人信息等,这个时候我们就可以使用安卓内置的数据库。
安卓系统内置了SQLLite数据库,它支持SQL语法,还遵循数据库的ACID事务,是一款轻量级的数据库
创建数据库
1创建数据库需要继承SQLiteOpenHelper,我们需要重写它的两个方法,onCreate()和onUpgrade().分别在这连个方法中创建和升级数据库
2SQLiteOpenHelper中有两个非常重要的实例方法,getReadableDatabase()和getWritableDatabase()。这两个方法都可以创建和打开一个现有的数据库。当磁盘空间满的时候getReadableDatabase()会打开一个只读的数据库,getWritableDatabase()会出现异常。磁盘空间未满的时候都是创建或打开一个可读可写的数据库。
数据库文件会存放在data/data/<package name>/databases/
3SQLiteOpenHelper的构造方法,四个参数 第一个Context 第二个 数据库名 第三个 查询数据时返回一个自定义的Cursor,一般都传null , 第四个是数据库的版本号
4在SQLite数据库中数据类型 integer表示整形 real表示浮点型 text 表示文本类型 blob表示二进制类型
demo: 我们在一个demo中展示创建数据库,升级数据库,实现数据库的增删改查功能 ,和使用事物功能,对每个功能的使用做了注释。
//布局文件<br><!-- 创建数据库-->
<Button
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:onClick=
"create_database"
android:text=
"创建数据库"
/>
<!-- 添加数据-->
<Button
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:onClick=
"insert_database"
android:text=
"添加数据"
/>
<!-- 更新数据-->
<Button
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:onClick=
"update_database"
android:text=
"更新数据"
/>
<!-- 删除数据-->
<Button
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:onClick=
"delete_database"
android:text=
"删除数据"
/>
<!-- 查询数据-->
<Button
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:onClick=
"query_database"
android:text=
"查询数据"
/>
<!-- 使用事务-->
<Button
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:onClick=
"Transaction_database"
android:text=
"使用事务"
/>
//MySQLiteOpenHelper帮助类
<br>
public
class
MySQLiteOpenHelper extends SQLiteOpenHelper {
public
static
final String Create_contact =
"create table person(_id integer primary key autoincrement, "
+
"name char(10), "
+
"salary char(20), "
+
"phone integer(20))"
;
public
MySQLiteOpenHelper(Context context, String name,
CursorFactory factory,
int
version) {
super(context, name, factory, version);
mcontext = context;
}
@Override
public
void
onCreate(SQLiteDatabase db) {
//当创建数据库的时候会调用此方法在此方法中创建表
db.execSQL(Create_contact);
Toast.makeText(mcontext,
"联系人数据库创建了"
, 0).show();
}
@Override
public
void
onUpgrade(SQLiteDatabase db,
int
oldVersion,
int
newVersion) {
}
}
//增删改查事物的功能<br>public class MainActivity extends Activity {
private
MySQLiteOpenHelper dbHelper;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//创建SQLiteOpenHelper实例
dbHelper =
new
MySQLiteOpenHelper(
this
,
"contact.db"
,
null
, 1);
}
//创建数据库
public
void
create_database(View view) {
dbHelper.getWritableDatabase();
}
//添加数据
public
void
insert_database(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values =
new
ContentValues();
//开始组装第一条数据
values.put(
"name"
,
"ben"
);
values.put(
"salary"
,
"10000"
);
values.put(
"phone"
, 1384567);
//第一个参数是名 第二个参数未指定添加数据的情况下给某些可为空的列自动赋值NULL,我们不使用这个功能传null 第三个ContentValues对象
db.insert(
"person"
,
null
, values);
//也可以使用sql语句,达到同样效果
//db.execSQL("insert into person (name,salary,phone) values(?,?,?)", new String []{"ben","10000","1384567"});
values.clear();
values.put(
"name"
,
"jack"
);
values.put(
"salary"
,
"15000"
);
values.put(
"phone"
, 1857545);
db.insert(
"person"
,
null
, values);
//也可以使用sql语句,达到同样效果
//db.execSQL("insert into person (name,salary,phone) values(?,?,?)", new String[]{"jack","15000","1857545"});
}
//更新数据
public
void
update_database(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values =
new
ContentValues();
values.put(
"salary"
,
"15000"
);
db.update(
"person"
, values,
"name=?"
,
new
String[]{
"ben"
});
//也可以使用sql语句,达到同样效果
//db.execSQL("update person set salary=? where name=?", new String[]{"15000","ben"});
}
//删除数据
public
void
delete_database(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete(
"person"
,
"name=?"
,
new
String[]{
"ben"
});
//也可以使用sql语句,达到同样效果
//db.execSQL("delete from person where name=?", new String[]{"ben"});
}
//查询数据
public
void
query_database(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
//参数以此表示 表名 列名 where约束条件 占位符填充值 groudby having orderBy
Cursor cursor = db.query(
"person"
,
null
,
null
,
null
,
null
,
null
,
null
);
while
(cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(
"name"
));
String salary = cursor.getString(cursor.getColumnIndex(
"salary"
));
int
phone = cursor.getInt(cursor.getColumnIndex(
"phone"
));
//也可以使用sql语句,达到同样效果
//db.rawQuery("select * from person",null);
System.
out
.println(name+
"--"
+salary+
"--"
+phone);
}
cursor.close();
//也可以使用sql语句,达到同样效果
//db.execSQL("delete from person where name=?", new String[]{"ben"});
}
//事务可以保证一系列操作要么全部完成,要么都不完成
public
void
Transaction_database(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
//开启事物
db.beginTransaction();
try
{
db.delete(
"person"
,
null
,
null
);
if
(
true
){
//在这里手动抛出异常,让事务失败,那么一条语句都没有执行成功
throw
new
NullPointerException();
}
ContentValues values =
new
ContentValues();
values.put(
"name"
,
"ben"
);
values.put(
"salary"
,
"10000"
);
values.put(
"phone"
, 1384567);
db.insert(
"person"
,
null
, values);
//事务执行成功
db.setTransactionSuccessful();
}
catch
(Exception e) {
e.printStackTrace();
}
finally
{
db.endTransaction();
//关闭事物
}
}
升级数据库。
需求:现在需要升级数据库,版本1里面有一张表,版本2中有一张表,用户升级分两种情况
如果用户现在的版本号是1,那么他则只需要创建一张表,如果用户直接安装版本2,则直接创建2张表(在update中使用switch代码有体现)。
public
class
MySQLiteOpenHelper extends SQLiteOpenHelper {
public
static
final String Create_contact =
"create table person(_id integer primary key autoincrement, "
+
"name char(10), "
+
"salary char(20), "
+
"phone integer(20))"
;
public
static
final String Create_sms =
"create table sms(_id integer primary key autoincrement, "
+
"name char(10), "
+
"body char(20), "
+
"time integer(20))"
;
private
Context mcontext;
public
MySQLiteOpenHelper(Context context, String name,
CursorFactory factory,
int
version) {
super(context, name, factory, version);
mcontext = context;
}
@Override
public
void
onCreate(SQLiteDatabase db) {
// 当创建数据库的时候会调用此方法在此方法中创建表
db.execSQL(Create_contact);
db.execSQL(Create_sms);
// 更新数据库时,如果用户直接安装最新版本,则同时将两张表创建好
Toast.makeText(mcontext,
"联系人数据库创建了"
, 0).show();
}
@Override
public
void
onUpgrade(SQLiteDatabase db,
int
oldVersion,
int
newVersion) {
switch
(oldVersion) {
case
1:
db.execSQL(Create_sms);
// 如果用户从1版本升级过来的话,就不会创建两张表而是再升级中添加一张表
default
:
}
}
}
public
class
MainActivity extends Activity {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MySQLiteOpenHelper helper =
new
MySQLiteOpenHelper(
this
,
"person.db"
,
null
, 2);
helper.getWritableDatabase();
}
}
新需求:这次要给contact表和sms表之间建立联系 给contact表中添加一个category_id的字段。 用户升级直接升级到3版本,或者从2版本升级过来。
在这里我们发现一个特别的地方,switch中case都没有break,这是为了每次升级修改都能被执行到。比如用户从从1版本升级到3版本,则case1 case2 中的逻辑都会执行到,使用这种方法可以保证数据时最新的,同时原来的数据也不会丢失
public
class
MainActivity extends Activity {
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MySQLiteOpenHelper helper =
new
MySQLiteOpenHelper(
this
,
"person.db"
,
null
, 3);
helper.getWritableDatabase();
}
}
public class MySQLiteOpenHelper extends SQLiteOpenHelper { public static final String Create_contact = "create table person(_id integer primary key autoincrement, " + "name char(10), " + "salary char(20), " + "phone integer(20),category_id integer)"; public static final String Create_sms = "create table sms(_id integer primary key autoincrement, " + "name char(10), " + "body char(20), " + "time integer(20))"; private Context mcontext; public MySQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); mcontext = context; } @Override public void onCreate(SQLiteDatabase db) { // 当创建数据库的时候会调用此方法在此方法中创建表 db.execSQL(Create_contact); db.execSQL(Create_sms); // 更新数据库时,如果用户直接安装最新版本,则同时将两张表创建好 Toast.makeText(mcontext, "联系人数据库创建了", 0).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { switch (oldVersion) { case 1: db.execSQL(Create_sms);// 如果用户从1版本升级过来的话,就不会创建两张表而是再升级中添加一张表 case 2: db.execSQL("alter table person add column category_id integer");// 如果用户从2版本升级过来的话,就不会创建两张表而是再升级中添加一张表 default: } }}
最后的升级数据库的最佳写法 来自郭霖著的第一行代码,我把其中的代码提炼出来,如果希望得到更详细的解释,可以查看第一行代码这本书
- Android 数据持久化技术(即数据存储方式)
- Android 数据持久化技术(即数据存储方式)
- Android-存储方式(持久化数据的方式)
- Android 数据持久化方式(2)SharedPreferences方式存储
- Android数据存储的持久化技术
- Android数据持久化存储方式
- Android数据持久化存储方式
- Android之数据持久化技术一(文件存储)
- Android 数据持久化方式(1)文件存储
- Android五种数据存储(数据持久化)方式
- Android--数据存储(数据持久化)
- 数据持久化存储技术
- Android数据持久化存储(一)
- Android数据持久化存储
- Android数据持久化存储
- Android--持久化技术之文件存储-数据读取
- Android 开发实践 数据存储方案 持久化技术
- android 摘要----数据存储全方案,详解持久化技术
- 单进程单线程,完成并发服务器(基础版)
- 《摆渡人》读后感
- Android仿拼多多拼团堆叠头像
- 基于SpringBoot+JavaFx的CS端的个人日程及项目管理工具
- leetcode290 word Pattern
- Android 数据持久化技术(即数据存储方式)
- 关于使用com.youth.banner.Banner遇到的问题
- /storage/emulated/0保存路径
- mybatis generator 生成数据库注释DAO源码
- @RequestParam(value = "disable", required = false)
- 为并发而生的 ConcurrentHashMap(Java 8)
- oracle数据库
- 浅谈javascript函数中apply()和call()方法的用途和异同
- linux安装之svn