四种存储方式(File sharepreference....)

来源:互联网 发布:大周皇族 知乎 编辑:程序博客网 时间:2024/04/27 19:46

文件存储

 默 认 存 储 到
/data/data/<package name>/files/目录下。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context=".MainActivity" >  <EditText        android:id="@+id/editText1"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_alignParentLeft="true"        android:layout_alignParentTop="true"  /></RelativeLayout>

public class MainActivity extends Activity { EditText edit;     @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                 edit=(EditText) findViewById(R.id.editText1);            }        @Override    protected void onDestroy() {    // TODO Auto-generated method stub    super.onDestroy();    String inputText=edit.getText().toString();        }    public void save(String inputText){        FileOutputStream out=null;    BufferedWriter writer=null;        try {out=openFileOutput("data", Context.MODE_PRIVATE);writer=new BufferedWriter(new OutputStreamWriter(out));writer.write(inputText);} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}finally{try {if (writer!=null) {writer.close();}} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}    }    }

首先我们在 onCreate()方法中获取了 EditText 的实例,然后重写了onDestroy()方法,这样就可以保证在活动销毁之前一定会调用这个方法。在 onDestroy()
方法中我们获取了 EditText 中输入的内容, 并调用 save()方法把输入的内容存储到文件中,文件命名为 data。save()方法中的代码和之前的示例基本相同,这里就不再做解释了。现在重新运行一下程序,并在 Editext 中输入一些内容




然后按下 Back 键关闭程序,这时我们输入的内容就已经保存到文件中了。那么如何才
能证实数据确实已经保存成功了呢?我们可以借助 DDMS 的 File Explorer 来查看一下。
切 换 到 DDMS 视 图 , 并 点 击 File Explorer 切 换 卡 , 在 这 里 进 入 到
/data/data/com.example. filepersistencetest/files/目录下, 可以看到生成了一个data

文件








读取数据


public class MainActivity extends Activity { EditText edit;     @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                 edit=(EditText) findViewById(R.id.editText1);            }          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 (Exception e) {// TODO: handle exception}finally{if (reader!=null) {try {reader.close();} catch (Exception e) {// TODO: handle exception}}}return content.toString();        }       



在 onCreate()方法中调用 load()方法来读取文件中存储的文本内容,如果读到的内容不为空,就调用 EditText 的 setText()方法将内容填充到 EditText 里, 并调用 setSelection 方法将输入光标移动到文本的末尾位置以便于继续输入,然后弹出一句还原成功的提示。




SharedPreferences 存储

SharedPreferences 文 件 都 是 存 放 在/data/data/<package name>/shared_prefs/目录下

SharedPreferences 是使用键值对的方式来存储数据的,。也就是说当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把相应的值取出来。 而且 SharedPreferences 还支持多种不同的数据类型存储,如果存储的数据类型是整型,那么读取出来的数据也是整型的,存储的数据是一个
字符串,读取出来的数据仍然是字符串

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

1.

此方法接收两个参数,第一个参数用于指定 SharedPreferences 文件的名称,如
果 指 定 的 文 件 不 存 在 则 会 创 建 一 个 第二个参数用于指定操作
模式,主要有两种模式可以选择,MODE_PRIVATE 和 MODE_MULTI_PROCESS。
MODE_PRIVATE 仍然是默认的操作模式,和直接传入 0 效果是相同的,表示只有当
前 的 应 用 程 序 才 可 以 对 这 个 SharedPreferences 文 件 进 行 读 写 。
MODE_MULTI_PROCESS 则 一 般 是 用 于 会 有 多 个 进 程 中 对 同 一 个
SharedPreferences 文件进行读写的情况。类似地,MODE_WORLD_READABLE 和
MODE_WORLD_WRITEABLE 这两种模式已在 Android 4.2 版本中被废弃。

2.  Activity 类中的 getPreferences()方法
这个方法和 Context 中的 getSharedPreferences()方法很相似,不过它只接收
一 个 操 作 模 式 参 数 , 因 为 使 用 这 个 方 法 时 会 自 动 将 当 前 活 动 的 类 名 作 为
SharedPreferences 的文件名。
3.  PreferenceManager 类中的 getDefaultSharedPreferences()方法
这是一个静态方法,它接收一个 Context 参数,并自动使用当前应用程序的包名
作为前缀来命名 SharedPreferences 文件。
得到了 SharedPreferences 对象之后, 就可以开始向 SharedPreferences 文件中存
储数据了,主要可以分为三步实现。

1.  调 用 SharedPreferences 对 象 的 edit() 方 法 来 获 取 一 个
SharedPreferences.Editor 对象。
2.  向 SharedPreferences.Editor 对象中添加数据,比如添加一个布尔型数据就使
用 putBoolean 方法,添加一个字符串则使用 putString()方法,以此类推。
3.  调用 commit()方法将添加的数据提交,从而完成数据存储操作。


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context=".MainActivity" >    <Button        android:id="@+id/button1"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentTop="true"        android:layout_centerHorizontal="true"        android:layout_marginTop="54dp"        android:text="Button" /></RelativeLayout>


public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                Button btn=(Button) findViewById(R.id.button1);        btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubSharedPreferences.Editor editor=getSharedPreferences("data", MODE_PRIVATE).edit();editor.putString("name", "Tom");editor.putInt("age", 28);editor.putBoolean("married", false);editor.commit();}});    }



