Все объекты гарантированно имеют метод .equals()
, поскольку Object содержит метод, .equals()
, который возвращает логическое значение. Задача подкласса переопределять этот метод, если требуется дополнительное определение определения. Без него (т. Е. С помощью ==
) только адреса памяти проверяются между двумя объектами для равенства. String переопределяет этот метод .equals()
и вместо использования адреса памяти возвращает сравнение строк на уровне символа для равенства.
Ключевое замечание состоит в том, что строки хранятся в одном пуле, поэтому после создания строки он всегда хранится в программе по тому же адресу. Строки не меняются, они неизменяемы. Вот почему это плохая идея использовать регулярную конкатенацию строк, если у вас есть серьезное количество обработки строк. Вместо этого вы будете использовать предоставленные классы StringBuilder
. Помните, что указатели на эту строку могут измениться, и если вам было интересно увидеть, были ли два указателя одинаковыми ==
, это был бы прекрасный способ. Строки сами не делают.
Это классическая проблема с асинхронными веб-интерфейсами. Вы не можете вернуть то, что еще не загружено. Другими словами, вы не можете просто создать глобальную переменную и использовать ее вне метода 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()
. Для получения дополнительной информации вы также можете посмотреть это видео .
Вот сумасшедшая идея, внутри onDataChange, поместите ее внутри TextView с видимостью ушедшей textview.setVisiblity (Gone) или что-то еще, XD затем сделайте что-то вроде
textview.setText (dataSnapshot.getValue (String. class))
, а затем получить его с textview.getText (). toString ()
просто сумасшедшая простая идея.
Полагаю, я понимаю, о чем вы спрашиваете. Хотя вы говорите, что хотите «вернуть» его (по сути) из метода выборки, достаточно сказать, что вы просто хотите использовать значение, полученное после завершения выборки. Если это так, это то, что вам нужно сделать:
. После того, как ваша выборка завершится успешно, вы можете делать много вещей с переменной. 4а и 4b - некоторые простые примеры:
4a. Изменить. В качестве примера использования вы можете запустить все, что вам нужно для запуска в вашем классе, который использует yourNameVariable
(и вы можете быть уверены, что yourNameVariable
не является нулевым)
4b. Изменить: в качестве примера использования вы можете использовать переменную в функции, которая запускается кнопкой onClickListener.
Попробуйте это.
// 1. Create a variable at the top of your class
private String yourNameVariable;
// 2. Retrieve your value (which you have done mostly correctly)
private void getUserName(String uid) {
databaseReference.child(String.format("users/%s/name", uid))
.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
// 3. Set the public variable in your class equal to value retrieved
yourNameVariable = dataSnapshot.getValue(String.class);
// 4a. EDIT: now that your fetch succeeded, you can trigger whatever else you need to run in your class that uses `yourNameVariable`, and you can be sure `yourNameVariable` is not null.
sayHiToMe();
}
@Override
public void onCancelled(DatabaseError databaseError) {}
});
}
// (part of step 4a)
public void sayHiToMe() {
Log.d(TAG, "hi there, " + yourNameVariable);
}
// 4b. use the variable in a function triggered by the onClickListener of a button.
public void helloButtonWasPressed() {
if (yourNameVariable != null) {
Log.d(TAG, "hi there, " + yourNameVariable);
}
}
Затем вы может использовать yourNameVariable
, где бы вы ни захотели на протяжении всего вашего класса.
Примечание: просто убедитесь, что вы проверяете, что yourNameVariable
не является нулевым при его использовании, поскольку onDataChange
является асинхронным и может не завершиться в то время, когда вы пытаетесь использовать его в другом месте.
yourNameVariable
всегда null
при использовании & quot ;, где бы вы ни находились по всему классу & quot ;. Вы можете использовать его только в своем методе onDataChange()
. Вот и все.
– Alex Mamo
17 December 2017 в 10:32
yourNameVariable
всегда null
. & Quot; Для тех, кто ищет более легкое решение (которое не требует определения интерфейса), это работает хорошо. После того, как переменная была восстановлена и установлена, они могут запускать любые функции, которые требуют результата, и знают, что переменная не является null
(если она существует в firebase в первую очередь). Я обновил свой пример, чтобы вы могли увидеть конкретный прецедент.
– Rbar
17 December 2017 в 20:08
Log.d(TAG, "hi there, " + yourNameVariable);
внутри метода onDataChange()
. Это означает, что при запуске метода onDataChange()
вы вызываете этот метод.
– Alex Mamo
18 December 2017 в 09:00
yourNameVariable will be be always null when using "wherever you would like throughout your class".
не относится ко всем случаям ... (продолжение)
– Rbar
18 December 2017 в 19:47
Future
, так как нам нужно использоватьnewSingleThreadExecutor()
, который создает другой поток, и поскольку api асинхронен, то он уже находится в потоке в java, и мы должны также поддерживайте эту тему и пишите больше кода. – Peter Haddad 31 July 2018 в 19:43