Android开发基础知识整理之数据存储

本篇主要涉及Android中的数据持久化技术。

一、 文件存储

不对存储内容进行任何格式化处理,原封不动的保存到文件中。适合存储一些简单的文本数据或二进制数据。

(一) 存储

1. 获取FileOutputStream对象:使用Context类中的 openFileOutput() 方法,接收两个参数:(文件名, 操作模式)。

  • 文件默认存储到/data/data/<package name>/files/目录下;
  • 两种操作模式可选:MODE_PRIVATE(默认)和MODE_APPEND 。

2. 构建出OutputStreamWriter对象,再借此构建出BufferedWriter对象,调用它的 write 方法将数据写入文件。

public void save(String inputText) {
    FileOutputStream out = null;
    BufferedWriter writer = null;
    try {
        out =openFileOutput("data", Context.MODE_PRIVATE); // 获取FileOutputStream对象
        writer = new BufferedWriter(new OutputStreamWriter(out); // 构建BufferedWriter对象
        writer.write(inputText);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (writer != null) {
                writer.close()
            } 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}   

(二) 读取

1. 获取FileInputStream对象:使用Context类中的 openFileInput(文件名) 方法。

2. 构建出InputStreamReader,再借此构建出BufferedReader对象,读取到StringBuilder中,最后返回。

public String load() {
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
    in = openFileInput("data"); // 获取FileInputStream对象
    reader = new BufferedReader(new InputStreamReader(in)); // 构建BufferedReader对象
    String line = "";
    while ((line = reader.readLine()) != null) {
        content.append(line); // 逐行读取,若内容不为空添加到StringBuilder中
    }
} catch (IOException e) {
    e.printStackTrace;
} finally {
    if (reader != null) {
        try {
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
return content.toString();
}

二、 SharedPreference存储

采用键值对的方式存储数据,支持多种不同的数据类型存储,使用XML格式来管理数据的。

(一) 存储

1. 获取SharedPreferences对象,有三种方法:

  • Context类中的 getSharePregerences(文件名, 操作模式) 方法。
    • SharedPregerences文件都存放在/data/data/<package name>/shared_prefs/目录下
    • 操作模式只有MODE_PRIVATE可选,和传入0效果一样
  • Activity类中的 getPreferences(操作模式) 方法。
    • 只接受一个操作模式参数,自动将当前活动类名作为文件名
  • PreferenceManager类中的 getDeafaultSharedPreferences(context) 方法。
    • 是一个静态方法,接收一个context参数,使用当前应用程序包名作为前缀来命名文件

2. 向SharedPreferences文件中存储数据

(1) 调用SharedPreferences对象的 edit() 方法来获取一个SharedPreferences.Editor对象;

(2) 向Editor对象中添加数据,根据数据类型调用 putString()putBoolean() 等方法;

(3) 调用 apply() 方法提交数据。

SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
editor.putString("name", "Tom");
editor.putInt("age", 28);
editor.putBoolean("married", false);
editor.apply();

(二) 读取

使用 getSring()getBoolean() 等方法,接收两个参数:(键, 默认值)。

SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE);
String name = pref.getString("name", "");
int age = pref.getInt("age", 0);
boolean married = pref.getBoolean("married", false);
Log.d("MainActivity", "name is " + name);
Log.d("MainActivity", "age is " + age);
Log.d("MainActivity", "married is " + married);

三、 SQLite数据库存储

适合存储大量复杂的关系型数据。

(一) 创建数据库

  • 借助抽象类SQLiteOpenHelper,继承此类后重写 onCreate()onUpgrade() 方法来创建和升级数据库。
  • SQLiteOpenHelper的构造方法接收4个参数:(Context, 数据库名, null, 版本号)。第三个参数允许在查询数据时返回一个自定义Cursor,一般传入null。
  • 两个实例方法 getReadableDatabase()getWritableDatabase() 用于创建或打开现有数据库,并返回一个可进行读写操作的对象。
  • 数据库文件存放在/data/data/<package name>/databases/目录下。
public class MyDatabaseOpenHelper extends SQLiteOpenHelper {

    public static final String CREATE_BOOK = "create table BOOK ("
            + "id integer primary key antoincrement, " // 设置为主键,自增长
            + "author text" // text表示文本类型
            + "price real, " // real表示浮点型
            + "pages integer, " // 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, "Create succeeded", Toast.LENGTH_SHORT).show();
    }

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

}
public class MainActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        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();
            }
        });
    }

}

(二) 升级数据库

1. 将建表语句添加到MyDatabaseHelper中;

2. onUpgrade() 中执行DROP语句,再调用 onCreate() 方法重新创建;

3. 修改MainActivity中构造方法的版本号,使 onUpgrade() 能够执行。

@Override
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);
}

(三) 添加数据

使用SQLiteDatabase的 insert() 方法,接收三个参数:(要添加数据的表名, null, ContentValues对象)

  • 第二个参数用于在未指定数据的情况下给某些为空的列自动赋值NULL,一般用不到直接传入null;
  • 第三个参数的ContentValues对象提供一系列 put() 方法重载用于向其中添加数据,传入 (列名, 待添加数据) 即可。
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();
// 开始组装第二条数据
...
db.insert("Book", null, valuse); // 插入第二条数据

(四) 更新数据

使用 update() 方法,接收四个参数:(表名, ContentValues对象, 约束语句, 字符串数组)。

  • 第二个参数中把要更新的数据组装进来;
  • 第三个参数用于约束更新某一行或某几行的数据,对应SQL语句的where部分,包含占位符,例如”name = ?”;
  • 第四个参数用一个字符串数组指定占位符的内容,如 new String[] {"The Da Vinci Code"}