从 SharedPreferences 中读取数据


 btn1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubSharedPreferences pref=getSharedPreferences("data", MODE_PRIVATE);String name=pref.getString("name", "");int age=pref.getInt("age", 0);boolean married=pref.getBoolean("married", false);Log.i("MainActivity", "name is"+name);Log.i("MainActivity", "age is " + age);Log.i("MainActivity", "married is " + married);}});



可以看到, 我们在还原数据按钮的点击事件中首先通过 getSharedPreferences()方法得 到 了 SharedPreferences 对 象 , 然 后 分 别 调 用 它 的 getString() 、 getInt() 和getBoolean()方法去获取前面所存储的姓名、年龄和是否已婚,如果没有找到相应的值就会使用方法中传入的默认值来代替



实现记住密码功能


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".MainActivity" >    <EditText        android:id="@+id/editText1"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_alignParentLeft="true"        android:layout_alignParentTop="true"       >    </EditText>    <EditText        android:id="@+id/editText2"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_alignParentLeft="true"        android:layout_below="@+id/editText1"        android:layout_marginTop="15dp"  />    <CheckBox        android:id="@+id/checkBox1"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentLeft="true"        android:layout_below="@+id/editText2"        android:layout_marginTop="21dp"        android:text="记住密码" />    <Button        android:id="@+id/button1"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_alignRight="@+id/editText2"        android:layout_below="@+id/checkBox1"        android:layout_marginTop="28dp"        android:text="Button" /></RelativeLayout>



