数据存储---数据持久化

Android 系统中主要提供了三种方式用于简单地实现数据持久化功能,即文件存储,SharedPreference以及数据库存储。你还可以将数据保存在手机的SD卡中,不过使用文件、SharedPreference或数据库来保存数据会相对更简单一些,而且比起将数据保存在SD卡中会更加安全。

文件存储

文件存储是Android中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中,因而它比较适合用于存储一些简单的文本数据或二进制数据。如果你想使用文件存储的方式来保存一些较为复杂的文本数据,就需要定义一套自己的格式规范,这样方便于之后将数据从文件中来保存数据。

将数据存储到指定文件中

Context类中提供了一个openFileOutput()方法,可以用于将数据存储到指定的文件中。这个方法接收两个参数,第一个参数是文件名,在文件创建的时候使用的就是这个名称,注意这里指定的文件名不可以包含路劲,因为所有的文件都是默认存储到data/data//files/目录下的。第二个参数是文件的操作模式,主要有两种模式可选,MODE_PRIVATE和MODE_APPEND。其中MODE_PRIVATE是默认的操作模式,表示当指定同样文件名的时候,所写入的内容会覆盖源文件中的内容,而MODE_APPEND则表示如果该文件已存在就往文件里面追加内容,不存在就创建新文件。openFileOutput()方法返回的是一个FileOutputStream对象,得到了这个对象之后就可以使用Java流的方式将数据写入到文件中了。
实例:

public void save(){
    String data="Data to save";
    FileOutputStream out=null;
    BufferedWriter writer=null;
    try{
        out =openFileOutput("data",MODE_PRIVATE);
        writer =new BufferedWriter(new OutputStreamWriter(out));
        writer.write(data);
    }catch(IOException e){
        e.printStackTrace();    
    }finally{
        try{
            if(writer!=null){
                writer.close();
            }
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

从指定文件中读取数据

Context类中还提供了一个openFileInput()方法,用于从文件中读取数据。这个方法值接收一个参数,即要读取的文件名,然后系统会自动到/data/data//files/目录下去加载这个文件,并返回一个FileInputStream对象,得到了这个对象之后再通过Java流的方式就可以将数据读取出来了。
实例:

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

SharedPreferences存储

不同于文件的存储方式,SharedPreferences是使用键值对的方式来存储数据的。SharedPreferences还支持多种不同的数据类型存储,如果存储的数据类型是整型,那么读取出来的数据也是整型的,存储的数据时一个字符串,读取出来的数据仍是字符串。使用XML格式来对数据进行管理的。

将数据存储到SharedPreferences中

要想使用SharedPreferences来存储数据,首先需要获取到SharedPreferences对象。Android中主要提供了三种方法用于得到SharedPreferences对象。

获取SharedPreferences对象

1、Context类中的getSharedPreferences()方法

此方法接收两个参数,第一个参数用于指定SharedPreferences文件的名称,如果指定的文件不存在则会创建一个,SharedPreferences文件都是存储在/data/data/<package name>/shared_prefs/目录下的。第二个参数用于指定操作模式,主要有两种模式可以选择,MODE_PRIVATE和MODE_MULTI_PROCESS.MODE_PRIVATE仍然是默认的操作模式,和直接传入0效果是相同的,表示只有当前的应用程序才可以对这个SharedPreferences文件进行读写。MODE_MULTI_PROCESS则一般是用于会有多个进程中对同一个SharedPreferences文件进行读写的情况。

2、 Activity类中的 getPreferences()方法

这个方法和Context中的getSharedPreferences()方法很相似,不过它只接收一个操作模式参数,因为使用这个方法会自动将当前活动的类名作为SharedPreferences的文件名。  

3、PreferenceManager类中的 getDefaultSharedPreferences()方法

这是一个静态方法,它接收一个Context参数,并自动使用当前应用程序的包名作为前缀来命名SharedPreferences文件。
向SharedPreferences文件中存储数据

1、 调用SharedPerferences对象的edit()方法来获取一个SharedPreferences.Editor对象。
2、 向SharedPreferences.Editor对象中添加数据。
3、 调用commit()方法将添加的数据提交,从而完成数据存储操作。

实例:
SharedPreferences.Editor editor=getSharedPreferences("data",MODE_PRIVATE).edit();
editor.putString("name","Tom");
editor.commit();
从SharedPreferences中读取数据
SharedPreferences pref=getSharedPreferences("data",MODE_PRIVATE);
String name =pref.getString("name","");  //name 为键,""为默认值。如果没有找到相应的值就会使用方法中传入的默认值来代替。
注:
SharedPreferences.Editor editor=getSharedPreferences().editor();
editor.clear();  //将SharedPreferences文件中的数据全部清除掉。

将密码以明文的形式存储在SharedPreferences文件中是非常不安全的,很容易就会被别人盗取,因此在正式的项目里还需要结合一定的加密算法来对密码进行保护才行。

SQLite数据库存储

SQLite 是一款轻量级的关系型数据库,它的运算速度非常快,
占用资源很少,通常只需要几百K 的内存就足够了,因而特别适合在移动设备上使用。SQLite不仅支持标准的SQL 语法,还遵循了数据库的ACID 事务,所以只要你以前使用过其他的关系型数据库,就可以很快地上手SQLite。

Android 为了让我们能够更加方便地管理数据库,专门提供了一个SQLiteOpenHelper 帮助类,借助这个类就可以非常简单地对数据库进行创建和升级。SQLiteOpenHelper 是一个抽象类,这意味着如果我们想要使用它的话,就需要创建一个自己的帮助类去继承它。SQLiteOpenHelper 中有两个抽象方法,分别是onCreate()和onUpgrade(),我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑。

SQLiteOpenHelper 中还有两个非常重要的实例方法, getReadableDatabase() 和getWritableDatabase()。这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法则将出现异常。

SQLiteOpenHelper 中有两个构造方法可供重写,一般使用参数少一点的那个构造方法即可。这个构造方法中接收四个参数,第一个参数是Context。第二个参数是数据库名,创建数据库时使用的就是这里指定的名称。第三个参数允许我们在查询数据的时候返回一个自定义的Cursor,一般都是传入null。第四个参数表示当前数据库的版本号, 可用于对数据库进行升级操作。

构建出SQLiteOpenHelper 的实例之后,再调用它的getReadableDatabase()或getWritableDatabase()方法就能够创建数据库了,数据库文件会存放在/data/data//databases/目录下。此时,重写的onCreate()方法也会得到执行,所以通常会在这里去处理一些创建表的逻辑。

创建表:

create table Book (
    id integer primary key autoincrement,
    author text,
    price real,   //real 表示浮点型  blob表示二进制类型
    pages integer,
    name text)

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 context;

    public MyDatabaseHelper(Context context,String name,CursorFactory cursorFactory,int version){
        super(context,name,cursorFactory,version);
        mContext = context;
    }

    public void onCreate(SQLiteDatabase db){
        db.execSQL(CREATE_BOOK);
        Toast.makeText(mContext,"Create succeeded",Toast.LENGTH_SHORT).show();
    }

    public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion){
        //数据库更新操作
    }
}

public class MainActivity extends Activity{
    private MyDatabaseHelper dbHelper;
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dbHelper=new MyDatabaseHelper(this,"BookStore.db",null,1); //数据库版本号1
        Button createDatabase =(Button)findViewById(R.id.create_database);
        createDatabase.setOnClickListener(new OnClickListener(){
            public void onClick(View view){
                dbHelper.getWritableDatabase();  //检测到当前程序中并没有BookStore.db这个数据库,于是会创建该数据库并调用MyDatabaseHelper中的onCreate()方法,这样Book表也就得到了创建。
            }
        });
    }
}

数据库版本的更新

public void onCreate(SQLiteDatabase db){
    db.execSQL(CREATE_BOOK);
    db.execSQL(CREATE_CATEGORY);
}
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()方法去重新创建。这里先将已经存在的表删除掉,是因为如果子啊创建表时发现这张表已经存在了,就会直接报错。

接下来的问题就是如何让onUpgrade()方法能够执行了,SQLiteOpenHelper的构造方法里接收的第四个参数就是用来对数据库版本的更新操作的,它当前的数据库版本是1,现在只要传入一个比1大的数,就可以让onUpgrade()方法得到执行了。

