Android数据持久化
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"); }}
源码下载
更多相关文章
- 【Android】win10操作系统下Android开发环境配置
- Android提交数据到服务器的两种方式四种方法
- 操作android中的通讯录
- Android 数据存储之SQLite数据库存储
- Android数据解析出错com.android.volley.NoConnectionError: jav
- Android手势操作示例(上/下/左/右的判断)
- 引用第三方进行Android前端与web后台的数据交互