public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                        final SharedPreferences pref=PreferenceManager.getDefaultSharedPreferences(this);                final EditText edit1=(EditText) findViewById(R.id.editText1);        final EditText edit2=(EditText) findViewById(R.id.editText2);        final CheckBox box=(CheckBox) findViewById(R.id.checkBox1);        Button btn=(Button) findViewById(R.id.button1);        boolean isRemember=pref.getBoolean("remember_password", false);        if (isRemember) {        // 将账号和密码都设置到文本框中String account=pref.getString("account", "");String passwd=pref.getString("password", "");    edit1.setText(account);    edit2.setText(passwd);    box.setChecked(true);}        btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubString account=edit1.getText().toString();String passwd=edit2.getText().toString();if (account.equals("zs")&&passwd.equals("123456")) {SharedPreferences.Editor editor=pref.edit();if (box.isChecked()) {editor.putBoolean("remember_password", true);editor.putString("account", account);editor.putString("password", passwd);}else {editor.clear();}editor.commit();Intent intent=new Intent(MainActivity.this,SecondActivity.class);startActivity(intent);finish();}else {Toast.makeText(MainActivity.this, "account or password is invalid", Toast.LENGTH_SHORT).show();}}});    }

可以看到,这里首先在 onCreate()方法中获取到了 SharedPreferences 对象,然后调用它的 getBoolean()方法去获取 remember_password 这个键对应的值,一开始当然不存在对应的值了,所以会使用默认值 false,这样就什么都不会发生。接着在登录成功之后,会调用 CheckBox 的 isChecked()方法来检查复选框是否被选中,如果被选中了表示用户想要记住密码,这时将 remember_password 设置为 true,然后把 account 和password 对应的值都存入到 SharedPreferences 文件当中并提交。如果没有被选中,就简单地调用一下 clear()方法,将 SharedPreferences 文件中的数据全部清除掉。当用户选中了记住密码复选框,并成功登录一次之后,remember_password 键对应的值就是 true 了,这个时候如果再重新启动登录界面,就会从 SharedPreferences 文件中将保存的账号和密码都读取出来,并填充到文本输入框中,然后把记住密码复选框选中,这样就完成记住密码的功能了


SQLite 数据库存储


 创建数据库

 数 据 库 文 件 会 存 放 在
/data/data/<package name>/databases/目录下

首先你要知道 SQLiteOpenHelper 是一个抽象类, 这意味着如果我们想要使用它的话,就需要创建一个自己的帮助类去继承它。SQLiteOpenHelper 中有两个抽象方法,分别是onCreate()和 onUpgrade(),我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑。SQLiteOpenHelper 中还有两个非常重要的实例方法,getReadableDatabase()和getWritableDatabase()。这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库) ,并返回一个可对数据库进行读写操作的对象。不同的是,当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而 getWritableDatabase()方法则将出现异这里我们希望创建一个名为 BookStore.db 的数据库,然后在这个数据库中新建一张Book 表,表中有 id(主键) 、作者、价格、页数和书名等列。创建数据库表当然还是需要用建表语句的,这里也是要考验一下你的 SQL 基本功了,Book 表的建表语句如下所示


create table Book (id integer primary key autoincrement,author text,price real,pages integer,name text)
integer 表示整型,real 表示浮点型,text 表示文本类型,blob 表示二进制类型。另外,上述建表语句中我们还使用了 primary key 将 id 列设为主键,并用 autoincrement 关键字表示 id 列是自增长的。

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)";Context mcontext;public MyDatabaseHelper(Context context, String name,CursorFactory factory, int version) {super(context, name, factory, version);// TODO Auto-generated constructor stubmcontext=context;}@Overridepublic void onCreate(SQLiteDatabase arg0) {// TODO Auto-generated method stubarg0.execSQL(CREATE_BOOK);Toast.makeText(mcontext, "create succeeded", Toast.LENGTH_SHORT).show();}@Overridepublic void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {// TODO Auto-generated method stub}}
我们把建表语句定义成了一个字符串常量,然后在 onCreate()方法中又调用了 SQLiteDatabase 的 execSQL()方法去执行这条建表语句, 并弹出一个 Toast 提示创建成功,这样就可以保证在数据库创建完成的同时还能成功创建 Book 表,

修改activity_main。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context=".MainActivity" >    <Button        android:id="@+id/button1"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentLeft="true"        android:layout_alignParentTop="true"        android:layout_marginTop="39dp"        android:text="Create database" /></RelativeLayout>


布局文件很简单,就是加入了一个按钮,用于创建数据库。最后修改 MainActivity 中的代码

public class MainActivity extends Activity {private MyDatabaseHelper dbHelper;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        dbHelper=new MyDatabaseHelper(this, "bookstore.db", null, 1);        Button btn=(Button) findViewById(R.id.button1);        btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubdbHelper.getWritableDatabase();}});    }