插入数据 insert

SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("name","Tony");
values.put("age",13);
db.insert("Book",null,values);  
values.clear();  //清空数据
values.put("name","lili");
values.put("age",23);
db.insert("Book",null,values);
//第一个参数是表名,第二个参数是用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传入null即可。第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据。

更新数据 update

SQLiteDatabase db=dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put("price",56);
db.update("Book",values,"name=?",new String[]{"Tony"});
//第三个参数对应的是SQL语句的where部分,表示去更新所有name等于?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串数组为每个占位符指定相应的内容。

删除数据

SQLiteDatabase db =dbHelper.getWritableDatabase();
db.delete("Book","page>?",new String[]{"500”});
//第二、第三个参数又是用于去约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。

查询数据

SQLiteDatabase中还提供了一个query()方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要传入七个参数。

第一个参数:表名
第二个参数:用于指定去查询哪几列 如果不指定则默认查询所有列
第三、四个参数:用于去约束查询某一列或某几行的数据,不指定则默认是查询所有行的数据。
第五个参数:用于指定需要去group by的列,不指定则表示不对查询结果进行group by操作
第六个参数:用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤。
第七个参数:用于指定查血结果的排序方法,不指定则表示使用默认的排序方式。

实例:
    SQLiteDatabase db=dbHelper.getWritableDatabase();
    Cursor cursor = db.query("Book",null,null,null,null,null,null);

使用SQL语句

添加数据
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" });
更新数据
db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99","The Da Vinci Code" });
删除数据
db.execSQL("delete from Book where pages > ?", new String[] { "500" });
查询数据
db.rawQuery("select * from Book", null);

使用事务

SQLite数据库是支持事务的,事务的特性可以保证让某一系列的操作要么全部完成,要么一个都不会完成。例如更新数据库文件时,要保证删除数据和添加数据的操作必须一起完成,否则就还有继续保留原来的旧数据。

使用事务:
    SQLiteDatabase db=dbHelper.getWritableDatabase();
    db.beginTransaction(); //开启事务
    try{
        db.delete("Book",null,null);
        if(true){
            throw new NullPointerException();
        }
        ContentValues values=new ContentValues();
        values.put("naem","Tomi");
        values.put("age",23);
        db.insert("Book",null,values);
        db.setTransactionSuccessful(); //事务已经执行成功
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        db.endTransaction();  //结束事务
    }

实际开发中的版本更新

public class MyDatabaseHelper extends SQLiteDatabaseHelper{

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

    public MyDatabaseHelper(Context context,String name,CursorFactory factory,int version){
        super(context,name,factory,version);
    }

    public void onCreate(SQLiteDatabase db){
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
    }

    public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion){
        switch(oldVersion){
            case 1:
                db.execSQL(CREATE_CATEGORY);  //新添加一个表
            default:
        }
    }
}
增加表字段
public class MyDatabaseHelper extends SQLiteDatabaseHelper{

    public static final String CREATE_BOOK="create table Book ("+"id integer primary key autoincrement, "+"author text, "+"price real, "+"pages integer, "+"name text, "+"category_id integer)";

    public static final String CREATE_CATEGORY="create table Category ("+"id integer primary key autoincrement, "+"category_name text, "+"category_code integer)";

    public MyDatabaseHelper(Context context,String name,CursorFactory factory,int version){
        super(context,name,factory,version);
    }

    public void onCreate(SQLiteDatabase db){
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
    }

    public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion){
        switch(oldVersion){
            case 1:
                db.execSQL(CREATE_CATEGORY);  //新添加一个表
            case 2;
                db.execSQL("alter table Book add column category_id integer");
            default:
        }
    }
}

注:
这里请注意一个非常重要的细节,switch中每一个case的最后都是没有使用break的,为什么要这么做呢?这是为了保证跨版本升级的时候,每次的数据库修改都能被全部执行到。比如用户当前是从第二版本升级到第三级版本程序的,那么case 2中的逻辑就会执行。而如果用户直接从第一版程序升级到第三版程序的,那么case 1和case 2中的逻辑都会执行。使用这种方式来维护数据库的升级,不管版本怎么更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值