Установка значения свойства 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 

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

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; } 
  • Почему объект Object.toString () по умолчанию содержит hash-код?
  • java.lang.ClassNotFoundException при работе в IntelliJ IDEA
  • Как сделать PDF в Android
  • Получение NoClassDefFoundError при использовании common.lang.StringUtils в javas-коде android?
  • Java: как добавить 10 минут в мое время
  • Преобразование формата даты в Android
  • Использование шаблона проектирования Singleton для SQLiteDatabase
  • как я могу обнаружить клавиши со стрелками в java-консоли, а не в графическом интерфейсе?
  • Ошибка конструктора списка Firebase
  • preincrement / postincrement в java
  • Что такое синтаксис?
  • Interesting Posts

    Автоматическая перезагрузка Windows8, если нет активности в Интернете

    Компьютер не запускается, если питание не отключено в течение ~ 5 минут

    Какой цикл имеет лучшую производительность? Зачем?

    В JavaFX как перемещать спрайт по экрану?

    В Windows 7 есть ли способ привязать окно к четверти экрана вместо половины?

    Есть ли ограничение на прикрепленные элементы панели задач?

    Как загрузить сайт с сайта archive.org Wayback Machine?

    Несколько файлов приложений .pk для Android приложений из одного исходного кода

    Scala: Почему mapValues ​​создает представление и есть ли стабильные альтернативы?

    ITerm2 теряет цвета «ls», если я ssh на сервере

    Python 2.7: streamовый HTTP-сервер, поддерживающий несколько соединений на одном порту

    Путаница в преобразовании YUV NV21 в RGB

    Полное использование памяти на графических картах

    Использование одного и того же controllerа на разных элементах для обращения к одному и тому же объекту

    Chrome не может подключиться к Интернету

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