在 onCreate()方法中构建了一个 MyDatabaseHelper 对象,并且通过构造函数的参数将数据库名指定为 BookStore.db, 版本号指定为 1, 然后在 Create database按钮的点击事件里调用了 getWritableDatabase()方法。这样当第一次点击 Createdatabase 按钮时, 就会检测到当前程序中并没有 BookStore.db 这个数据库, 于是会创建该数据库并调用 MyDatabaseHelper 中的 onCreate()方法,这样 Book 表也就得到了创建,然后会弹出一个 Toast 提示创建成功。再次点击 Create database 按钮时,会发现此时已经存在 BookStore.db 数据库了,因此不会再创建一次。此时 BookStore.db 数据库和 Book 表应该都已经创建成功了,因为当你再次点击

Create database 按钮时不会再有 Toast 弹出。可是又回到了之前的那个老问题,怎样才能证实它们的确是创建成功了?如果还是使用 File Explorer,那么最多你只能看到
databases 目录下出现了一个 BookStore.db 文件, Book 表是无法通过 File Explorer 看到的。因此这次我们准备换一种查看方式,使用 adb shell 来对数据库和表的创建情进行检查。adb 是 Android SDK 中自带的一个调试工具, 使用这个工具可以直接对连接在电脑上的手机或模拟器进行调试操作。它存放在 sdk 的 platform-tools 目录下,如果想要在命令行中使用这个工具,就需要先把它的路径配置到环境变量里。如果你使用的是 Windows 系统,可以右击我的电脑→属性→高级→环境变量,然后在系统变量里找到 Path 并点击编辑,将 platform-tools 目录配置进去




如果你使用的是 Linux 系统,可以在 home 路径下编辑.bash_profile 文件,将platform-tools 目录配置进去即可




配置好了环境变量之后,就可以使用 adb 工具了。打开命令行界面,输入 adb shell,就会进入到设备的控制台



然后使用 cd 命令进行到/data/data/com.example.databasetest/databases/目录下,并使用 ls 命令查看到该目录里的文件



这个目录下出现了两个数据库文件,一个正是我们创建的 BookStore.db,而另一个BookStore.db-journal 则是为了让数据库能够支持事务而产生的临时日志文件,通常情况下这个文件的大小都是 0 字节。接下来我们就要借助 sqlite 命令来打开数据库了,只需要键入 sqlite3,后面加上数据库名即可




这时就已经打开了 BookStore.db 数据库,现在就可以对这个数据库中的表进行管理了。首先来看一下目前数据库中有哪些表,键入.table 命令


可以看到,此时数据库中有两张表,android_metadata 表是每个数据库中都会自动生成的,不用管它,而另外一张 Book 表就是我们在 MyDatabaseHelper 中创建的了还可以通过.schema 命令来查看它们的建表语句


由此证明,BookStore.db 数据库和 Book 表确实已经是创建成功了。之后键入.exit或.quit 命令可以退出数据库的编辑,再键入 exit 命令就可以退出设备控制台了


升级数据库


目前 DatabaseTest 项目中已经有一张 Book 表用于存放书的各种详细数据,如果我们想再添加一张 Category 表用于记录书籍的分类该怎么做呢?

package com.example.database;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteDatabase.CursorFactory;import android.database.sqlite.SQLiteOpenHelper;import android.widget.Toast;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)";public static final String CREATE_CATEGORY="create table Category ("+"id integer primary key autoincrement,"+"category_name text," +"category_code integer)";Context mcontext;public MyDatabaseHelper(Context context, String name,CursorFactory factory, int version) {super(context, name, factory, version);// TODO Auto-generated constructor stubmcontext=context;}@Overridepublic void onCreate(SQLiteDatabase arg0) {// TODO Auto-generated method stubarg0.execSQL(CREATE_BOOK);arg0.execSQL(CREATE_CATEGORY);Toast.makeText(mcontext, "create succeeded", Toast.LENGTH_SHORT).show();}@Overridepublic void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {// TODO Auto-generated method stub}}

