Это классическая проблема с асинхронными веб-интерфейсами. Вы не можете вернуть то, что еще не загружено. Другими словами, вы не можете просто создать глобальную переменную и использовать ее вне метода onDataChange()
, потому что она всегда будет null
. Это происходит потому, что метод onDataChange()
называется асинхронным.
Но не только Firebase Realtime Database загружает данные асинхронно, почти все современные другие сети API, так как это может занять некоторое время. Поэтому вместо того, чтобы ждать данных (что может привести к невосприимчивым диалоговым окнам приложений для ваших пользователей), ваш основной код приложения продолжается, пока данные загружаются во вторичный поток. Затем, когда данные доступны, вызывается метод onDataChange () и может использовать данные. Другими словами, к тому времени, когда метод onDataChange()
называется, ваши данные еще не загружены.
Давайте возьмем пример, разместив несколько кодов журнала в коде, чтобы более четко видеть, что происходит.
private String getUserName(String uid) {
Log.d("TAG", "Before attaching the listener!");
databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// How to return this value?
dataSnapshot.getValue(String.class);
Log.d("TAG", "Inside onDataChange() method!");
}
@Override
public void onCancelled(DatabaseError databaseError) {}
});
Log.d("TAG", "After attaching the listener!");
}
Если мы запустим этот код, выход wil будет:
Перед присоединением слушателя!
После присоединения слушателя!
Внутри метода onDataChange ()!
blockquote>Это, вероятно, не то, что вы ожидали, но оно точно объясняет, почему ваши данные
null
при возврате.Первоначальный ответ для большинства разработчиков заключается в том, чтобы попытаться «исправить» это
asynchronous behavior
, что я лично рекомендую против этого. Интернет является асинхронным, и чем скорее вы примете это, тем скорее вы сможете узнать, как стать продуктивным с помощью современных веб-API.Я нашел проще всего пересмотреть проблемы для этой асинхронной парадигмы. Вместо того, чтобы говорить «Сначала получить данные, а затем занести в журнал», я создаю проблему как «Начать получать данные. Когда данные загружаются, запишите их». Это означает, что любой код, который требует данных, должен быть внутри метода
onDataChange()
или вызван изнутри, например:databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // How to return this value? if(dataSnapshot != null) { System.out.println(dataSnapshot.getValue(String.class)); } } @Override public void onCancelled(DatabaseError databaseError) {} });
Если вы хотите использовать это снаружи, есть другой подход. Вам нужно создать свой собственный обратный вызов, чтобы ждать, пока Firebase вернет вам данные. Для этого сначала нужно создать
interface
следующим образом:public interface MyCallback { void onCallback(String value); }
Затем вам нужно создать метод, который фактически получает данные из базы данных. Этот метод должен выглядеть так:
public void readData(MyCallback myCallback) { databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { String value = dataSnapshot.getValue(String.class); myCallback.onCallback(value); } @Override public void onCancelled(DatabaseError databaseError) {} }); }
. В конце просто просто вызовите метод
readData()
и передайте экземпляр интерфейсаMyCallback
в качестве аргумента везде, где вам это нужно:readData(new MyCallback() { @Override public void onCallback(String value) { Log.d("TAG", value); } });
Это единственный способ, которым вы можете использовать это значение вне метода
onDataChange()
. Для получения дополнительной информации вы также можете посмотреть это видео .
Самый простой способ - создать граф, содержащий все метаданные, а затем igraph позаботится об остальном. Например,
library(igraph)
answers <- read.table(textConnection(
" Player Q1_I1
1 k 1
2 l 0
3 n 1
4 m 0
"))
topology <- read.table(textConnection(
" Node.1 Node.2
1 k l
2 l k
3 l m
4 m l
5 l n
6 n l
7 n k
8 k n
"))
g2 <- graph.data.frame(topology, vertices=answers, directed=FALSE)
g <- simplify(g2)
V(g)$color <- ifelse(V(g)$Q1_I1 == 1, "lightblue", "orange")
plot(g)
[/g0]
Но на самом деле, если вы не включаете каждое ребро в обоих направлениях в своей таблице данных, тогда вы даже не необходимо упростить.
Проблема в том, что граф сортируется после simplify
, а вектор ответов - нет. Возможно, вам будет проще, но я просто сортирую вашу таблицу ответов: answers <-answers[order(answers[,1]),]
перед установкой V(g)$color <- ifelse(answers[V(g), 2] == 1, "blue", "red")
.
Вы можете видеть, что ваш график отсортирован с помощью get.data.frame(g, what="vertices")
Кроме того, вы могли бы match
имена get.data.frame
(обратите внимание, что я дважды создаю g
. По какой-то причине get.data.frame
не играет хорошо с simplify
.
answers <- read.csv("c:/answers2.csv",header=T)
data1<-read.csv('c:/edges2.csv')
data2<-graph.data.frame(data1, directed=FALSE)
g<-simplify(data2)
ordered.vertices <-get.data.frame(g, what="vertices")
g<-simplify(data2)
V(g)$color <- ifelse(answers[match(answers[,1],ordered.vertices$name), 2] == 1, "blue", "red")
plot(g, layout=layout.fruchterman.reingold, vertex.color=V(g)$color)
[/g0]