Android Architecture Components
2018-02-10 本文已影响162人
JokAr_
最近使用了 google 新发布的框架 Android Architecture Components,使用起来很酷,不用操心数据的持久化,自动刷新等生命周期问题。但是却遇到了个问题,这个框架一起使用很爽,但是要是使用其中的几个就会遇到些问题,比如:不使用 room sql 框架把自己的数据库数据转换为 DataSource.Factory
,下面介绍如何解决的:
查看了 room 源码发现了所有跟DataSource.Factory
相关的数据转换都用到了LimitOffsetDataSource
类,所以我们只要仿照这个类来实现我们自己的DataSource.Factory
转换就行了。
Step 1
创建一个继承 PositionalDataSource
的抽象类,由于我本地数据库框架用的是 DBFlow, 所以我需要传入 几个我需要的参数来实现监听数据库变化:
public abstract class LimitOffsetDataSource<T extends BaseModel> extends PositionalDataSource<T> {
private Class<T> mClass;
private FlowContentObserver mContentObserver;
private Where<T> mWhere;
protected LimitOffsetDataSource(final Class<T> aClass,
Context context,
Where where) {
mClass = aClass;
mWhere = where;
//监听数据库改变
mContentObserver = new FlowContentObserver(FlowManager.DEFAULT_AUTHORITY);
mContentObserver.registerForContentChanges(context, mClass);
mContentObserver.addContentChangeListener(new FlowContentObserver.ContentChangeListener() {
@Override
public void onModelStateChanged(@Nullable Class<?> table,
BaseModel.Action action,
@NonNull SQLOperator[] primaryKeyValues) {
invalidate();
}
@Override
public void onTableChanged(@Nullable Class<?> tableChanged,
@NonNull BaseModel.Action action) {
// invalidate();
}
});
}
如上的构造函数,FlowContentObserver
是为了实现当数据库数据变化时我们能改变数据,mClass
,mWhere
是为了创建通用的 dbFlow 查询所需的参数。
Step 2
创建 方法返回 当前查询表里一共有多少数据:
public int countItems() {
try {
mContentObserver.beginTransaction();
int size = SQLite.select()
.from(mClass)
.queryList()
.size();
mContentObserver.endTransactionAndNotify();
return size;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
Step 3
重写相关方法:
@Override
public boolean isInvalid() {
mContentObserver.setNotifyAllUris(true);
return super.isInvalid();
}
加载初始化数据
@Override
public void loadInitial(@NonNull LoadInitialParams params,
@NonNull LoadInitialCallback<T> callback) {
int totalCount = countItems();
if (totalCount == 0) {
//如果当前表数据为空直接返回
callback.onResult(Collections.<T>emptyList(), 0, 0);
return;
}
// bound the size requested, based on known count
// 实现分页加载功能
final int firstLoadPosition = computeInitialLoadPosition(params, totalCount);
final int firstLoadSize = computeInitialLoadSize(params, firstLoadPosition, totalCount);
//loadRange 是我们自己实现的数据查询方法
List<T> list = loadRange(firstLoadPosition, firstLoadSize);
if (list != null && list.size() == firstLoadSize) {
callback.onResult(list, firstLoadPosition, totalCount);
} else {
// null list, or size doesn't match request - DB modified between count and load
invalidate();
}
}
下面就是核心了,就是下面两个方法把我们本地的数据传给 LiveData,并且增加了个对外公开方法以在需要改变查询的数据所需
/**
* 提供对外数据接口方便改数据需求
* @param dataList
* @return
*/
@SuppressWarnings("WeakerAccess")
protected abstract List<T> convertData(List<T> dataList);
/**
* Return the rows from startPos to startPos + loadCount
*/
@Nullable
public List<T> loadRange(int startPosition, int loadCount) {
mContentObserver.beginTransaction();
List<T> ts = mWhere.
limit(loadCount)
.offset(startPosition)
.queryList();
mContentObserver.endTransactionAndNotify();
return convertData(ts);
}
在加载初始回调传递到分派加载初始化初始化PagedList后,将调用此方法以从DataSource加载附加页面。
@Override
public void loadRange(@NonNull LoadRangeParams params,
@NonNull LoadRangeCallback<T> callback) {
List<T> list = loadRange(params.startPosition, params.loadSize);
if (list != null) {
callback.onResult(list);
} else {
invalidate();
}
}
Step5
使用:
new DataSource.Factory<Integer, Session>() {
@Override
public LimitOffsetDataSource<Session> create() {
return new LimitOffsetDataSource<Session>(Session.class,
context,
where) {
@Override
protected List<Session> convertData(List<Session> dataList) {
return dataList;
}
};
}
};
这样就创建了自己的DataSource.Factory
,注意这里的第一个类型只能是Integer
类型,因为LimitOffsetDataSource
继承的PositionalDataSource
用的是Integer
类型。
总结
以上就是所有了,最关键的两个核心就是
- 增加数据库监听,数据库改变时需要调用
invalidate();
来刷新这个 数据model
,这个很关键,否则做不到数据的自动刷新 - 把查询的本地数据传给
DataSource
,就是我们的loadRange(int startPosition, int loadCount)
方法了