看上去好像都挺对的吧,现在我们重新运行一下程序,并点击 Create database 按钮,咦?竟然没有弹出创建成功的提示。当然,你也可以通过 adb 工具到数据库中再去检查一下,这样你会更加地确认,Category 表没有创建成功!其实没有创建成功的原因不难思考,因为此时 BookStore.db 数据库已经存在了,之后不管我们怎样点击 Create database 按钮,MyDatabaseHelper 中的 onCreate()方法都不会再次执行,因此新添加的表也就无法得到创建了。解决这个问题的办法也相当简单,只需要先将程序卸载掉,然后重新运行,这时BookStore.db 数 据 库 已 经 不 存 在 了 , 如 果 再 点 击 Create database 按 钮 ,MyDatabaseHelper 中的 onCreate()方法就会执行,这时 Category 表就可以创建成功了。不过通过卸载程序的方式来新增一张表毫无疑问是很极端的做法, 其实我们只需要巧妙地 运 用 SQLiteOpenHelper 的 升 级 功 能 就 可 以 很 轻 松 地 解 决 这 个 问 题


package com.example.database;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteDatabase.CursorFactory;import android.database.sqlite.SQLiteOpenHelper;import android.widget.Toast;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)";public static final String CREATE_CATEGORY = "create table Category ("+ "id integer primary key autoincrement," + "category_name text,"+ "category_code integer)";Context mcontext;public MyDatabaseHelper(Context context, String name,CursorFactory factory, int version) {super(context, name, factory, version);// TODO Auto-generated constructor stubmcontext = context;}@Overridepublic void onCreate(SQLiteDatabase arg0) {// TODO Auto-generated method stubarg0.execSQL(CREATE_BOOK);arg0.execSQL(CREATE_CATEGORY);Toast.makeText(mcontext, "create succeeded", Toast.LENGTH_SHORT).show();}@Overridepublic void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {// TODO Auto-generated method stubarg0.execSQL("drop table if exists Book");arg0.execSQL("drop table if exists Category");onCreate(arg0);}}


可以看到,我们在 onUpgrade()方法中执行了两条 DROP 语句,如果发现数据库中已经存在 Book 表或 Category 表了,就将这两张表删除掉,然后再调用 onCreate()方法去
重新创建。这里先将已经存在的表删除掉,是因为如果在创建表时发现这张表已经存在了,就会直接报错。接下来的问题就是如何让 onUpgrade()方法能够执行了, 还记得 SQLiteOpenHelper的构造方法里接收的第四个参数吗?它表示当前数据库的版本号,之前我们传入的是 1,现在只要传入一个比 1 大的数,就可以让 onUpgrade()方法得到执行了

    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        dbHelper=new MyDatabaseHelper(this, "bookstore.db", null, 2);        Button btn=(Button) findViewById(R.id.button1);        btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubdbHelper.getWritableDatabase();}});    }


现在你已经掌握了创建和升级数据库的方法, 接下来就该学习一下如何对表中的数据进行操作了。其实我们可以对数据进行的操作也就无非四种,即 CRUD。其中 C 代表添加
(Create) ,R 代表查询(Retrieve) ,U 代表更新(Update) ,D 代表删除(Delete) 。每一种操作又各自对应了一种 SQL 命令, 如果你比较熟悉 SQL 语言的话, 一定会知道添加数据时使用 insert,查询数据时使用 select,更新数据时使用 update,删除数据时使用delete。


        Button btn1=(Button) findViewById(R.id.add);        btn1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubSQLiteDatabase db=dbHelper.getWritableDatabase();ContentValues values=new ContentValues();//第一条数据values.put("name", "zs");values.put("author", "ZZZ");values.put("pages", 453);values.put("price", 16.96);db.insert("Book", null, values);values.clear();//第二条数据values.put("name", "zas");values.put("author", "AA");values.put("pages", 42);values.put("price", 16.96);db.insert("Book", null, values);}});    }


