Android上的Firebase实时数据库加载指示

时间:2017-01-26 22:26:30

标签: android firebase-realtime-database

Firebase提供了非常棒的实时数据库服务,但它有点突出,我建议不仅仅是我的经验,当这个引擎被用作主本地存储(离线模式)时。

当应用程序请求某些数据时,我期望看到该过程的一些指示。但是我没有找到任何直接的功能来使它工作。

所以,我创建了自定义解决方案,并希望得到社区的评论。

1 个答案:

答案 0 :(得分:0)

这个想法很简单:将ValueEventListener绑定到某个版本标记,该标记使用SingleValueEvent回调更新数据并更改其状态来创建请求。

假设我有一个User对象,对于版本控制,我添加了带有时间戳内容的ver属性:

users : {
    id : {
       name : 'Mr. Anderson',
       ver : '<timestamp>'
    }
}

要查看数据状态,我会介绍enum Stateinterface StateIndicatableinterface 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();
            }
        }

    }
);

好吧,正如我所说的,这里发布的任何意见和替代解决方案都非常感谢!

代码是从内存中输入的,可能有错误