TL;博士

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

因此,我们要做的就是:

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


完整问题

根据Firebase documentation,要写一个值,我必须通过a 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可能吗?

最佳答案

TL;博士

我将ObservableArrayMapChildEventListener合并为FirebaseArrayMap

一个可用的FirebaseUI-Android示例可用here。现在您要做的就是:

// 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);


记住要在OnMapChangedCallbackonResume中取消注册onPause,以避免由ChildEventListener远程更新引起的内存泄漏。

欺诈

首先,我们需要为Map创建一个同义词库接口,即:

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();

}


此接口将由FirebaseArrayMap实现,并返回与Map返回的值相同的值。

这些方法类似于putputAllget,但是它们不是返回值,而是返回带有那些值(或异常)的Tasks。例:

    @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

我们创建一个抽象FirebaseArrayMap来扩展ObservableArrayMap并实现ChildEventListenerCRUD

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;
    }


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

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

    @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());
    }


合约

为了不破坏CRUD合约,需要Map接口。例如,当在给定位置插入新值时,put返回先前的值,但是此操作现在是异步的。

对于写操作,这里的黑客使用CRUD进行非阻塞操作,并使用Map进行阻塞操作:

    @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;
        }
    }


数据绑定

免费,现在您的Android Data Binding也有Map

@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());
    }

}


请记住在onResumeonPause中取消注册回调,以免由于ChildEventListener更新导致的内存泄漏。

09-18 03:49