仅在firebase调用完成时加载布局

JCL*_*oot 6 android firebase firebase-realtime-database

问题是:在我有机会完成调用firebase以恢复应用程序数据之前,我的MainActivity中的布局已生成.如果我旋转屏幕,从而导致onCreate在MainActivity中再次运行,则生成的一切都很好.

在我的应用程序中,我有一个Application类的自定义实现,它会对Firebase进行大量调用以恢复数据/确保数据始终保持同步.但是,我有大约20个ValueEventListener,而不是只有几个ValueEventListener,下面有大量的子代.这是为了防止我的应用程序在每次用户生成一小部分数据时几乎同步整个数据库,并避免在异步操作数据时可能发生的冲突.有趣的是,ValueEventListener实际上并没有按照它们编码的顺序获取它们的数据,所以当最后一个完成时我不能将bool设置为true.

我想知道是否有一种简单的方法来检测firebase读取是否全部完成,除了在每个侦听器的末尾放置一些代码并执行手动执行此操作的操作.我已经查看了Application类中可用的方法以及Firebase,到目前为止我还没有找到任何有效的方法.

我的Application类中的一些代码:

public class CompassApp extends Application {
Run Code Online (Sandbox Code Playgroud)

...然后在应用程序的onCreate中:

//        Fetching Data from DB.
    FirebaseDatabase database = FirebaseDatabase.getInstance();
    DatabaseReference dbRef = database.getReference();

//        Current User Data
    dbRef.child("currentAppData").child("workingOut").addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            activeFirebaseConnections += 1;
//                Stops executing method if there is no data to retrieve
            if (!dataSnapshot.exists()) {
                return;
            }

            workingOut = dataSnapshot.getValue(boolean.class);

            activeFirebaseConnections -= 1;

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
            Log.e(TAG, "Firebase read of sleepDebt failed");
        }
    });
    dbRef.child("currentAppData").child("sleeping").addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            activeFirebaseConnections += 1;
//                Stops executing method if there is no data to retrieve
            if (!dataSnapshot.exists()) {
                return;
            }

            sleeping = dataSnapshot.getValue(boolean.class);

            activeFirebaseConnections -= 1;

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
            Log.e(TAG, "Firebase read of sleepDebt failed");
        }
    });
Run Code Online (Sandbox Code Playgroud)

(依此类推......其余的只是更多的ValueEventListeners)

Dou*_*son 14

我目前正在使用我创建的实用程序类来启动多个数据加载.当所有侦听器都完成时,它将触发最终任务.这里的关键是巧妙地使用Play服务提供的异步编程的新任务工具.您可以使用其他一些异步框架完成所有这些操作,但是使用Play服务免费提供任务并在Firebase的其他部分使用,因此您也可以学习使用它们.:-)

我在Google I/O 2016上发表了关于任务的演讲.这是一个直接跳转到该会话相关部分的视频的链接.

public class FirebaseMultiQuery {

    private final HashSet<DatabaseReference> refs = new HashSet<>();
    private final HashMap<DatabaseReference, DataSnapshot> snaps = new HashMap<>();
    private final HashMap<DatabaseReference, ValueEventListener> listeners = new HashMap<>();

    public FirebaseMultiQuery(final DatabaseReference... refs) {
        for (final DatabaseReference ref : refs) {
            add(ref);
        }
    }

    public void add(final DatabaseReference ref) {
        refs.add(ref);
    }

    public Task<Map<DatabaseReference, DataSnapshot>> start() {
        // Create a Task<DataSnapsot> to trigger in response to each database listener.
        //
        final ArrayList<Task<DataSnapshot>> tasks = new ArrayList<>(refs.size());
        for (final DatabaseReference ref : refs) {
            final TaskCompletionSource<DataSnapshot> source = new TaskCompletionSource<>();
            final ValueEventListener listener = new MyValueEventListener(ref, source);
            ref.addListenerForSingleValueEvent(listener);
            listeners.put(ref, listener);
            tasks.add(source.getTask());
        }

        // Return a single Task that triggers when all queries are complete.  It contains
        // a map of all original DatabaseReferences originally given here to their resulting
        // DataSnapshot.
        //
        return Tasks.whenAll(tasks).continueWith(new Continuation<Void, Map<DatabaseReference, DataSnapshot>>() {
            @Override
            public Map<DatabaseReference, DataSnapshot> then(@NonNull Task<Void> task) throws Exception {
                task.getResult();
                return new HashMap<>(snaps);
            }
        });
    }

