Android 学习笔记--数据存储

Author Avatar
ChihoPang 1月 25, 2018
  • 在其它设备中阅读本文章

本篇笔记包含以下知识点:SharedPreferences、文件存储、SQLite 数据库、Room 框架,已在思维导图中提供总览。

参考资料

《第一行代码》第 6 章
Saving Data Using the Room Persistence Library | Android Developers
Room ORM 数据库框架 - Android - 掘金

思维导图

img

SharedPreferences

使用键值对存储数据。
SharedPreferences 存储路径为 /data/data/< package_name>/shared_prefs/ 目录。
支持的数据类型有:

  • boolean
  • float
  • long
  • int
  • String
  • Set< String>

三种获取 SharedPreferences 的方式

  1. context.getSharedPreferences(String name, int mode)
    mode 只有 MODE_PRIVATE 可选,表示只有当前应用才可以读写此 SharedPreferences。
  2. activity.getPreferences(int mode)
    自动将当前 Activity 的类名作为 SharedPreferences 的文件名。
  3. PreferenceManager.getDefaultSharedPreferences(context)
    传入一个 context,底层实现仍为使用方法 1 获取 SharedPreferences,传入的文件名为包名+”_preferences”,mode 为 MODE_PRIVATE。即:
    public static SharedPreferences getDefaultSharedPreferences(Context context) {
         return context.getSharedPreferences(context.getPackageName() + "_preferences”, MODE_PRIVATE);
    }
    

存储

  1. 获取 Editor:sharedPreferences.edit();
  2. 写入数据:editor.putBoolean(String key, boolean value)/putString(k,v)/putXXX(k,v)
  3. 提交修改:editor.apply()/commit (),效果相同,commit() 会返回操作是否成功。

读取

直接调用 sharedPreferences.getXXX(key,defaultValue) 即可获取对应数据。

文件存储

适合存储简单的文本数据或二进制数据。

存储

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

首先,通过 context.openFileOutput(String name, int mode) 获取文件的输出流(FileOutputStream),其中,name 为打开文件名,路径默认为 /data/data/< package_name>/files/ 目录,mode 为文件的操作模式,主要有两种:1.MODE_PRIVATE:若指定文件已存在,写入时会覆盖原文件内容;
2.MODE_APPEND:若指定文件已存在,写入时会追加内容。
获取到文件输出流之后,即可按照 Java 流操作写入内容。

读取

public String load(){
    FileInputStream in=null;
    BufferedReader reader=null;
    StringBuffer content=new StringBuffer();
    try{
        in=openFileInput("data");
        reader=new BufferedReader(new InputStreamReader(in));
        String line="";
        while((line=reader.readLine())!=null){
            content.append(line);
        }
    }catch(...){...}finally{...}
}

通过 context.openFileInput(String name) 获取文件输入流,再通过 Java 流操作即可读取内容。

SQLite 数据库存储

路径为 /data/data/< package_name>/databases/ 目录。

创建数据库

继承抽象类 SQLiteOpenHelper 并实现 onCreate() 和 onUpgrade() 两个抽象方法,利用 SQL 语句,完成数据库的创建和升级操作。
然后,通过 new MyDatabaseHelper(context,String name,CursorFactory factory, int version) 获取 helper,一般 factory 传 null。
此时,调用 helper.getReadableDatabase()\getWritableDatabase() ,若数据库未创建,都会根据 onCreate() 内容创建数据库,若传入版本号高于当前数据库版本号,则调用 onUpgrade() 更新数据库。

public class MyDatabasesHelper extends SQLiteOpenHelper{
    public static final String SQL_CREATE_BOOK="CREATE TABLE Book ("
      +"id integer primary key autoincrement"
      +"author text,"
      +"price real,"
      +"pages integer,"
      +"name text";

  @Override public void onCreate(SQLiteDatabase db) {
    db.execSQL(SQL_CREATE_BOOK);
  }

  @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("drop table if exists Book");
        onCreate(db);
    }
}

增加数据(Create)

  1. SQLiteDatabase db=helper.getWritableDatabase() 获取可写数据库
  2. 通过 ContentValues 组装数据并通过 db.insert() 写入数据库,具体操作如下:
    ContentValues values=new ContentValues();
    values.put(key,value);
    ...
    db.insert(String table, String nullColumnHack, ContentValues values);//第二个参数一般传 null,用于未指定数据列自动赋值 NULL
    

