Установка значения свойства 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; } 
  • Ошибка Android «gps требует ACCESS_FINE_LOCATION», хотя мой файл манифеста содержит это
  • Как разбивать Firestore на Android?
  • Как получить текущий момент в формате ISO 8601 с датой, часом и минутой?
  • Как преобразовать массив байтов в строку и наоборот?
  • В чем разница между List и ArrayList?
  • NoClassDefFoundError в Eclipse
  • Шаблон MVC на Android
  • выполнить команду оболочки из android
  • Что такое IndexOutOfBoundsException? Как я могу это исправить?
  • Скрытие строк в Obfuscated code
  • Уровень заряда батареи и состояние в Android
  • Interesting Posts

    Поддерживается ли домашняя группа Windows 7 (рабочая группа) и домен Windows Vista?

    Недействительная спецификация продолжительности с FFMPEG – что может быть причиной?

    Как рисовать одно и то же движущееся изображение несколько раз?

    Сделать все мои документы с текстом анонимными

    Как сделать одно исключение для исходящего правила брандмауэра Windows

    Как использовать кеширование диска в Picasso?

    Преобразование std :: __ cxx11 :: string в std :: string

    Размеры значков кадров, используемых в Swing

    Как установить пользовательские привязки клавиш и ключевые макросы в Mac OS X?

    Maven: жизненный цикл против фазы против плагина против цели

    Как извлечь файлы из пакета MSI?

    Необходима дополнительная конфигурация для перенаправления ssh-agent?

    Почему выгружать переменные в пакетные файлы Windows

    Странное использование python и / или оператора

    Ярлык клавиатуры для выбора адресной строки в Total Commander

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