Firebase提供了非常棒的实时数据库服务,但它有点突出,我建议不仅仅是我的经验,当这个引擎被用作主本地存储(离线模式)时。
当应用程序请求某些数据时,我期望看到该过程的一些指示。但是我没有找到任何直接的功能来使它工作。
所以,我创建了自定义解决方案,并希望得到社区的评论。
答案 0 :(得分:0)
这个想法很简单:将ValueEventListener绑定到某个版本标记,该标记使用SingleValueEvent回调更新数据并更改其状态来创建请求。
假设我有一个User
对象,对于版本控制,我添加了带有时间戳内容的ver
属性:
users : {
id : {
name : 'Mr. Anderson',
ver : '<timestamp>'
}
}
要查看数据状态,我会介绍enum State
,interface StateIndicatable
,interface OnStateChangeListener
:
enum State{ INIT, LOADING, LOADED, FAILED }
interface OnStateChangeListener{
void stateChanged(State newState);
}
interface StateIndicatable{
State getState();
void setOnStateChangeListener(OnStateChangeListener listener);
void clearOnStateChangeListener();
}
我不想更改User
模型,我将使用一个存在于视图/ presentation / view_model级别的包装器:
我想围绕任何可版本化的模型创建一个抽象的Wrapper:
abstract class FirebaseVersionedModelWrapper<M> implements StateIndicatable{
int version;
protected WeakReference<M> modelRef;
State state;
OnStateChangeListener onStateChangeListener;
DatabaseReference verRef;
FirebaseVersionedModelWrapper(M model, String firebasePath, OnStateChangeListener onStateChangeListener){
this.modelRef = new WeakReference(model);
this.state = State.INIT;
this.onStateChangeListener = onStateChangeListener;
verRef = FirebaseDatabase.getInstance().getReference(firebasePath + "/ver");
verRef.keepSynced(true);
verRef.addValueEventListener(
new OnValueEventListener(){
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(modelRef.get()==null){
verRef.removeEventListener(this);
return;
}
if(dataSnapshot.hasChild("ver"){
if(dataSnapshot.child("ver").getValue(Integer.class) != version){
requestUpdate();
}
}
else
setState(State.FAILED);
}
@Override
public void onCancelled(DatabaseError databaseError) {
if(modelRef.get()==null){
verRef.removeEventListener(this);
return;
}
setState(State.FAILED);
}
}
);
}
private void requestUpdate(){
setState(State.LOADING);
DatabaseReference ref = FirebaseDatabase
.getInstance
.getReference(firebasePath);
ref.keepSynced(true);
ref.addListenerForSingleValueEvent(
new OnValueEventListener(){
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
M model = modelRef.get();
if (model==null) return;
if(dataSnapshot.hasChild("ver")){
version = dataSnapshot.child("ver").getValue(Integer.class);
setFields(model, dataSnapshot);
setState(State.LOADED);
}
else{
setState(State.FAILED);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
setState(State.FAILED);
}
}
);
}
private void setState(State newState){
if (this.state == newState) return;
this.state = newState;
if (onStateChangeListener != null)
onStateChangeListener.stateChanged(newState);
}
@Override
public State getState(){
return this.state;
}
@Override
public void setOnStateChangeListener(OnStateChangeListener listener){
this.onStateChangeListener = listener;
}
@Override
public void clearOnStateChangeListener(){
this.onStateChangeListener = null;
}
protected abstract void setFields(M model, DataSnapshot dataSnapshot);
}
这里调用DatabaseReference#keepSynced(true)
以确保数据路径上的请求将返回新数据。这是可以讨论的方法,现在我的实践中并没有完全证实。
然后我将User
打包到UserWrapper
:
class UserWrapper extends FirebaseVersionedModelWrapper<User>{
FirebasedUser(User userModel, String userPath, OnStateChangeListener listener){
super(userModel, userPath, listener);
}
@Override
void setFields(User model, DataSnapshot dataSnapshot){
// setting values for model
}
}
最终我可以将包装器和绑定指示例程实例化为OnStateChangeListener回调:
User user = new User();
UserWrapper wrapper = new UserWrapper(
user,
new OnStateChangeListener(){
@Override
void stateChanged(State newState){
if(newState == State.LOADED){
showUserDetails();
}
else if(newState == State.FAILD){
showErrorState();
}
else{
showLoadingIndicator();
}
}
}
);
好吧,正如我所说的,这里发布的任何意见和替代解决方案都非常感谢!
代码是从内存中输入的,可能有错误