Установка значения свойства Singleton в Firebase Listener

В настоящее время я тестирую Firebase вместе с моделью Singleton, которую планирую использовать для доступа во время жизненного цикла всего приложения. Я теперь застрял с чем-то, что кажется действительно тривиальным, но я не могу понять его жизнь. У меня есть образец модели, которую я использую: Закладки в firebase.

public class BookSingleton { private static BookSingleton model; private ArrayList bookmarks = new ArrayList(); public static BookSingleton getModel() { if (model == null) { throw new IllegalStateException("The model has not been initialised yet."); } return model; } public ArrayList theBookmarkList() { return this.bookmarks; } public void setBookmarks(ArrayList bookmarks){ this.bookmarks = bookmarks; } public void loadModelWithDataFromFirebase(){ Firebase db = new Firebase(//url); Firebase bookmarksRef = fb.child(//access correct child); final ArrayList loadedBookmarks = new ArrayList(); bookmarksRef.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { //getting all properties from firebase... Bookmark bookmark = new Bookmark(//properties here); loadedBookmarks.add(bookmark); } } //bookmarks still exist here at this point setBookmarks(loadedBookmarks); } @Override public void onCancelled(FirebaseError firebaseError) { } }); //by now loadedBookmarks is empty //this is probably the issue? //even without this line bookmarks is still not set in mainactivity setBookmarks(loadedBookmarks); } 

Теперь, когда я запускаю mainActivity с экземпляром набора Singleton, я получаю нулевую ошибку, потому что явно функция, которую я написал для загрузки данных модели из firebase, ничего не устанавливает.

Что-то вроде этого: MainActivity

 public class MainActivity extends AppCompatActivity { private BookSingleton theModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Load the model theModel = BookSingleton.getModel(this); //manually setting this works // ArrayList bookSamples = new ArrayList; // bookSamples.add(aBookSample); theModel.loadModelWithSampleData(bookSamples); //should have set the singleton model property Bookmarks to the results from firebase theModel.loadModelWithDataFromFirebase(); //returns 0 Log.d(TAG, "" + theModel.theBookmarkList().size()); setContentView(R.layout.activity_main); //......rest of code 

Как я могу сделать эту работу?

3 Solutions collect form web for “Установка значения свойства Singleton в Firebase Listener”

Firebase загружает и синхронизирует данные асинхронно . Таким образом, ваш loadModelWithDataFromFirebase() не ждет завершения загрузки, он просто начинает загрузку данных из базы данных. К моменту возврата функции loadModelWithDataFromFirebase() загрузка еще не завершена.

Вы можете легко протестировать это для себя с помощью некоторых хорошо сделанных записей журнала:

 public void loadModelWithDataFromFirebase(){ Firebase db = new Firebase(//url); Firebase bookmarksRef = fb.child(//access correct child); Log.v("Async101", "Start loading bookmarks"); final ArrayList loadedBookmarks = new ArrayList(); bookmarksRef.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Log.v("Async101", "Done loading bookmarks"); //getting all properties from firebase... Bookmark bookmark = new Bookmark(//properties here); loadedBookmarks.add(bookmark); } @Override public void onCancelled(FirebaseError firebaseError) { } }); Log.v("Async101", "Returning loaded bookmarks"); setBookmarks(loadedBookmarks); } 

Вопреки тому, что вы, скорее всего, ожидаете, порядок операторов журнала будет:

 Start loading bookmarks Returning loaded bookmarks Done loading bookmarks 

У вас есть два варианта решения асинхронного характера этой загрузки:

  1. squash asynchronous баг (обычно сопровождаемый бормотание фразы типа: «это была ошибка, эти люди не знают, что они делают»)

  2. обнимать асинхронного зверя (обычно сопровождаемого довольно часами проклятия, но через некоторое время мирными и лучшими заявлениями)

Возьмите синюю таблетку – сделайте синхронный asynchronous вызов

Если вам нравится выбирать первый вариант, хорошо подобранный примитив синхронизации будет делать трюк:

 public void loadModelWithDataFromFirebase() throws InterruptedException { Firebase db = new Firebase(//url); Firebase bookmarksRef = fb.child(//access correct child); Semaphore semaphore = new Semaphore(0); final ArrayList loadedBookmarks = new ArrayList(); bookmarksRef.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Bookmark bookmark = new Bookmark(//properties here); loadedBookmarks.add(bookmark); semaphore.release(); } @Override public void onCancelled(FirebaseError firebaseError) { throw firebaseError.toException(); } }); semaphore.acquire(); setBookmarks(loadedBookmarks); } 

Обновление (20160303) : когда я только что протестировал это на Android, он заблокировал мое приложение. Он работает на обычном JVM отлично, но Android более тонкий, когда дело доходит до streamовой передачи. Не стесняйтесь попробовать и заставить его работать … или

Возьмите красную таблетку – поговорите с асинхронным характером синхронизации данных в Firebase

Если вы предпочитаете использовать асинхронное программирование, вы должны пересмотреть логику своего приложения.

В настоящее время у вас есть «Сначала загрузите закладки, затем загрузите данные образца, а затем загрузите еще больше».

С асинхронной моделью загрузки вы должны думать, что «всякий раз, когда закладки загружены, я хочу загрузить образцы данных. Всякий раз, когда загруженные образцы данных загружаются, я хочу загрузить еще больше».

Бонус мышления таким образом заключается в том, что он также работает, когда данные могут постоянно меняться и, таким образом, синхронизироваться несколько раз: «Всякий раз, когда изменения закладок меняются, я хочу также загрузить образцы данных. Всякий раз, когда данные образца меняются, я хочу загрузить даже Больше.”

В коде это приводит к вложенным вызовам или цепочкам событий:

 public void synchronizeBookmarks(){ Firebase db = new Firebase(//url); Firebase bookmarksRef = fb.child(//access correct child); final ArrayList loadedBookmarks = new ArrayList(); bookmarksRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { Bookmark bookmark = new Bookmark(//properties here); loadedBookmarks.add(bookmark); setBookmarks(loadedBookmarks); loadSampleData(); } @Override public void onCancelled(FirebaseError firebaseError) { throw firebaseError.toException(); } }); } 

В приведенном выше коде мы не просто ждем единственного события value, но вместо этого имеем дело со всеми из них. Это означает, что всякий раз, когда закладки изменяются, выполняется onDataChange и мы (повторно) загружаем образцы данных (или любое другое действие соответствует потребностям вашего приложения).

Обновление (20171227): Чтобы сделать код более многоразовым, вы можете определить свой собственный интерфейс обратного вызова, вместо того, чтобы называть точный код в onDataChange . Посмотрите на этот anwer для хорошего примера.

TL; DR: Embrace Firebase Asynchronicity

Как я упоминал в другом посте , вы можете иметь дело с асинхронным характером Firebase, используя обещания. Это будет так:

 public Task> synchronizeBookmarks(List bookmarks) { return Tasks.forResult(null) .then(new GetBook()) .then(new AppendBookmark(bookmarks)) .then(new LoadData()) } public void synchronizeBookmarkWithListener() { synchronizeBookmarks() .addOnSuccessListener(this) .addOnFailureListener(this); } 

com.google.android.gms.tasks

Google API для Android предоставляет платформу задач (как и Parse with Bolts ), которая похожа на концепцию обещаний JavaScript .

Сначала вы создаете Task для загрузки закладки из Firebase:

 class GetBook implements Continuation> { @Override public Task then(Task task) { TaskCompletionSource tcs = new TaskCompletionSource(); Firebase db = new Firebase("url"); Firebase bookmarksRef = db.child("//access correct child"); bookmarksRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { tcs.setResult(dataSnapshot.getValue(Bookmark.class)); } }); tcs.getTask(); } } 

Теперь, когда вы получили эту идею, setBookmarks что setBookmarks и loadSampleData также асинхронны. Вы также можете создавать их как задачи Continuation (как и предыдущие), которые будут выполняться последовательно:

 class AppendBookmark(List bookmarks) implements Continuation, Task { final List bookmarks; LoadBookmarks(List bookmarks) { this.bookmark = bookmark; } @Override Task> then(Task task) { TaskCompletionSource> tcs = new TaskCompletionSource(); bookmarks.add(task.getResult()); tcs.setResult(this.bookmarks); return tcs.getTask(); } } class LoadSampleData implements Continuation, List> { @Override public Task> then(Task> task) { // ... } } 

Вы должны инициализировать Singleton при загрузке classа. Поместите это на свой код:

 private static BookSingleton model = new BookSingleton(); private BookSingleton() { } public static BookSingleton getModel() { return model == null ? new BookSingleton() : model; } 
  • Почему методы переопределения не могут исключать исключения, кроме переопределенного метода?
  • Как сделать HTTP-запрос с помощью файлов cookie на Android?
  • После обновления службы Google Play до версии 13 я получил ошибку
  • Когда именно это безопасно для утечки для использования (анонимных) внутренних classов?
  • Продвижение на Java?
  • java.lang.IllegalMonitorStateException: объект не заблокирован streamом перед wait ()?
  • Java Runtime.exec ()
  • Как приложение WhiteList в режиме «Доза» Android 6.0
  • В чем разница между JDK и JRE?
  • Ошибка: Не удалось запустить SDK-инструмент mksdcard
  • Лучший способ сравнить даты в Android
  • Interesting Posts

    “ERROR TypeError: Object (…) не является функцией”, используя AngularFirestore и firebase

    Может ли медиа-запрос изменять размер на основе элемента div вместо экрана?

    Почему MVVM и каковы основные преимущества?

    Есть ли способ отключить «максимизировать» привязку?

    Дженерики – где Т – число?

    Использовать клавиатуру bluetooth для доступа / редактирования BIOS?

    Лучшая практика сохранения настроек приложения в приложении Windows Forms

    Создание базы данных MySQL с Java

    Android: Я использую библиотеку AChartEngine для графиков, но не могу интегрировать графический вид артангина с помощью android xml?

    Как жесткий диск сравнивается с флэш-памятью, работающей как жесткий диск с точки зрения скорости?

    Что произойдет, если вы добавите графическую карту в i7 со встроенной графикой (например, HD 4000)

    Вставка изображения в ggplot2

    Как повторно включить эффекты aero в Windows 7 после того, как он разбился?

    Как представить двойные значения в виде кругов в 2d-матрице в java

    Создание загрузочного компакт-диска из ISO

    Давайте будем гением компьютера.