Android学习笔记——持久化存储

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

文件存储——最基本的一种存储方式

作为最基本的存储方式,不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中。
适用:简单的文本数据或二进制数据
默认文件存放目录:所有文件默认存储到/data/data//files/

将数据存储到文件中

openFileOutput()方法:由Context类提供。将数据存储到指定文件。
两个参数:

  • 参数一:文件名,文件创建时候使用的名字,不包括路径名(所有文件默认存储到/data/data//files/目录下);
  • 参数二:文件的操作模式,主要有两种可选:
    MODE_PRIVATE:默认的操作模式,覆盖原有内容;
    MODE_APPEND:如果文件已存在,向文件中追加内容;不存在就创建文件。
    (Android4.2后已将MODE_WORLD_READABLE、MODE_WORLD_WRITEABLE两种危险的模式废弃)

返回:返回一个FileOutputStream对象,得到这个对象后就可以使用java流的方式将数据写入到文件中。

从文件中读取数据

openFileInput()方法:同样Context类提供。只接收一个参数:要读取的文件名。系统会自动在默认目录下去加载这个文件。
返回:返回一个FileInputStream对象,得到对象后可以通过Java流的方式将数据读取出来。

实例

P202及之前几页

SharedPreferences存储

使用键值对的方式来存储数据。支持多种不同的数据类型存储。存储的数据是什么类型,读出仍然一样。显然更方便。
默认SharedPreferences文件存放目录:/data/data//shared_prefs/目录下

将数据存储到SharedPreferences

首先获取SharedPreferences对象。三种方法:

  • Context类中的getSharedPreferences()方法
    接收两个参数:参数一指定SharedPreferences文件名称,如果指定文件不存在则会创建一个;参数二指定操作模式,目前只有MODE_PRIVATE一种模式可选,也是默认。
  • Activity类中的getPreferences()方法
    getSharedPreferences()方法相似,只不过只接收一个参数,为操作模式。会将当前活动的类名作为SharedPreferences的文件名。
  • PreferenceManager类中的getDefaultSharedPreferences()方法
    这是一个静态方法,接收一个Context参数,并自动使用当前应用程序包名作为前缀来命名SharedPreferences文件。得到SharedPreferences对象后,开始向文件中存储数据,可分为三步:
  1. 调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象;
  2. SharedPreferences.Editor对象中添加数据。比如添加一个布尔型数据就用putBoolean()方法,添加一个字符串则用putString()方法,以此类推.
  3. 调用apply()方法将添加的数据提交,从而完成数据存储操作。

从SharedPreferences中读取数据

对应Preferences.Editor中每一个put方法,都有对应get方法进行读取。
这些get方法接收两个参数:参数一是键,传入存储数据时使用的键就可以得到相应的值了;第二个参数是默认值,表示当传入的键找不到时会以什么样的默认值进行返回,要与方法索取的数据类型一致。

实例

实现记住密码功能

SQLite数据库存储

Android系统内置。
SQLite作为一种轻量级的关系型数据库,运算速度快,占用资源少,适合移动设备使用。
Android提供了一系列的辅助行方法,可以使开发者去不编写SQL语句,也能完成增删改查操作。(CRUD:create添加,retrieve查询,update更新,delete删除)
适用:大量复杂的关系型数据。
默认存储目录:/data/data//databases/

创建数据库

Android提供了一个SQLiteOpenHelper帮助类用以帮助开发者更方便管理数据库。

  • SQLiteOpenHelper类是一个抽象类,需要创建一个自己的类去继承它。
  • SQLiteOpenHelper类有两个抽象方法:onCreate()onUpgrade()。必须在自己的帮助类中重写,实现创建、升级数据库的逻辑。
  • SQLiteOpenHelper类还有两个非常重要的实例方法:getReadableDatabase()getWritableDatabase()。两个方法都可以创建(数据库不存在)或打开(数据库已存在)一个数据库,并返回一个可对数据库进行读写操作的对象。
    不同的是,当数据库不可写入时(例如磁盘空间已满),getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法则抛出异常。
  • SQLiteOpenHelper类有两个构造方法可以重写,一般使用参数较少的那个。
    此构造方法接收4个参数,一是Context,有它才能对数据库进行操作;二是数据库名,创建时使用的就是这里指定的名称;第三个是允许我们在查询数据时返回一个自定义的Cursor,一般是传入null,第四个表示当前是数据库版本号,可用于对数据库进行升级操作。
  • 构建出SQLiteOpenHelper实例后再调用getReadableDatabase()getWritableDatabase()方法就能够创建数据库了。此时重写的onCreate()方法中创建表的逻辑也会得到执行。

实践一下:

  1. 创建Book表的SQL语句如下:
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列是自增长的。

  1. 创建 MyDatabaseHelper 类如下:
public class MyDatabaseHelper extends SQLiteOpenHelper {

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

    public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable 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, "Create succeeded", Toast.LENGTH_SHORT).show();
    }

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

    }
}

