Android数据持久化
1.内部存储1:
1)SharedPreferences :共享参数,使用简单,使用xml格式存储,一般用来存储系统设置。
目录:data/data/{包名}/shared_prefs
//使用共享参数保存数据
public void saveData(View v)
{
//获取共享参数对象
//第一个参数是共享参数文件的名字
//第二个参数是对该文件的操作模式
SharedPreferences shared = getSharedPreferences("configration",Context.MODE_PRIVATE);
//获取共享参数编辑对象
SharedPreferences.Editor edit = shared.edit();
//使用编辑对象存储数据
edit.putFloat("fontSize", fontSize);
edit.putInt("fontColor", fontColor);
edit.putInt("backColor", backColor);
//提交
edit.commit();
}
//使用共享参数读取数据
public void readData(View v) {
//获取共享参数对象
//第一个参数是被读取的共享参数文件
SharedPreferences shared = getSharedPreferences("configration", Context.MODE_PRIVATE);
fontSize = shared.getFloat("fontSize", 25);
fontColor = shared.getInt("fontColor", Color.GRAY);
backColor = shared.getInt("backColor", Color.WHITE);
textView.setTextSize(fontSize);
textView.setTextColor(fontColor);
textView.setBackgroundColor(backColor);
}
2)内部存储2:
内部存储的文件的存放位置 /data/data/(包名)/files/
系统封装好了openFileOutput(filename, Context.MODE_PRIVATE)方法和openFileInput(filename)方法。
//把数据保存到内部存储空间的文件中
public void saveFile(View v) throws IOException{
//获取文件名和文件内容
String filename = edit_fileName.getText().toString().trim();
String filecontent = edit_content.getText().toString().trim();
FileOutputStream fos = openFileOutput(filename, Context.MODE_PRIVATE);
fos.write(filecontent.getBytes());
fos.close();
}
public void readData(View v) throws IOException{
//获取被读取的文件名
String filename = edit_fileName.getText().toString().trim();
//读取文件
FileInputStream fis = openFileInput(filename);
byte[] arr = new byte[fis.available()];
int len = fis.read(arr);
edit_content.setText(new String(arr,0,len));
fis.close();
}
2.外部存储
文件操作工具类:
/** *
* 扩展卡种文件操作的工具类: 以缓存图片为例: 目录:mnt/sdcard
*/
public class FileUtil {
// 存储图片的目录
// Environment.getExternalStorageDirectory() :获取外部存储空间的目录
public static final String IMAGE_URL = Environment
.getExternalStorageDirectory() + "/by/images";
public static final int FORMAT_PNG = 1;
public static final int FORMAT_JPEG = 2;
/**
* 判断扩展卡是否挂载
* @return
*/
public static boolean isMounted() {
String state = Environment.getExternalStorageState();
return state.equals(Environment.MEDIA_MOUNTED);
}
/**
* 判断扩展卡的剩余空间够不够用
*/
public static boolean isAble()
{
//文件系统状态管理对象
StatFs fs = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath());
int count = fs.getFreeBlocks();//空闲的数据块个数
int size = fs.getBlockSize();//返回每个数据块的大小
//剩余空间大小
long total = count*size;//单位是字节
int t = (int) (total/1024/1024);
if(t>2)
return true;
else
return false;
}
/**
* 保存图片到扩展卡的功能
* @throws IOException
*/
public static void saveImage(String url, byte[] data) throws IOException { // 判断扩展卡是否挂载
if (!isMounted())
return;
// 判断存储目录是否存在
File dir = new File(IMAGE_URL);
if (!dir.exists())
dir.mkdirs();
// 把图片数据写入到一个图片文件
FileOutputStream fos = new FileOutputStream(new File(dir,
getFileName(url)));
fos.write(data);
fos.close();
}
/**
* 保存图片到扩展卡的功能
* @throws FileNotFoundException
*/
public static void saveImage(String url, Bitmap bitmap, int format)
throws FileNotFoundException {
// 判断扩展卡是否挂载
if (!isMounted())
return;
// 判断存储目录是否存在
File dir = new File(IMAGE_URL);
if (!dir.exists())
dir.mkdirs();
// 把图片数据写入到一个图片文件
FileOutputStream fos = new FileOutputStream(new File(dir,
getFileName(url)));
// 图片的压缩 CompressFormat.PNG:压缩之后的格式
bitmap.compress(format == 1 ? CompressFormat.PNG : CompressFormat.JPEG,
100, fos);
}
/**
* 从扩展卡读取图片的功能
*/
public static Bitmap readImage(String url) {
// 判断扩展卡是否挂载
if (!isMounted())
return null;
String filename = getFileName(url);
File file = new File(IMAGE_URL, filename);
Bitmap bitmap = null;
if(file.exists())
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
return bitmap;
}
/**
* 清空扩展卡 缓存目录中的内容的功能
*/
public static void clear() {
// 判断扩展卡是否挂载
if (!isMounted())
return;
File dir = new File(IMAGE_URL);
if (dir.exists()) {
File[] arr = dir.listFiles();
for(File f:arr)
{
f.delete();
}
}
}
/**
* 根据文件的下载路径获取文件名
* @param url
* @return
*/
public static String getFileName(String url) {
return url.substring(url.lastIndexOf("/") + 1);
}
}
3.数据库存储
**3.1从外部导入的数据库操作**
a)从外部把数据库文件push到扩展卡中,通过数据库文件路径得到数据库
b)在程序内部创建数据库
c)游标中的数据变化时,数据源用cursor时,要调用adapter(SimpleCursorAdapter)的swapCursor方法通知数据变化,
public void loadData()//加载数据
{
SQLiteDatabase db = dbHelper.getReadableDatabase();
cursor = db.query("t_user", columns, null, null, null, null, null);
//游标中的数据变化了,需要切换适配器的数据源
adapter.swapCursor(cursor);
}
使用SimpleCursorAdapter时,表中必须存在_id字段,否则报错,因为这个适配器内部会自动寻找_id字段的值.
//打开数据库
//第一个参数是被打开的数据库文件的路径
//第二个参数是管理游标的工厂类对象
//第三个操作数据库的方式
SQLiteDatabase db = SQLiteDatabase.openDatabase(DB_PATH, null, SQLiteDatabase.OPEN_READONLY);
然后就可以进行增删改查操作。
3.2 SQLiteOpenHelper操作内部数据库
a)定义一个类继承SQLiteOpenHelper
b)主要方法:构造方法、onCreate(SQLiteDatabase db)、onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
c) onCreate方法在该类对象调用第一次getWritableDatabase或getReadableDatabase时被系统调用,创建数据库,之后不会在调用了。
d) onUpgrade方法在版本更新时调用,主要写升级数据库的代码
使用:
dbHelper = new DBHelper(this);//初始化数据库帮助类
//因为是添加数据,所以获取到可写的数据库操作对象
SQLiteDatabase db = dbHelper.getWritableDatabase();
然后进行增删改查操作即可。
3.3数据库中事务的使用
SQLite数据库是支持事务的,事务的特性就是可以保证让某一系列的操作要么全部完成,要么一个不会完成。 比如转账场景中,银行会将转账的金额先从你的账户扣除,然后再向收款方的账户中添加等量的金额。如果这两个操作都成功了那没事,可如果当转账方的金额扣除了,而收款方收钱这个操作出意外失败了,那钱不就飞了。这种时候就需要用到事务。
下面来一个事务操作的实例:
数据库帮助类:为了简单,t_user表中只有一个_id和一个name字段
public class DbHelper extends SQLiteOpenHelper {
public DbHelper(Context context) {
super(context, "user.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table t_user(_id integer primary key, name text)");
db.execSQL("insert into t_user(name) values('Tom')");
db.execSQL("insert into t_user(name) values('Cat')");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
主程序:
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction();//开始事务
try {
db.delete("t_user", null, null);
if(isException){
throw new NullPointerException();//手动抛一个异常
}
db.execSQL("insert into t_user(name) values('Jack')");
db.setTransactionSuccessful();//两个操作都执行完成,设置事务执行成功
} catch (Exception e) {
Log.i("--", "异常");
e.printStackTrace();
} finally{
db.endTransaction();
}
}
});
button2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor cursor = db.rawQuery("select * from t_user", null);
while(cursor.moveToNext()){
String name = cursor.getString(cursor.getColumnIndex("name"));
Log.i("--", "name:" + name);
}
}
});
1)把isException变量设置为true,在beginTransaction之后抛一个异常,也就是说执行delete操作后就异常,setTransactionSuccessful不能执行,该事务执行失败,所以这两个操作都不会执行成功。然后再查看数据库数据:会得到开始的两条数据,如下图:
2)把isException变量设置为false,然后该事务会执行成功,到最后数据会显示新插入的一条,如下图:
3.4数据库升级需要注意的
1)不要每次升级数据库就把之前的表全部删除,在重新全部创建,这样之前的数据全部清空,用户体验为0
2)需要考虑的是,有些用户之前就安装了这个程序,有些用户是新用户
3)最后的结果必须是不管是新用户还是老用户,升级之后数据库都是最新的
数据库升级实例:
需求1:还是上面那个数据库,现在需求是数据库更新到版本2,新增一个表t_friend。
实现1:看注释
public class DbHelper extends SQLiteOpenHelper {
private final String CREATE_USER = "create table t_user(_id integer primary key, name text)";
//更新操作1:--->数据库更新到版本2,新增一个表
private final String CREATE_FRIEND = "create table t_friend(_id integer primary key, firend_id integer, name text)";
public DbHelper(Context context) {
super(context, "user.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_USER);
db.execSQL("insert into t_user(name) values('Tom')");
db.execSQL("insert into t_user(name) values('Cat')");
//数据库更新,注意:这个方法只用直接用第二版的用户才会执行,
//所以在onUpgrade里需要执行一些操作
db.execSQL(CREATE_FRIEND);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch (oldVersion) {
case 1:
db.execSQL(CREATE_FRIEND);//这就是说,如果是老用户,该语句会得到执行,所以也就升级了
}
}
}
需求2:在t_user表中添加一个字段friend_id
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch (oldVersion) {
case 1:
db.execSQL(CREATE_FRIEND);//这就是说,如果是老用户,该语句会得到执行,所以也就升级了
case 2:
//数据库更新操作2:--->在t_user表中添加一个字段friend_id
//(这个操作不更新数据库版本,所以都需要被执行,注意这里的细节,case后面都没带break,,所以这里所有的语句都会被执行
//这是为了保证在跨版本升级时,每一次的数据库修改都会被全部执行,比如当前用户是从第二版本升级到第三版本,那么case2的语句会被执行,
//如果用户直接从第一版本升级到第三版本,那么case1后面所有语句都会执行,这样,数据库都是更新到最新状态,而且表中的数据还保留着)
db.execSQL("alter table t_user add column friend_id integer");
}
}