学习笔记(八)数据存储

  Android 系统主要提供了三种方式用于简单地实现数据持久化功能,即文件存储、SharedPreference 存储以及数据库存储。

一. 文件存储

  文件存储不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中的,因此比较适合用于存储一些简单的文本数据或二进制数据。

1. 存储

(1)首先通过openFileOutput()方法得到一个 FileOutputStream 对象,该方法要传入两个参数,文件名和写入方式 Content.MODE_PRIVATE、Content.MODE_APPEND;

(2)然后借助 FileOutputStream 对象,构建出一个 OutputStreamWriter 对象;

(3)再借助 OutputStreamWriter 对象,构建出一个 BufferedWriter 对象;

(4)最后可以通过 BufferedWriter 对象的write()方法将文本内容写入到文件中了;

(5)写入完毕要调用close()关闭 BufferedWriter 对象。

private EditText edit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edit = (EditText) findViewById(R.id.edit_text);
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();
        String inputText = edit.getText().toString();
        save(inputText);//退出前保存输入的文本
    }

    public void save(String inputText){
        FileOutputStream fileOutputStream = null;
        BufferedWriter bufferedWriter = null;

        try{
            fileOutputStream = openFileOutput("data",Context.MODE_PRIVATE);//该方法返回一个FileOutputStream对象
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);//传入fileOutputStream得到一个OutputStreamWriter对象
            bufferedWriter = new BufferedWriter(outputStreamWriter);//传入outputStreamWriter得到一个BufferedWriter对象
            bufferedWriter.write(inputText);//将文本写入文件
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try{
                if(bufferedWriter != null){
                    bufferedWriter.close();//关闭BufferedWriter
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

2. 读取

(1)首先通过openFileInput()方法得到一个 FileInputStream 对象,该方法只要传入一个参数,文件名;

(2)然后借助 FileInputStream 对象,构建出一个 InputStreamReader 对象;

(3)再借助 InputStreamReader 对象,构建出一个 BufferedReader 对象;

(4)最后可以通过 BufferedReader 对象的readLine()方法读取文件中的文本;

(5)读取完毕要调用close()关闭 BufferedReader 对象。

private EditText edit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        edit = (EditText) findViewById(R.id.edit_text);
        String inputText = load();//打开程序就恢复数据
        if(!TextUtils.isEmpty(inputText)){//TextUtils.isEmpty(text)可以判断为空字符或null,当传入的参数为上述两种情况都会返回true
            edit.setText(inputText);//读取文本放到EditView上
            edit.setSelection(inputText.length());//将输入光标放置到文本末尾
            Toast.makeText(this,"恢复成功",Toast.LENGTH_SHORT).show();
        }
    }

    public String load(){
        FileInputStream fileInputStream = null;
        BufferedReader bufferedReader = null;
        StringBuilder content = new StringBuilder();
        try{
            fileInputStream = openFileInput("data");
            bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
            String line = "" ;
            while((line = bufferedReader.readLine()) != null){
                content.append(line);//将内容都读取出来并存放在StringBuilder对象中
            }
        }catch(Exception ex){
            ex.printStackTrace();
        }finally {
            try{
                if(bufferedReader != null){
                    bufferedReader.close();
                }
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
        return content.toString();
    }

二. SharedPreferences 存储

  SharedPreferences 是使用键值对的方式来存储数据的。

1. 存储

(1)首先要获取 SharedPreferences 对象,主要有三种方法:

  • Context 类中的getSharedPreferences()方法,它接收两个参数,文件名称和操作模式(MODE_PRIVATE:表示只有当前的应用程序才可以对 SharedPreferences 文件读写; MODE_MULTI_PROCESS:表示多个进程对同一个 SharedPreferences 文件进行读写。)
  • Activity 类中的getPreferences()方法,它接收一个参数,操作模式,它会自动将当前活动的类名作为文件名。
  • PreferenceManager 类中的gerDefaultSharedPreferences()方法,这是一个静态方法,接收一个 Context 参数,并且自动使用当前应用程序的包名作为前缀来命名文件。

(2)调用 SharedPreferences 对象的edit()方法来获取一个 SharedPreferences.Edit 对象。

(3)向 SharedPreferences.Editor 对象中添加数据,putString()putBoolean()等。

(4)调用commit()将添加的数据提交。

private Button saveData;
...
        saveData = (Button) findViewById(R.id.save);
        saveData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
                editor.putString("name", "Tom");
                editor.putInt("age", 28);
                editor.putBoolean("married", false);
                editor.commit();

2. 读取

(1)首先,获取 SharedPreferences 对象;

(2)然后,利用getString()getInt()等获取数据。传入两个参数,一是 key,二是默认值,即如果找不到该 key,则返回该默认值。

private Button loadData;
...
        loadData = (Button) findViewById(R.id.load);
        loadData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SharedPreferences sharedPreferences = getSharedPreferences("data", MODE_PRIVATE);
                String name = sharedPreferences.getString("name", "");
                int age = sharedPreferences.getInt("age", 0);
                boolean married = sharedPreferences.getBoolean("married", false);
                Log.d("2333", "name is " + name);
                Log.d("2333", "age is " + age);
                Log.d("2333", "married is " + married);
            }
        });

三. SQLite

  SQL : Structured Query Language 结构化查询语言

  

  借助 SQLiteOpenHelper 帮助类可以非常简单地对数据库进行创建和升级。SQLiteOpenHelper 是个抽象类,需要重写两个抽象方法,分别是onCreate()onUpgrade();它还有两个非常重要的实例方法,getReadableDatabase()getWritableDatabase(),这两个方法都可以创建或者打开一个现有的数据库(没有的话就新建),并返回一个可对数据库进行读写操作的对象。

  SQLiteOpenHelper 中有两个构造方法可供重写。一般使用参数少一点的方法,该方法接收四个参数:Context,数据库名,Cursor(一般为null)和 version 版本号。

1. 创建数据库 create

(1)新建 MyDatabaseHelper 类继承 SQLiteOpenHelper,并重写onCreate()onUpgrade()和构造方法。

(2)在onCreate()中调用 SQLiteDatebase 的execSQL()方法去执行这条建表语句。

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

    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;

    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);//调用方法去执行这条建表语句
        Toast.makeText(mContext, "创建成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

(3)在 MainActivity 中调用 MyDatabaseHelper 对象的getReadableDatabase()getWritableDatabase()创建出数据库。因为检测到并没有 BookStore.db 这个数据库,于是会创建该数据库并调用 MyDatabaseHelper 中的onCreate()方法,这样 Book 表也得到创建。

private MyDatabaseHelper helper;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        helper = new MyDatabaseHelper(this,"BookStore.db",null,1);
        Button create = (Button) findViewById(R.id.create);
        create.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                helper.getWritableDatabase();
            }
        });
    }

