如何使用数据绑定库将 Firebase 侦听器转换为可观察地图?
How can I turn Firebase listeners into observable map using data binding library?
TL;博士
是否可以隐藏所有Firebase read and write operations behind a ObservableMap
as a Facade Pattern?
所以我们要做的就是:
User oldUser = map.put(user);
User newUser = map.get(primaryKey);
完整问题
根据Firebase documentation, in order to write a value, I would have to define a resource path through aDatabaseReference
设置一个值。
例如,如果我们有一个 User
纯文本对象,我们会将其设置为:
mDatabase.child("users")
.push()
.setValue(user);
为了读取整个 users
树,我们必须实现 ChildEventListener
. As soon as the new user be part of the tree, it would be received through 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
as a Facade Pattern, hidding all Firebase read and write operations 吗?
TL;博士
我合并了ObservableArrayMap
and ChildEventListener
into a FirebaseArrayMap
.
工作 FirebaseUI-Android example is available 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);
记得(取消)注册您的 OnMapChangedCallback
in onResume
and onPause
in order to avoid memory leaking caused by 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
相同的值 ].
这些方法类似于 put
, putAll
and get
, but instead of return values they return 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
extending ObservableArrayMap
and implementing both ChildEventListener
and CRUD
。
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
will use the super methods, turning the 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
returns在给定位置插入新值时的前一个值,但是现在这个操作是异步.
对于写操作,这里的技巧是使用 CRUD
for non blocking and 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 数据绑定 用于您的 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());
}
}
记得在 onResume
and onPause
in order to avoid memory leaking caused by ChildEventListener
更新中(取消)注册您的回调。
TL;博士
是否可以隐藏所有Firebase read and write operations behind a ObservableMap
as a Facade Pattern?
所以我们要做的就是:
User oldUser = map.put(user);
User newUser = map.get(primaryKey);
完整问题
根据Firebase documentation, in order to write a value, I would have to define a resource path through aDatabaseReference
设置一个值。
例如,如果我们有一个 User
纯文本对象,我们会将其设置为:
mDatabase.child("users")
.push()
.setValue(user);
为了读取整个 users
树,我们必须实现 ChildEventListener
. As soon as the new user be part of the tree, it would be received through 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
as a Facade Pattern, hidding all Firebase read and write operations 吗?
TL;博士
我合并了ObservableArrayMap
and ChildEventListener
into a FirebaseArrayMap
.
工作 FirebaseUI-Android example is available 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);
记得(取消)注册您的 OnMapChangedCallback
in onResume
and onPause
in order to avoid memory leaking caused by 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
相同的值 ].
这些方法类似于 put
, putAll
and get
, but instead of return values they return 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
extending ObservableArrayMap
and implementing both ChildEventListener
and CRUD
。
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
will use the super methods, turning the 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
returns在给定位置插入新值时的前一个值,但是现在这个操作是异步.
对于写操作,这里的技巧是使用 CRUD
for non blocking and 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 数据绑定 用于您的 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());
}
}
记得在 onResume
and onPause
in order to avoid memory leaking caused by ChildEventListener
更新中(取消)注册您的回调。