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");
        }
}

源码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值