2. 升级数据库 upgrade

注意:

(1)当相同版本的数据库 BookStore 已经存在了,则 MyDatabaseHelper 中的onCreate()方法就不会执行了。

(2)如果创建表时发现该表已经存在,则会直接报错,所以需要将已经存在的表删除。

(3)只有当版本号比原来高,onUpdate()方法才会执行。

helper = new MyDatabaseHelper(this, “BookStore.db”, null, 2);

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
        Toast.makeText(mContext, "创建成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("drop table if exists Book");//两条DROP语句,发现存在Book或Category表,就将它们删除
        db.execSQL("drop table if exists Category");
        onCreate(db);
    }

3. 添加数据 insert

  CRUD:C 代表添加(Create),R 代表查询(Retrieve),U 代表更新(Update),D 代表删除(Delete)。

  SQL 语言:添加数据使用 insert,查询数据使用 select,更新数据使用 update,删除数据使用 delete 。

  

  SQLiteDatabase 中提供了一个insert()方法,这个方法就是专门用于添加数据的,它接收三个参数,第一个表名(向哪添加数据);第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般用不上这个功能,直接传入null;第三个是一个 ContentValues 对象,它提供了一系列的put()方法重载,用于向 ContentValues 中添加数据。

(1)先获取 SQLiteDatabase 对象,然后使用 ContentValues 来对要添加的数据进行组装。