对类构造完成提取contex到私有变量中,传入Toast.makeText()作为上下文。
调用db.execSQL(CREATE_BOOK)直接执行赋值到CREATE_BOOK常量的创建table的SQL语句字符串。

  1. 在主活动布局新增按钮,用于创建数据库。
  2. 主活动中添加功能
public class MainActivity extends AppCompatActivity {
    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 createDatabase = (Button) findViewById(R.id.create_database);
        createDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dbHelper.getWritableDatabase();
            }
        });
    }
}

创建成功显示Toast提示。

  1. 使用File Explorer可以查看到databases目录下的BookStores.db文件。
  2. 使用adb调试工具查看Book表
    adb是Android SDK自带的调试工具,存放在sdk的platform-tools目录下。
    使用adb之前需要将其路径配置到环境变量里(右键此电脑->属性->高级系统设置->环境变量->在系统变量的Path中添加platform-tools目录)。
    《《《因为权限问题报错Permission denied。暂时不管啦。先往下学。》》》

挖坑:调试工具P216

升级数据库

SQLiteOpenHelper类中另一空方法onUpgrade()方法:对数据库进行升级。
数据库已经创建完毕,table已经加入;由于数据库已经创建完成,再新增table时,要用onUpgrade()方法去完成。
改写onUpgrade()方法:

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    //如果发现已经有表了就将其删除,再调用onCreate()方法重新创建
    db.execSQL("drop table if exists Book");
    db.execSQL("drop table if exists Category");
    onCreate(db);
}

将活动中构造方法的版本号参数进行更新,则会执行onUpgrade()方法。这里吧版本号1更换为2:

 dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);

添加数据

SQLiteDatabase类提供了一个insert()方法,用于添加数据。
有三个参数:

  • 参数一是表名;
  • 参数二是未指定添加数据的情况下给某些可为空的列自动赋值NULL(一般用不到,直接传入null);
  • 参数三是一个ContentValues对象,提供了一系列put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。

添加数据样例(在活动中新增一个按钮用于完成添加数据的操作):

        Button addData = (Button) findViewById(R.id.add_data);
        addData.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                //开始组装第一条数据
                values.put("name", "The Da Vinci Code");
                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);//插入第二条数据
            }
        });

更新数据

修改表中已有数据:update()方法,用于数据更新。
接收四个参数:

  • 参数一:表名
  • 参数二:ContentValues对象,要把更新数据在此组装进去,
  • 参数三、参数四:用于约束更新某一行或某几行中的数据,不指定的话默认为所有行

更新数据示例(在布局中添加一个按钮用于执行更新数据操作):

        Button updateData = (Button)findViewById(R.id.update_data);
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("price", 10.99);
                db.update("Book", values, "name = ?", new String[]{"The Da Vinci Code"});
            }
        });

构建了一个ContentValues对象,并指定了一组数据。调用了update()方法执行具体更新操作。第三、四个参数指定具体更新哪一行。第三个参数对应的是SQL语句的where部分,表示更新所有name等于?的行,而?是一个占位符,可以通过第四个参数提供的字符串数组为第三个参数中的每个占位符指定相应的内容。

删除数据

删除数据:SQLiteDatabase类中的delete()方法。
接收三个参数:

  • 参数一:表名
  • 参数二、参数三:用于约束更新某一行或某几行中的数据,不指定的话默认删除所有行

删除数据示例(在布局中添加一个按钮用于执行删除操作):

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

通过参数二参数三指定删除页数超过500页的书。指定行用法和update一致。

查询数据

用于查询数据:query()方法。
方法比较复杂,最短的一个方法也需要传入7个参数。

  • 参数1:表名
  • 参数2:指定去查询哪几列,默认查询所有列;
  • 参数3、参数4:约束查询某一行或某几行,默认查询所有行;
  • 参数5:指定需要去group by的列,不指定则表示表示不对查询结果进行group by操作;
  • 参数6:对group by之后的数据进行进一步的过滤,不指定则不进行过滤;
  • 参数7:指定查询结果的排序方式,不指定则表示使用默认的排序方式。

