[我经常发现自己在使用Firebase实时数据库时在多个活动中一次又一次地编写这段代码:
ValueEventListener v =new ValueEventListener() {
@Override
public void onDataChange (@NonNull DataSnapshot dbSnapshot){
String ourKey="";
String ourValueID="";
for (DataSnapshot childSnap : dbSnapshot.getChildren()) {
String childKey = childSnap.getKey();
if (childKey == null) {
//do some stuff 1 // and break/Continue/return
}
//or we can directly do something here, as we already assured key is present
else if(childKey.equals(ourKey)){
//do some stuff 2 // and break/Continue/return
MyClass myClass =childSnap.getValue(MyClass.class);
if(myClass==null){
//do some stuff 3 // and break/Continue/return
}
else if(myClass.getID().equals(ourValueID)){
//do some stuff 4 // and break/Continue/return
}
else {
//do some stuff 5 // and break/Continue/return
}
}
else {
//do some stuff 6 // and break/Continue/return
}
}
}
@Override
public void onCancelled (@NonNull DatabaseError databaseError){
//do some stuff 7
}
};
尽管这假定是firebase的工作方式,但它使我的代码更加难以阅读和调试。以某种方式使用这些回调的好方法是,我只编写一次此代码并整理我的代码库?一个示例将是很好的。
在onDataChange()
内部,您可以只调用一个方法:
ValueEventListener v =new ValueEventListener() {
@Override
public void onDataChange (@NonNull DataSnapshot dbSnapshot){
String ourKey="";
String ourValueID="";
retrieveDataFromFb(dbSnapshot);
public void retrieveDataFromFb(DataSnapshot dataSnapshot){
for (DataSnapshot childSnap : dbSnapshot.getChildren()) {
String childKey = childSnap.getKey();
if (childKey == null) {
//do some stuff 1 // and break/Continue/return
}
else if(childKey.equals(ourKey)){
MyClass myClass =childSnap.getValue(MyClass.class);
}
}
据我了解,您希望将所有数据库方法存储在单独的类中,以便可以重用这些方法,这将使代码看起来更整洁,并且您尝试从Firebase返回它们时获取回调值。
有很多方法可以处理事件的回调,我建议是使用接口,它将使您的代码模块化并使其看起来更简洁,因此您可以将数据库方法存储在单独的类中(例如FirebaseDB),在此处创建方法并使用接口获取回调。有关如何执行此操作的示例:-
在类中或与类分开创建接口
public class FirebaseDB {
//This is your interface
public interface DBCallbacklistner {
void onCallback(Map<String, Object> keyMap);
}
public void getkeys(String any_value_you_need_to_pass, DBCallbacklistner dbCallbacklistner){
//I have used a different method here you can use your releveant method here
database.somemethod(any_value_you_need_to_pass, new EventListener<DocumentSnapshot>() {
@Override
public void onEvent(@Nullable DocumentSnapshot documentSnapshot) {
//Suppose you receive the callback here
if(documentSnapshot.exists()){
Map<String, Object> keysMap = (HashMap<String, Object>) documentSnapshot.getData();
//Pass the callback in your interface
dbCallbacklistner.onCallback(keysMap);
}
}
});
}
}
随时随地使用该界面
使用类中的函数调用该接口并使用值
mFirebaseDBObject.getkeys(value, new FirebaseDB.DBCallbacklistner() {
@Override
public void onCallback(Map<String, Object> keyMap) {
if (keyMap != null) {
//Use your keymap here
}
}
});
我想指出的另一件事是,如果不同的调用有太多的回调,我建议根据回调的逻辑分隔来创建单独的接口。因为如果单个接口中有很多回调,那么无论是否需要,都必须覆盖它们中的每个回调。
目前,我正在使用以下方法:
假设我的firebase数据库由可以反序列化为以下格式的对象列表组成:
class MyClass{
public String myClassUniqueID;
... other attributes;
}
对于数据库,我将在自己的活动中处理所有值事件侦听器的生命周期(即通过dbRef.addValueEventListener(dbListener);
或dbRef.removeEventListener(dbListener);
附加到数据库引用,但是创建此dbListener
并将其传递给必要的任务的过程将在以下实用程序功能中进行管理:
public interface DbListenerActions {
void onMyClassObjFound(@NonNull MyClass matchedObj);
default void onMyClassObjNOTFound() {
}
}
public static ValueEventListener getMyClassObjectFinderListener(String id, DbListenerActions actions) {
Log.e(TAG, "onDataChange: our id:" + id);
ValueEventListener dbListener = new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dbSnapshot) {
for (DataSnapshot currChildSnap : dbSnapshot.getChildren()) {
String currChildKey = currChildSnap.getKey();
MyClass currChildValue = currChildSnap.getValue(MyClass.class);
if (currChildKey == null) {
Log.e(TAG, "onDataChange: currChildKey is null. continuing");
continue;
}
if (currChildValue == null) {
Log.e(TAG, "onDataChange: currChildValue is null.continuing");
continue;
}
if (currChildValue.myClassUniqueID.equals(id)) {
Log.e(TAG, "onDataChange: currChildValue id matches our id ");
Log.e(TAG, "onDataChange: performing action and RETURNING(i.e getting out of this callback)");
//do stuff here
actions.onMyClassObjFound(currChildValue);
return;
} else {
Log.e(TAG, "onDataChange: current obj DOES NOT matches our id. continuing");
Log.e(TAG, "onDataChange: current object ID :" + currChildValue.myClassUniqueID);
Log.e(TAG, "onDataChange: --------------------------------------------------------------");
continue;
}
}
Log.e(TAG, "onDataChange: user not found, performing not found action" );
actions.onMyClassObjNOTFound();
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
};
return dbListener;
}
这样,我能够在调试期间获取所需的日志信息,并且由于我只想执行2种可能的操作,因此我对监听器的工作有了更多的保证。将11个活动中的50行必要但冗余的代码组合在一起,只有1个实用程序功能!
现在我需要写的只是这个小巧的代码,在我的每个活动中都更易于调试:
ValueEventListener dbListener=getMyClassObjectFinderListener("some_id", new DbListenerActions() {
@Override
public void onMyClassObjFound(@NonNull MyClass matchedObj) {
//callSomeFunction()
// callSomeOtherFunction(matchedObj)
//...
}
});
因为我做了onMyClassObjNOTFound(..)
函数default
,所以我什至不需要提供它,除非我真的想在那里执行一些操作。所以整个事情对我来说都很好:D
我也在Twitter上问了这件事,有人告诉我,抽象类也可以用于这件事。我在那里不需要做更多的研究,但是如果有人也知道这种方法,那么请告诉我!