(2)接下来调用insert()方法将数据添加到表中。

        Button add = (Button) findViewById(R.id.add);
        add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = helper.getWritableDatabase();
                ContentValues values = new ContentValues();
            //开始组装第一条数据
                values.put("name","达芬奇");
                values.put("author","Dan Brown");
                values.put("pages",454);
                values.put("price",16.96);
                db.insert("Book",null,values);//插入第一条数据
                values.clear();
            //开始组装第二条数据
                values.put("name","The Lost Symbol");
                values.put("author","Dan Brown");
                values.put("pages",510);
                values.put("price",19.95);
                db.insert("Book",null,values);//插入第二条数据
            }
        });

4. 更新数据 update

  SQLiteDatabase 中的update()方法接收四个参数:第一个表名,指定去更新哪张表的数据;第二个参数是 ContentValues 对象,把更新数据组装进去;第三个、第四个参数用于约束更新某一行或几行中的数据,不指定的话默认更新所有行。

(1)在按钮点击事件里构建一个 ContentValues 对象,并给它指定一组数据。

(2)然后调用update()方法去执行具体的更新操作。

        Button update = (Button) findViewById(R.id.update);
        update.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = helper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("price",10.99);
                db.update("Book",values,"name=?",new String[]{"达芬奇"});
            }//第三个参数对应的是SQL语句的where部分,表示去更新所有name等于?的行
             //?是一个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每一个占位符指定相应的内容
             //因此,上述代码表达的意图是,将名字是 达芬奇 的这本书的价格改成 10.99
        });

5. 删除数据 delete

  SQLiteDatabase 中的delete()方法接收三个参数,第一个参数是表名;第二、第三个参数又是用于去约束删除某一行或几行的数据,不指定的话默认是删除所有行。

        Button delete = (Button) findViewById(R.id.delete);
        delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = helper.getWritableDatabase();
                db.delete("Book","pages > ?",new String[]{"500"});
            }//意思是,删除Book表中页数大于500的数据
        });

6. 查询数据 query

query() 方法参数 对应 SQL 部分 描述
table from table_name 指定查询的表名
columns select column1,column2 指定查询的列名
selection where column = value 指定 where 的约束条件
selectionArgs - 为 where 中的占位符提供具体的值
groupBy group by column 指定需要 group by 的列
having having column = value 对 group by 后的结果进一步约束
orderBy order by column1,column2 指定查询结果的排序方式

  

(1)调用 SQLiteDatabase 的query()方法去查询数据。

(2)查询完得到一个 Cursor 对象,这个对象是指向第一条记录之前的。接着调用它的moveToFirst()方法将数据的指针移动到第一行的位置,然后进入循环,去遍历查询到的每一行数据。在这个循环中通过 Cursor 的getColumnIndex()方法获取到某一列在表中对应的位置索引,然后传入到相应的取值方法中,就可以得到从数据库中读取到的数据了。

        Button query = (Button) findViewById(R.id.query);
        query.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = helper.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("233333","name is " + name);
                        Log.d("233333","author is " + author);
                        Log.d("233333","pages is " + pages);
                        Log.d("233333","price is " + price);
                    }while(cursor.moveToNext());//为false时跳出循环
                }
                cursor.close();
            }
        });

7. 使用 SQL 操作数据库

  除了查询数据是用 SQLiteDatabase 的rawQuery()方法,其他的操作都是调用的execSQL()方法。

  • 添加数据:

    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","达芬奇密码"});

  • 删除数据:

    db.execSQL("delete from Book where pages>?",new String[] {"500"});

  • 查询数据:

    db.rawQuery("select * from Book",null);

查询数据方法:
首先,获取 Cursor:
Cursor cursor=null;
cursor=db.rawQuery("select * from Book", null);
然后,利用 cursor 得到返回的数据:
while(cursor.moveToNext())
{
String author=cursor.getString(cursor.getColumnIndex("author"));
String name=cursor.getString(cursor.getColumnIndex("name"));
double price=cursor.getDouble(cursor.getColumnIndex("price"));
int pages=cursor.getInt(cursor.getColumnIndex("pages"));
Log.i("main", author);
Log.i("main", name);
Log.i("main", price+"");
Log.i("main", pages+"");
}