在添加数据按钮的点击事件里面,我们先获取到了 SQLiteDatabase 对象,然后使用ContentValues 来对要添加的数据进行组装。如果你比较细心的话应该会发现,这里只对
Book 表里其中四列的数据进行了组装,id 那一列没并没给它赋值。这是因为在前面创建表的时候我们就将 id 列设置为自增长了,它的值会在入库的时候自动生成,所以不需要手动给它赋值了。接下来调用了 insert()方法将数据添加到表当中,注意这里我们实际上添加了两条数据,上述代码中使用 ContentValues 分别组装了两次不同的内容,并调用了两次insert()方法。

打开 BookStore.db 数据库瞧一瞧。输入 SQL 查询语句 select * from Book



更新数据

 Button btn1=(Button) findViewById(R.id.add);        btn1.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View arg0) {// TODO Auto-generated method stubSQLiteDatabase db=dbHelper.getWritableDatabase();ContentValues values=new ContentValues();values.put("price", 10.9);db.update("Book", values, "name=?", new String[]{"The Da VinciCode"});}});    }

里在更新数据按钮的点击事件里面构建了一个 ContentValues 对象,并且只给它指定了一组数据,说明我们只是想把价格这一列的数据更新成 10.99。然后调用了
SQLiteDatabase 的 update()方法去执行具体的更新操作,可以看到,这里使用了第三、第四个参数来指定具体更新哪几行。第三个参数对应的是 SQL 语句的 where 部分,表示去更新所有 name 等于?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串组为第三个参数中的每个占位符指定相应的内容。因此上述代码想表达的意图就是,将名字是 The Da Vinci Code 的这本书的价格改成 10.99

 删除数据


deleteButton.setOnClickListener(new OnClickListener() {    @Override     public void onClick(View v) {         SQLiteDatabase db = dbHelper.getWritableDatabase();         db.delete("Book", "pages > ?", new String[] { "500" });   } });


可以看到, 我们在删除按钮的点击事件里指明去删除 Book 表中的数据, 并且通过第二、第三个参数来指定仅删除那些页数超过 500 页的书籍。当然这个需求很奇怪,这里也仅仅是为了做个测试。你可以先查看一下当前 Book 表里的数据,其中 The Lost Symbol 这本书的页数超过了 500 页,也就是说当我们点击删除按钮时,这条记录应该会被删除掉


查询数据




SQLiteDatabase db = dbHelper.getWritableDatabase();// 查询Book表中所有的数据  Cursor cursor = db.query("Book", null, null, null, null, null, null);   if (cursor.moveToFirst()) {      do {        // 遍历Cursor对象,取出数据并打印       String name = cursor.getString(cursor.       getColumnIndex("name"));       String author = cursor.getString(cursor.       getColumnIndex("author"));       int pages = cursor.getInt(cursor.getColumnIndex ("pages"));       double price = cursor.getDouble(cursor.       getColumnIndex("price"));      Log.d("MainActivity", "book name is " + name);      Log.d("MainActivity", "book author is " + author);      Log.d("MainActivity", "book pages is " + pages);       Log.d("MainActivity", "book price is " + price);     }  while (cursor.moveToNext()); } cursor.close();


的 query()方法非常简单,只是使用了第一个参数指明去查询 Book表,后面的参数全部为 null。这就表示希望查询这张表中的所有数据,虽然这张表中目前只剩下一条数据了。 查询完之后就得到了一个Cursor对象, 接着我们调用它的moveToFirst()方法将数据的指针移动到第一行的位置,然后进入了一个循环当中,去遍历查询到的每一行数据。在这个循环中可以通过 Cursor 的 getColumnIndex()方法获取到某一列在表中对应的位置索引,然后将这个索引传入到相应的取值方法中,就可以得到从数据库中读取到的数据了。接着我们使用 Log 的方式将取出的数据打印出来,借此来检查一下读取工作有没有成功完成。最后别忘了调用 close()方法来关闭 Cursor。


使用 SQL 操作数据库

添加数据的方法如下:
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" });
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
new String[] { "The Lost Symbol", "Dan Brown", "510", "19.95" });
更新数据的方法如下:
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);
可以看到, 除了查询数据的时候调用的是 SQLiteDatabase 的 rawQuery()方法, 其他
的操作都是调用的 execSQL()方法。以上演示的几种方式,执行结果会和前面几小节中我
们学习的 CRUD 操作的结果完全相同,选择使用哪一种方式就看你个人的喜好了