查询数据(Retrieve)

  1. SQLiteDatabase db=helper.getReadableDatabase() 获取可读数据库
  2. db.query(String table, String[] columns, String selection,
             String[] selectionArgs, String groupBy, String having,
             String orderBy)//获取游标 cursor,其中,传入参数为对应 SQL 语句各部分内容。
    
  3. 遍历 cursor,取出数据,最终关闭 cursor。
    List itemIds = new ArrayList<>();
    while(cursor.moveToNext()) {
    long itemId = cursor.getLong(
       cursor.getColumnIndexOrThrow(columnName));//查询某列名在cursor 的位置,并传入获取数值
    itemIds.add(itemId);
    }
    cursor.close();
    

更新数据(Update)

  1. SQLiteDatabase db=helper.getWritableDatabase() 获取可写数据库
  2. 构建含更改内容的 ContentValues ,通过 db.update(String table, ContentValues values, String whereClause, String[] whereArgs) 更新对应数据,具体操作如下:
    ContentValues values=new ContentValues();
    values.put(key,value);//只需传入要更改的key-value
    db.update("Book",values,"name=?",new String[]{"Android"});//其中,"Book" 为表名,"name=?"为 where 语句部分,?为占位符,内容由第四个参数一一填充
    

删除数据(Delete)

  1. SQLiteDatabase db=helper.getWritableDatabase() 获取可写数据库
  2. 调用 db.delete(String table, String whereClause, String[] whereArgs) 删除对应数据,参数作用与更新操作 db.update() 相同。

Room 框架使用

Room 框架是 Google I/O 2017 上谷歌发布的一款 ORM(Object Relational Mapping,对象映射关系)框架,通过注解的形式简化 SQL 操作,并在编译时对 SQL 语句进行检查。结构如下:
img
使用前应添加对应依赖:

//project 的 build.gradle
allprojects {
  repositories {
    jcenter()
    maven { url 'https://maven.google.com' }
  }
}

//app、moudle 的 build.gradle
compile 'android.arch.persistence.room:runtime:1.0.0-rc1'
annotationProcessor 'android.arch.persistence.room:compiler:1.0.0-rc1'

添加 Entity

@Entity(primaryKeys = {...},tableName = "users")//@Entity 标记模型类,内部属性 primaryKeys 指定主键,tableName 指定表名(表名默认使用类名)
public class User {
    @PrimaryKey private int uid;//@PrimaryKey 指定主键

    @ColumnInfo(name = "first_name")
    private String firstName;//@ColumnInfo 用于指定列名(默认使用变量名)

      @Ignore Bitmap bitmap;//@Ignore 用于忽略成员变量

    // getter、setter 方法...(必备) 
}

添加 DAO(Data Access Objects)

DAO 类用于完成数据与对象操作功能,为接口类,并在开头加 @Dao 注解。在增删查改操作方法前添加对应注解,其中,@Query 需要添加 SQL 查询语句作为属性,编译器会对 SQL 进行错误检查。
@Query 方法可选择返回的类型有:

  • 数据模型类
  • 集合类
  • 数据库游标 Cursor
    @Dao
    public interface UserDao {
      @Query("SELECT * FROM user")
      List<User> getAll Users();
      @Insert
      void insert(User... users);
      @Update
      void update(User... users);
      @Delete
      void delete(User... users);
    }
    

添加 Database 类

新建抽象类 UserDatabase ,继承 RoomDatabase,在 @Database 注解中指定对应属性和版本号,并提供获取 DAO 的抽象方法

@Database(entities={User.class}, version = 1)
public abstract class UserDatabase extends RoomDatabase {
    public abstract UserDao getUserDao();
}

获取 Database 并使用

mUserDatabase = Room.databaseBuilder(getApplicationContext(),
                UserDatabase.class, databaseName).build();
new Thread(){
    @Override public void run() {
      super.run();
      User user = new User();
      setupUser(user);
      mUserDatabase.getUserDao().insert(user);
   }
}.start();//插入为耗时操作,Room 强制在非 UI 线程进行,否则会 crash

通过 Room.databaseBuilder(context,databaseClass, databaseName).build() 可以获取到自建的 Database 类实例,从而获取 DAO 类实例,进行相关操作。