四. 最佳实践

1. 使用事务

  事务操作可以保证某一系列的操作要么全部完成,要么全部都不完成,保证安全。

  当执行操作出现故障时,事务不会成功,则以上的操作不会完成,数据库不会变化。

(1)开启事务: db.beginTransaction();

(2)执行数据库操作

(3)事务成功:db.setTransactionSuccessful();

(4)结束事务

以下就是 Android 中事务的标准用法:

        Button replace = (Button) findViewById(R.id.replace);
        replace.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = helper.getWritableDatabase();
                db.beginTransaction();//开启事务
                try{
                    db.delete("Book",null,null);
                    if(true){
                        throw new NullPointerException();//手动抛出一个异常,模仿事务失败
                    }
                    ContentValues values = new ContentValues();
                    values.put("name","权力的游戏");
                    values.put("author","George Martin");
                    values.put("pages",720);
                    values.put("price",20.85);
                    db.insert("Book",null,values);
                    db.setTransactionSuccessful();//事务已经执行成功
                }catch(Exception ex){
                    ex.printStackTrace();
                }finally{
                    db.endTransaction();//结束事务
                }
            }
        });

删除掉手动抛出异常那行代码,再重新运行一下程序,此时点击按钮数据就会被替换成新数据了。

2. 升级数据库最佳写法

  前面学习升级数据库的方式太粗暴,为了保证数据库中的表是最新的,就删除掉了当前所有的表,然后又重新创建表,真的很傻。

  其实应该这么做:每一个数据库版本都会对应一个版本号,当指定的数据库版本号大于当前数据库版本号的时候,就会进入到onUpgrade()方法中去执行更新操作。这里需要为每一个版本号赋予它各自改变的内容,然后在onUpgrade()中添加一个 switch 语句,对当前数据库的版本号进行判断,再执行相应的改变。

  注意:switch 中每一个 case 的最后都是没有使用 break 的,这是为了保证在跨版本升级的时候,每一次的数据库修改都能被全部执行到。

  

  databaseHelper = new MyDatabaseHelper(this, "BookStore", null, 1);

比如 app 一共更新了两次,第一次添加了 table Category,第二次在 table Book 中添加 category_id.

于是,就有三个版本:

版本号 数据操作
version = 1 建数据库,添加 table Book
version = 2 添加 table Category
version = 3 在 table Book 中添加 category_id

  

主程序中: databaseHelper = new MyDatabaseHelper(this, "BookStore", null, 3); 

将版本号改为 3:

如果用户是第一次安装,则直接进入onCreate(),直接安装最新版本;

如果用户版本号是 1,则进入onUpgrade(), 连续进行了两次升级;

如果用户版本号是 2,则进入onUpgrade(),只进行了最后一次升级。

时间: 2024-08-29 02:21:25

学习笔记(八)数据存储的相关文章

Android 学习笔记之数据存储SharePreferenced+File

学习内容: Android的数据存储.... 1.使用SharedPreferences来保存和读取数据... 2.使用File中的I/O来完成对数据的存储和读取...   一个应用程序,经常需要与用户之间形成交互...需要保存用户的设置和用户的数据信息...这些都离不开数据的存储...Android的数据采用五种方式来进行存储...在这里就先介绍两种存储方式... 1.使用SharedPreferences存储数据...   对于软件配置参数的保存,Windows系统采用ini文件来进行保存,

android菜鸟学习笔记20----Android数据存储(四))Android数据库操作

Android内置了一个名为SQLite的关系型数据库,这是一款轻量型的数据库,操作十分简便.SQLite与别的数据库不同的是,它没有数据类型.可以保存任何类型的数据到你所想要保存的任何表的任何列中.但它又支持常见的类型比如: NULL, VARCHAR, TEXT, INTEGER, BLOB, CLOB...等. 唯一的例外是:integer primary key 此字段只能存储64位整数. 在JAVA项目中,要使用JDBC操作数据库需要加载数据库驱动,连接数据库等操作.Android简化

Redis学习笔记2--Redis数据存储优化机制

