Evil Mouth's Blog

初试Google Agera database

February 10, 2017

(内白:第一次真正意义上写博客,这几年来开发路程遇到过许多问题,但都是简单标记一下,并没有记录下来,等过段时间遇到同样问题脑袋一热都忘了又得翻翻翻,特此开始写博客记录我的开发路程,老了,记忆力下降了,再过几年连代码都不会打了。。。 0 0)

进入主题,最近打算用 agera 运用在新项目上,本来打算 rxjava,貌似个个都在用。。但是对比了一下 agera 和 rxjava,感觉在 android 上使用 agera 更适合,个人觉得 rxjava 的确很强大,但是操作符有点乱,什么 action1、action2,虽然后面 rxjava2 更改并加强了概念,还是觉得使用起来有时候会忘记,况且 agera 跟 android 生命周期绑定,所以选择了 agera

那么开始吧,先从常用的 sqlite 入手

依赖

Google 官网出了一个 database 的库,就是基于 Agera 概念封装了一下 sqlite,使用方法如下

compile 'com.google.android.agera:agera:1.2.0'
compile 'com.google.android.agera:database:1.2.0'

Agera Github 地址: https://github.com/google/agera 还有中文翻译: https://github.com/captain-miao/AndroidAgeraTutorial/wiki

首先是创建一个 SQLiteOpenHelper

这里 database 库里有个SqlDatabaseSupplier帮我们继承了SQLiteOpenHelper并且实现了一个Supplier接口(这个 Supplier 接口是使用 agera 必须实现的),所以直接继承SqlDatabaseSupplier就行了

public class BikeDBHelper extends SqlDatabaseSupplier {

    private static final String DB_NAME = "DB.db";
    public static final String TABLE_NAME = "Bikes";

    public BikeDBHelper(@NonNull Context context) {
        super(context, DB_NAME, null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String sql = "create table if not exists " + TABLE_NAME + " (id integer primary key,num text)";
        db.execSQL(sql);
        //插入两条数据模拟一下
        db.execSQL("insert into " + TABLE_NAME + " (id,num) values (1,'123456')");
        db.execSQL("insert into " + TABLE_NAME + " (id,num) values (2,'254365')");
    }

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

下面是SqlDatabaseSupplier的源码,很简单

/**
 * Abstract extension of {@link SQLiteOpenHelper} implementing a sql database {@link Supplier} to be
 * used with the {@link SqlDatabaseFunctions}.
 */
public abstract class SqlDatabaseSupplier extends SQLiteOpenHelper
    implements Supplier<Result<SQLiteDatabase>> {

  /**
   * Extending the base constructor, for overriding in concrete implementations.
   */
  public SqlDatabaseSupplier(@NonNull final Context context, @NonNull final String path,
      @Nullable final CursorFactory factory, final int version) {
    super(context, path, factory, version);
  }

  @NonNull
  @Override
  public final synchronized Result<SQLiteDatabase> get() {
    try {
      return absentIfNull(getWritableDatabase());
    } catch (final SQLException e) {
      return failure(e);
    }
  }
}

创建 Repository

在 agera 最重要的就是这个Repository了,先从查询开始

Repository<Result<List<Bike>>> query = Repositories.repositoryWithInitialValue(Result.<List<Bike>>absent())
                .observe(onSearchObservable)//这里观察一个按钮,点击按钮就获取一次
                .onUpdatesPerLoop()//刷新频率
                .getFrom(new Supplier<String>() {
                    @NonNull
                    @Override
                    public String get() {
                        return "";//可以是查询条件
                    }
                })
                .goTo(EXECUTOR)//异步
                .transform(new Function<String, SqlRequest>() {
                    @NonNull
                    @Override
                    public SqlRequest apply(@NonNull String input) {
                    	//这里的input就是getFrom返回的字符串,可以是查询条件,根据查询条件创建不同的SqlRequest
                        return SqlRequests.sqlRequest()
                                        .sql("SELECT * FROM " + BikeDBHelper.TABLE_NAME)
                                        .compile()
                    }
                })
                .thenTransform(SqlDatabaseFunctions.databaseQueryFunction(new BikeDBHelper(this), new Function<Cursor, Bike>() {
                    @NonNull
                    @Override
                    public Bike apply(@NonNull Cursor cursor) {
                        return new Bike(
                                cursor.getString(cursor.getColumnIndex("num"))
                        );
                    }
                }))
                .compile();

重点就是最后一步thenTransform,将SqlRequest转换成Result<List<Bike>>。官网提供了SqlDatabaseFunctions类,里面有数据库增删改查四个方法,这里用到的就是查询databaseQueryFunction方法,通过传入一个Supplier<Result<SQLiteDatabase>>,就是一开始创建的BikeDBHelper,还传入一个Function将里面处理好的游标Cursor提供给我们去获取转换成该游标下的数据,至此转换成最后需要的Result<List<Bike>>

/**
   * Creates a sql query {@link Function}.
   */
  @NonNull
  public static <T> Function<SqlRequest, Result<List<T>>> databaseQueryFunction(
      @NonNull final Supplier<Result<SQLiteDatabase>> database,
      @NonNull Function<Cursor, T> rowMap) {
    return new DatabaseFunction<>(database, new DatabaseQueryMerger<>(rowMap));
  }

push event

@Override
    protected void onResume() {
        super.onResume();
        query.addUpdatable(this);
    }

@Override
    protected void onPause() {
        super.onPause();
        query.removeUpdatable(this);
    }

pull data

@Override
    public void update() {
        query.get().ifSucceededSendTo(this);
    }

    @Override
    public void accept(@NonNull List<Bike> value) {
        mMainAdapter.setList(value);
    }

总结

创建Repository过程中涉及到一个SqlRequest,这其实就是一个查询 request,针对增删改还有SqlInsertRequest``SqlDeleteRequest``SqlUpdateRequest一共四种 request,都是通过SqlRequests创建。其实整个 database 库很小,一共也就几个类,所以使用起来还是挺方便的,重点还是得理解 Repository。

1

— Evil Mouth