返回:返回一个Cursor对象,查询的所有数据都从这个对象中取出。
以下是参数介绍,各种重载都大同小异:
Android学习笔记——持久化存储_第1张图片
示例:

        Button queryButton = (Button)findViewById(R.id.query_data);
        queryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                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();
            }
        });

在Cursor类中使用了以下方法:

  • moveToFirst()方法:将数据的指针移动到第一行的位置;
  • getColumnIndex()方法:获取到某一列在表中对应的位置索引,这个索引传入到相应的取值方法中,就可以得到从数据库中读取到的数据了。

使用SQL操作数据库

对于熟练使用SQL的开发者,同样有相应的方法可以通过使用SQL来操作数据库。CRUD操作如下:
Android学习笔记——持久化存储_第2张图片
可以看到,除了查询数据时调用的是SQLiteDatabase的rawQuery()方法,其他的操作都是调用的execSQL()方法。

使用LitePal操作数据库

LitePal是一款开源的Android数据库框架,采用了对象关系映射(ORM)的模式,并将我们平时开发最常用到的一些数据库功能进行了封装,使得不编写一行SQL就可以完成各种建表和增删改查的操作。

配置LitePal

1. 声明引用

编辑app/build.gradle文件,在dependencies闭包中添加如下内容:compile 'org.litepal.android:core:1.3.2';

2. 配置

在app/src/main目录中新建一个assets目录,在assets目录新建一个文件litepal.xml,编辑如下:


<litepal>
    <dbname value="BookStore">dbname>
    <version value="1">version>
    <list>

    list>
litepal>

标签指定数据库名,标签指定数据库版本号,标签指定所有的映射模型。

3. 配置LitePalApplication

修改AndroidMainfest.xml,在application标签内添加android:name="org.litepal.LitePalApplication",如下:

    <application
        android:name="org.litepal.LitePalApplication"
        ...
        android:theme="@style/AppTheme">
        ...
    application>

至此配置完成。

创建一个数据库

LitePal采用的是对象关系映射(ORM)模式。也就是在面向对象语言和关系型数据库之间建立一种映射关系,使得可以利用面向对象的思维去操作数据库。
示例创建一张Book表。

1.定义一个Book类:

package com.example.litepaltest;

public class Book {
    private int id;
    private String author;
    private double price;
    private int pages;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

这样就是使用面向对象的方法完成了一张表的构建。类中的每一个字段分别对应表中的一个列。(使用Alt+Insert快捷键可以直接生成相应的getter和setter方法)

2.配置映射关系

将Book类添加到映射模型列表中,修改litepal.xml中的代码,将标签添加到标签中。
需要映射的模型都应当添加到标签中。且类名必须使用完整的类名。

3.修改MainActivity代码

调用Connector.getDatabase()方法就是一次简单的数据库操作,点击一下按钮,数据库就会完成自动创建。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button createDatabase = (Button) findViewById(R.id.create_database);
        createDatabase.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Connector.getDatabase();
            }
        });
    }
}

升级数据库

例如向Book表添加一个press字段,直接修改Book类中的代码即可:

...
private String press;
...
    public String getPress() {
        return press;
    }
    public void setPress(String press) {
        this.press= press;
    }

再添加一张Category表,只需要新建一个Category类:

在这里插入代码片

所有需要更新的内容修改完毕后,只需再修改litepal.xml中的代码,如下:


<litepal>
    <dbname value="BookStore">dbname>
    <version value="2">version>
    <list>
		<mapping class="com.example.litepaltest.Book">mapping>
		<mapping class="com.example.litepaltest.Category">mapping>
    list>
litepal>

如有新增表新增一下注册,再更新一下version即可。

使用LitePal添加数据

按钮的onClick()方法内创建一个对象,并通过set方法依次赋值,最后调用对象的save方法直接存入。
创建的表对象需要继承DataSupport类,这样可以继承save方法。

更新数据

对象是否已经存储根据调用model.isSaved()方法的结果来判断的,返回true表示已存储,返回false表示未存储。
返回true情况:1已经调用过model.save()方法去添加数据;2model对象通过LitePal提供的查询API查出来的。

你可能感兴趣的:(Android学习笔记,android,学习,android,studio)