1.zipmap优化hash: 前面谈到将一个对象存储在hash类型中会占用更少的内存,并且可以更方便的存取整个对象.省内存的原因是新建一个hash对象时开始是用zipmap来存储的.这个zipmap其实并不是hash table,但是zipmap相比正常的hash实现可以节省不少hash本身需要的一些元数据存储开销.尽管zipmap的添加,删除,查找都是O(n),但是由于一般对象的field数量都不太多.所以使用zipmap也是很快的,也就是说添加删除平均还是O(1).如果field或者val

android菜鸟学习笔记19----Android数据存储(三)XML文件的解析及序列化

Android内置了PULL解析器的XPP3实现,以及SAX解析器等,可以直接使用PULL或SAX解析XML,直接把JAVA中进行PULL或SAX解析的代码直接拿来用,遗忘的话,可以参考java拾遗1,2,3关于XML的解析: 如有如下XmlUtils类实现PULL方式解析XML到List和序列化List到XML: Student实体类代码: 1 package cn.csc.bean; 2 3 public class Student { 4 5 private String id; 6 7

Lua学习笔记(八):数据结构

table是Lua中唯一的数据结构,其他语言所提供的数据结构,如:arrays.records.lists.queues.sets等,Lua都是通过table来实现,并且在Lua中table很好的实现了这些数据结构. 1.数组 在Lua中通过整数下标访问table中元素,既是数组,并且数组大小不固定,可动态增长.通常我们初始化数组时,就间接地定义了数组的大小,例如: 1 a = {} -- new array 2 for i=1, 1000 do 3 a[i] = 0 4 end 5 6 --数

python 学习笔记 3 -- 数据结构篇上

数据结构是可以处理一些 数据 的 结构 .或者说,它们是用来存储一组相关数据的.在Python中有三种内建的数据结构--列表.元组和字典.本文主要对这三种数据类型以及相关的使用做介绍,以例子的形式演示更加容易理解! 1.列表(List) 列表是处理一组有序项目的数据结构,即你可以在一个列表中存储一个 序列 的项目.在Python中,你在每个项目之间用逗号分割. 列表中的项目应该包括在**方括号**中,这样Python就知道你是在指明一个列表.一旦你创建了一个列表,你可以添加.删除或是搜索列表中的

python 学习笔记 3 -- 数据结构篇下

5.引用 当你创建一个对象并给它赋一个变量的时候,这个变量仅仅 引用 那个对象,而不是表示这个对象本身!也就是说,变量名指向你计算机中存储那个对象的内存.这被称作名称到对象的绑定.eg. [python] view plaincopy # -*- coding: utf-8 -*- shoplist = ['apple', 'mango', 'carrot', 'banana'] print "we copy the shoplist to mylist directly \"with

angular学习笔记(八)

本篇介绍angular控制视图的显示和隐藏: 通过给元素添加ng-show属性或者ng-hide属性来控制视图的显示或隐藏: ng-show: 绑定的数据值为true时,显示元素,值为false时,隐藏元素 ng-hide: 绑定的数据值为true时,隐藏元素,值为false时,显示元素 (其实只要用到其中一个就可以了) 下面来看个简单的例子,点击按钮可以显示/隐藏元素: <!DOCTYPE html> <html ng-app> <head> <title>

Linux System Programming 学习笔记(八) 文件和目录管理

1. 文件和元数据 每个文件都是通过inode引用,每个inode索引节点都具有文件系统中唯一的inode number 一个inode索引节点是存储在Linux文件系统的磁盘介质上的物理对象,也是LInux内核通过数据结构表示的实体 inode存储相关联文件的元数据 ls -i 命令获取文件的inode number /* obtaining the metadata of a file */ #include <sys/types.h> #include <sys/stat.h>

Android开发学习笔记:数据存取之SQLite浅析

一.SQLite的介绍 1.SQLite简介 SQLite是一款轻型的数据库,是遵守ACID的关联式数据库管理系统,它的设计目标是嵌入 式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了.它能够支持 Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如Tcl.PHP.Java.C++..Net等,还有ODBC接口,同样比起 Mysql.PostgreSQL这两款开源世界著名的数据库管理系统来讲,它的