如何使用数据绑定库将Firebase侦听器转换为可观察的地图?

时间:2017-01-20 15:54:17

标签: android firebase firebase-realtime-database android-databinding

TL; DR

是否可以隐藏Firebase read and write operations ObservableMap后面的所有Facade Pattern

所以我们所要做的就是:

User oldUser = map.put(user);
User newUser = map.get(primaryKey);

完整问题

根据Firebase documentation,为了写一个值,我必须通过DatabaseReference定义资源路径并设置一个值。

例如,如果我们有一个User纯文本对象,我们会将它设置为:

mDatabase.child("users")
    .push()
    .setValue(user);

为了阅读整个users树,我们必须实现ChildEventListener。只要新用户成为树的一部分,就会通过onChildAdded

收到
@Override
public void onChildChanged (DataSnapshot snapshot, String previousChildName) {
    Log.i(TAG, snapshot.getValue(User.class));
}

最后,为了阅读特定用户,我们使用ValueEventListener

mDatabase.child("users")
    .child(primaryKey)
    .setValue(user)
    .addValueEventListener(new ValueEventListener() {
        @Override
            public void onDataChange(DataSnapshot snapshot) {
            Log.i(TAG, snapshot.getValue(User.class));
        }

        @Override
        public void onCancelled(DatabaseError error) {
            Log.w(TAG, "loadPost:onCancelled", error.getMessage());
        }
    });

那么可以使用ObservableMap作为Facade Pattern,隐藏所有Firebase read and write operations吗?

1 个答案:

答案 0 :(得分:1)

TL; DR

我将ObservableArrayMapChildEventListener合并为FirebaseArrayMap

可以使用FirebaseUI-Android有效的here示例OnMapChangedCallback。现在你所要做的就是:

// Remote updates immediately send you your view
map.addOnMapChangedCallback(mUserChangedCallback);

// Non blocking operation to update database
map.create(user);

// Local up-to-date cache
User user = map.get(primaryKey);

请记住(取消)在onResumeonPause中注册ChildEventListener,以避免Map远程更新导致的内存泄漏。

CRUD

首先我们需要为FirebaseArrayMap创建词库接口,即:

public interface CRUD<K, V> {

    Task<V> create(V value);

    Task<V> create(K key, V value);

    Task<Void> createAll(SimpleArrayMap<? extends K, ? extends V> array);

    Task<Void> createAll(Map<? extends K, ? extends V> map);

    V read(K key);

    Task<V> delete(K key);

    Task<Void> free();

}

此界面将由Map实施,并返回由put返回的相同值

这些方法类似于putAllgetTasks,但不是返回值,而是返回FirebaseArrayMap这些值(或例外)。例如:

    @Override
    public Task<Void> createAll(Map<? extends K, ? extends V> map) {
        Collection<Task<V>> tasks = new ArrayList<>(map.size());
        for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
            tasks.add(create(entry.getKey(), entry.getValue()));
        }
        return Tasks.whenAll(tasks);
    }

    @Override
    public V read(K key) {
        return get(key);
    }

    @Override
    public Task<V> delete(K key) {
        final V oldValue = get(key);
        final Continuation<Void, V> onDelete = new Continuation<Void, V>() {
            @Override
            public V then(@NonNull Task<Void> task) throws Exception {
                task.getResult();
                return oldValue;
            }
        };
        return mDatabaseReference.child(key.toString())
            .setValue(null)
            .continueWith(onDelete);
    }

ObservableArrayMap

我们创建了一个展开ObservableArrayMap的摘要ChildEventListener并实施了CRUDChildEventListener

public abstract class FirebaseArrayMap<K extends Object, V> extends
    ObservableArrayMap<K, V> implements ChildEventListener, CRUD<K, V> {

    private final DatabaseReference mDatabaseReference;

    public abstract Class<V> getType();

    public FirebaseArrayMap(@NonNull DatabaseReference databaseReference) {
        mDatabaseReference = databaseReference;
    }

ObservableArrayMap将使用超级方法,将ChildEventListener转换为本地缓存。

因此,当成功完成写入操作(或发生远程更改)时,Map将自动更新我们的CRUD

    @Override
    public void onCancelled(DatabaseError error) {
        Log.e(TAG, error.getMessage(), error.toException());
    }

    @Override
    public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
        if (snapshot.exists()) {
            super.put((K) snapshot.getKey(), snapshot.getValue(getType()));
        }
    }

    @Override
    public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
        super.put((K)snapshot.getKey(), snapshot.getValue(getType()));
    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
        super.put((K)snapshot.getKey(), snapshot.getValue(getType()));
    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {
        super.remove(dataSnapshot.getKey());
    }

合同

Map接口是必需的,以便不违反put合同。例如,CRUD在给定位置插入新值时返回先前的值,但此操作现在异步

对于编写操作,此处的hack使用Map进行非阻止,使用Android Data Binding进行阻止操作:

    @Override
    @WorkerThread
    public V put(K key, V value) {
        try {
            return Tasks.await(create(key, value));
        } catch (ExecutionException e) {
            return null;
        } catch (InterruptedException e) {
            return null;
        }
    }

数据绑定

免费,现在您的Map还有onResume

@Override
protected onCreate() {
    mUserMap = new UserArrayMap();
    mChangedCallback = new OnUserMapChanged();
}

@Override
protected void onResume() {
    super.onResume();
    mUserMap.addOnMapChangedCallback(mChangedCallback);
}

@Override
protected void onPause() {
    mUserMap.removeOnMapChangedCallback(mChangedCallback);
    super.onPause();
}

static class OnUserMapChanged extends OnMapChangedCallback<FirebaseArrayMap<String, User>, String, User> {

    @Override
    public void onMapChanged(FirebaseArrayMap<String, User> sender, String key) {
        Log.e(TAG, key);
        Log.e(TAG, sender.get(key).toString());
    }

}

请记住(取消)在onPauseChildEventListener中注册您的回调,以避免{{3}}次更新导致的内存泄漏。