Button replaceData = (Button) findViewById(R.id.replace_data);replaceData.setOnClickListener(new OnClickListener() {       @Override        public void onClick(View v) {        SQLiteDatabase db = dbHelper.getWritableDatabase();        db.beginTransaction(); // 开启事务       try {         db.delete("Book", null, null);         if (true) {         // 在这里手动抛出一个异常,让事务失败           throw new NullPointerException();        }          ContentValues values = new ContentValues();          values.put("name", "Game of Thrones");          values.put("author", "George Martin");          values.put("pages", 720);          values.put("price", 20.85);          db.insert("Book", null, values);          db.setTransactionSuccessful(); // 事务已经执行成功         } catch (Exception e) {        e.printStackTrace();     } finally {        db.endTransaction(); // 结束事务     }   }  }); }}

上 述 代 码 就 是 Android 中 事 务 的 标 准 用 法 , 首 先 调 用 SQLiteDatabase 的beginTransaction()方法来开启一个事务,然后在一个异常捕获的代码块中去执行具体的数据库操作,当所有的操作都完成之后,调用 setTransactionSuccessful()表示事务已经执行成功了,最后在 finally 代码块中调用 endTransaction()来结束事务。
在删除旧数据的操作完成后手动抛出了一个 NullPointerException,这样添加新数据的代码就执行不到了。不过由于事务的存在,中途出现异常会导致事务的失败,此时旧数据应该是删除不掉的。现在可以运行一下程序并点击 Replace data 按钮,你会发现,Book 表中存在的还是之前的旧数据。然后将手动抛出异常的那行代码去除,再重新运行一下程序,此时点击一下
Replace data 按钮就会将 Book 表中的数据替换成新数据了

@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  switch (oldVersion) {   case 1:   db.execSQL(CREATE_CATEGORY);   default:}}
 onCreate()方法里我们新增了一条建表语句,然后又在 onUpgrade()方法中添加了一个 switch 判断,如果用户当前数据库的版本号是 1,就只会创建一张Category 表。这样当用户是直接安装的第二版的程序时,就会将两张表一起创建。而当用户是使用第二版的程序覆盖安装第一版的程序时,就会进入到升级数据库的操作中,此时由于 Book 表已经存在了,因此只需要创建一张 Category 表即可。但是没过多久,新的需求又来了,这次要给 Book 表和 Category 表之间建立关联,需要在 Book 表中添加一个 category_id 的字段。再次修改 MyDatabaseHelper 中的代码

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:  }}
可以看到,首先我们在 Book 表的建表语句中添加了一个 category_id 列,这样当用户直接安装第三版的程序时,这个新增的列就已经自动添加成功了。然而,如果用户之前已
经安装了某一版本的程序,现在需要覆盖安装,就会进入到升级数据库的操作中。在onUpgrade()方法里,我们添加了一个新的 case,如果当前数据库的版本号是 2,就会执行 alter 命令来为 Book 表新增一个 category_id 列。这里请注意一个非常重要的细节,switch 中每一个 case 的最后都是没有使用 break
的,为什么要这么做呢?这是为了保证在跨版本升级的时候,每一次的数据库修改都能被全部执行到。比如用户当前是从第二版程序升级到第三版程序的,那么 case 2 中的辑就会执行。而如果用户是直接从第一版程序升级到第三版程序的,那么 case 1 和 case 2 中的逻辑都会执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失了。




0 0
原创粉丝点击