    public void stop() {
        for (final Map.Entry<DatabaseReference, ValueEventListener> entry : listeners.entrySet()) {
            entry.getKey().removeEventListener(entry.getValue());
        }
        snaps.clear();
        listeners.clear();
    }

    private class MyValueEventListener implements ValueEventListener {
        private final DatabaseReference ref;
        private final TaskCompletionSource<DataSnapshot> taskSource;

        public MyValueEventListener(DatabaseReference ref, TaskCompletionSource<DataSnapshot> taskSource) {
            this.ref = ref;
            this.taskSource = taskSource;
        }

        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            snaps.put(ref, dataSnapshot);
            taskSource.setResult(dataSnapshot);
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
            taskSource.setException(databaseError.toException());
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

你使用它的方式是这样的.确定DatabaseReference从中加载数据并将其存储在Activity成员中所需的所有内容.然后,在您的活动期间onStart(),将它们全部传递到FirebaseMultiQuery的实例并调用start()它.它将返回一个Task,它将生成一个DatabaseReference Map到它将生成的DataSnapshot.有了该Task,请注册一个最终的侦听器,该侦听器将在加载所有数据时触发:

firebaseMultiQuery = new FirebaseMultiQuery(dbRef1, dbRef2, dbRef3, ...);
final Task<Map<DatabaseReference, DataSnapshot>> allLoad = firebaseMultiQuery.start();
allLoad.addOnCompleteListener(activity, new AllOnCompleteListener());
Run Code Online (Sandbox Code Playgroud)

在你的onStop(),你不要忘记呼吁stop()它确保一切都正常关闭:

firebaseMultiQuery.stop();
Run Code Online (Sandbox Code Playgroud)

接收到地图中所有数据的任务完成触发的侦听器可能如下所示:

private class AllOnCompleteListener implements OnCompleteListener<Map<DatabaseReference, DataSnapshot>> {
    @Override
    public void onComplete(@NonNull Task<Map<DatabaseReference, DataSnapshot>> task) {
        if (task.isSuccessful()) {
            final Map<DatabaseReference, DataSnapshot> result = task.getResult();
            // Look up DataSnapshot objects using the same DatabaseReferences you passed into FirebaseMultiQuery
        }
        else {
            exception = task.getException();
            // log the error or whatever you need to do
        }
        // Do stuff with views
        updateUi();
    }
}
Run Code Online (Sandbox Code Playgroud)

值得注意的是Firebase数据库调用写入数据也会返回可以监听完成的任务.此外,Firebase存储使用任务,Firebase身份验证也是如此.

此处的代码目前可能无法使用Firebase数据库查询,该查询不是数据库中节点的简单命名引用.这取决于它们是否以对键入HashMap的方式进行哈希处理.

上面的代码仍然有点实验性,但到目前为止我还没有遇到任何问题.它看起来像很多代码,但是如果你做很多并发加载,它真的很方便.


Sid*_*ghe 5

我的应用程序遇到了同样的问题.经过长时间研究如何检查firebase是否完成检索数据,我找到了这个解决方案.

因此,当您的应用中有多个事件侦听器时.Firebase不会按顺序逐个执行此侦听器.
Value Event Listener总是最后执行.
因此,如果要在检索所有数据时执行某些操作,可以调用ValueEventListener并执行一个方法,该方法将所有视图加载到其中.

因此rootRef.addListenerForSingleValueEvent(),在onDataChanged()从Firebase检索数据后,使用并执行其中要执行的内容.

这对我有用,希望它也适合你