2018 May 01 —— improvement; android

Android Component-Room

Room 的 1.0 出来有段时间了,抽个时间来看看;

是什么?

Room 提供了一个基于 SQLite 的抽象层用以便利的处理 SQLite 相关的数据库操作;

首先回忆下原始的数据库使用方式,定义数据库结构,定义表字段,实现接口,确定各种策略, Match 构建等等,整体还是比较繁琐的一套逻辑,尤其是当数据库相关的逻辑比较复杂之后相关的问题就更复杂了;

在此问题基础之上,为了解决这些问题,同后端的技术解决方案类似,出现了很多 ORM 框架,较常用的有 GreenDao , Reaml ….而 Room 则相当于是一种官方解决方案;

其核心优势在于基于注解的SQL语句编译时校验检查可减少运行时可能的崩溃问题;

怎么用?

依赖引入:

 

    implementation 'android.arch.persistence.room:runtime:1.0.0'
    annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'

核心注解使用,构建库,表,以及查询语句的使用:

@Database : 定义数据库,数据库名称,表名称,版本号

 

//注意 Schema 的 Export 问题
@Database(entities = {User.class},version = 1,exportSchema = false)
public abstract class UserDataBase extends RoomDatabase{

    private static final String USER_DB_NAME = "userDb.db";

    private static volatile  UserDataBase instance ;

    public synchronized static UserDataBase getInstance(Context context) {
        if (instance == null) {
            instance = create(context);
        }
        return instance;
    }

    private static UserDataBase create(Context context) {
        return Room.databaseBuilder(context,UserDataBase.class,USER_DB_NAME).build();
    }

    public abstract UserDao getUserDao();
}

Schema 会输出所构建数据库的相关信息,默认 export 输出,而如果输入不指定位置则有警告:

Schema export directory is not provided to the annotation processor ...

利用 gradle 指定 schema 输出位置:

 

    //app/build.gradle
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
            }
        }
    }

@Entity : 对应的表名, Entity 中的属性字段对应表中的 column 字段;

 

   /**
     * 对于非基础类型需要注解 @NonNull
     */
    @PrimaryKey
    @NonNull
    public final String id;

    @ColumnInfo(name = "")

    @Ignore 
    // 对应多构造函数匹配忽略的情况

@Dao : 定义数据库的操作接口

 

@Dao
public interface UserDao {

    @Query("SELECT * FROM User")
    List<User> getAllUser();

    @Query("SELECT * FROM User WHERE id=:id")
    User getUserById(int id);

    @Query("SELECT * FROM User")
    Cursor getUserCursor();

    @Query("SELECT * FROM User WHERE name =:name LIMIT :max")
    List<User> getLimitUsersByName(int max, String name);

    @Insert
    void insertUser(User... users);

    @Insert
    void insertUserArray(List<User> userList);

    @Update
    void updateUser(User... users);

    @Delete
    void deleteUser(User... users);

}

使用 Dao 接口操纵数据库:

UserDataBase.getInstance(this).getUserDao().getAllUser();

注解

简单去看看 Room 注解所生成的代码:

 

//数据库的抽象类 UserDataBase 生成 UserDataBase_Impl 的实现
public class UserDataBase_Impl extends UserDataBase {
  ...
  @Override
  public UserDao getUserDao() {
    if (_userDao != null) {
      return _userDao;
    } else {
      synchronized(this) {
        if(_userDao == null) {
        // UserDao 接口也生成了对应的 Dao 接口实现
          _userDao = new UserDao_Impl(this);
        }
        return _userDao;
      }
    }
  }
}

这个实现中可以看出其实现还是逃脱不了封装 SupportSQLiteOpenHelper 的命运,只是 AnnotationProcessor 的使用的确极大的解放了生产力;

 
// 这里面的 SupportSQLiteOpenHelper 具体实现在 FrameworkSQLiteOpenHelper
// FrameworkSQLiteOpenHelper 具体又是代理了 OpenHelper 
// OpenHelper 继承我们原始常见的 SQLiteOpenHelper
class FrameworkSQLiteOpenHelper implements SupportSQLiteOpenHelper {
    private final OpenHelper mDelegate;
    ...
    static class OpenHelper extends SQLiteOpenHelper {
    }
}


// 在 SQLiteOpenHelper的实现 OpenHelper 中我们继续可以看到 DataBase 也被代理封装
// 抽象了 SupportSQLiteDatabase 的接口,并通过 FrameworkSQLiteDatabase 实现接口
// FrameworkSQLiteDatabase 则代理了真实的 SQLiteDatabase
/**
 * Delegates all calls to an implementation of {@link SQLiteDatabase}.
 */
@SuppressWarnings("unused")
class FrameworkSQLiteDatabase implements SupportSQLiteDatabase {
    private static final String[] CONFLICT_VALUES = new String[]
            {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
    private static final String[] EMPTY_STRING_ARRAY = new String[0];

    private final SQLiteDatabase mDelegate;

// FrameworkSQLiteStatement 同样继承了SupportSQLiteStatement接口
// 同时代理 SQLiteStatement
/**
 * Delegates all calls to a {@link SQLiteStatement}.
 */
class FrameworkSQLiteStatement implements SupportSQLiteStatement {
    private final SQLiteStatement mDelegate;

这些代理的真实目的都在于解决我们常说的数据库的操作太麻烦,细节太多的这一问题,通过代理屏蔽原始数据库操作处理相关的复杂细节,将一些意义明确简洁的接口暴露给开发者,简化数据库相关的操作;


Quote:

GoogleDoc-Save data in a local database using Room

Room — Introduction

7 Steps To Room

上一篇
下一篇
Loading Disqus comments...
Table of Contents