SQLiteDatabase db = dbHelper.getWritaberDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99);
db.update("Book", values, "name = ?", new String[] {"The Da Vinci Code"}); // 更新书名为"The Da Vinci Code"的数据

(五) 删除数据

使用 delete() 方法,接收三个参数:(表名, 约束语句, 字符串数组)。

  • 和insert类似,第二三个参数用于约束删除某一行或某几行数据。
db.delete("Book", "pages > ?", new String[] {"500"}); // 删除页数超过500的书

(六) 查询数据

1. 使用 query() 方法,最短的方法重载接收七个参数;

2. 查询完得到一个Cursor对象,调用 moveToFirst() 将数据指针移动到第一行位置(返回True表示移动成功数据不为空);

3. 通过一个循环遍历查询到的每一行数据,循环中可以通过Cursor的 getColumnIndex() 方法获取到某一列在表中对应的位置索引,将其传入对应取值方法中就可以读取到数据了;

4. 最后调用 colse() 方法关闭Cursor。

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 指定查询结果的排序方式

// 查询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();

(七) 使用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);

四、 使用LitePal操作数据库

LitePal是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式,并对常用数据库功能进行了封装。

(一) 配置LitePal

  • 在app/build.grale文件中声明开源库的引用:
compile 'org.litepal.android:core:1.4.1'
  • 配置litepal.xml。在app/src/main下创建一个assets目录,在其中创建一个litepal.xml文件,编辑其中内容:
<?xml version = "1.0" encoding="utf-8"?>
<litepal>
    <!-- 数据库名 -->
    <dbname value="BookStore" ></dbname> 

    <!-- 数据库版本号 -->
    <version value="1" ></version>

    <!-- 指定所有映射模型 -->
    <list>
    </list>
</litepal>
  • AndroidManifest中配置LitePalApplication。
<application  android:name="org.litepal.LitePalApplication" ...>
    ...
</application>

(二) 创建和升级数据库

1.创建数据库

(1) 根据要创建的表定义一个类,定义相应字段并生成对应的getter和setter方法;

(2) 将创建的类添加到映射模型列表中,使用<mapping>标签;

(3) 调用 Connector.getDatabase() 创建数据库。

/** 根据Book表的内容定义Book类 */
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;
    }

    ... //生成各个字段对应的getter和setter方法

}
<!-- 添加到映射模型列表中 -->
<list>
    <mapping class="com.example.litepaltest.Book"></mapping>
</list>
@Override
public void onClick(View v) {
    Connector.getDatabase(); // 创建数据库
}

2. 升级数据库

更改想要修改的任何内容,然后将版本号加1即可。升级会保留之前表中的所有数据。

(三) 添加数据

创建出模型实例(要进行CRUD操作模型类必须继承字DataSupport类),调用set方法将所有要存储的数据设置好,最后调用 save() 方法即可。

public class Book extends DataSupport {
...
}
Book book = new Book();
book.setName("The Da Vinci Code");
book.setAuthor("Dan Brown");
book.setPages(454);
book.setPrice(16.96);
book.setPress("UnKnow");
book.save():

(四) 更新数据

  • new出模型实例,调用set方法设置要更新的数据,最后调用 updateAll() 方法执行更新操作(updateAll中可以指定约束条件)。
  • 注意:要更新为默认值,使用 setToDefault() 方法。
/** 将书名为The Lost Symbol并且作者是Dan Brown的书价格更新为14.95,出版社更新为Anchor */
Bool book = new Book();
book.setPrice(14.95);
book.setPress("Anchor");
book.updateAll("name = ? and author = ?", "The Lost Symbol", "Dan Brown");
/** 将所有书页更新为0(默认值) */
Book book = new book;
book.setToDefault("pages");
book.updateAll();

(五) 删除数据

  • 调用已存储对象(调用过save方法或通过LitePal提供的查询API查出来的对象)的 delete() 方法。
  • 调用 DataSupport.deleteAll() 方法,接收三个参数:(要删除数据的表名, 约束条件, 占位符内容)
/** 删除Book表中价格低于15的书 */
DataSupport.deleteAll(Book.class, "price < ?", "15");

(六) 查询数据

  • 调用 findAll() 方法,返回值为对应类型的List集合。
List<Book> books = DataSupport.finAll(Book.class);
for (Book book : books) {
    Log.d("MainActivity", "book name is " + book.getName());
    Log.d("MainActivity", "book author is " + book.getAuthor());
    Log.d("MainActivity", "book pages is " + book.getPages());
    Log.d("MainActivity", "book price is " + book.getPrice());
    Log.d("MainActivity", "book press is " + book.getPress());
}
  • findFirst()findLast() 分别查询第一条和最后一条数据.
  • 通过连缀查询定制更多查询功能:
方法 对应SQL部分 描述
select() select 指定查询哪几列数据
where() where 指定查询的约束条件
order() order by 指定结果的排序方式(desc降序,asc或不写为升序)
limit() limit 指定查询结果的数量
offset() limit 指定查询结果的偏移量

/** 查询Book表中第11~20条页数大于400的name、author、pages这三列数据,并将结果按页数升序排列 */
List<Book> books = DataSupport.select("name", "author", "pages")
                              .where("pages > ?", "400")
                              .order("pages")      // 默认为asc升序
                              .limit(10)           // 只查前10条
                              .offset(10)          // 偏移10个位置
                              .find(Book.class);
  • 也支持使用原生SQL进行查询:调用 DataSupport.findBySQL(SQL语句, 占位符的值) ,返回一个Cursor对象。
Cursor c = DataSupport.findBySQL("select * from Book where pages > ? and price < ?", "400", "20");

你可能感兴趣的:(Android开发,数据存储)