2015-07-29 5 views
7

Я пытаюсь обновить части WebView в своем приложении для Android с данными, которые я получаю от однорангового узла, подключенного через Firebase. Для этого было бы полезно выполнить блокирующие операции, которые возвратят необходимые данные. Например, реализация примера чата, который будет ждать, пока другой участник чата ничего не напишет перед возвратом push.setValue(). Возможно ли такое поведение с Firebase?Возможно ли синхронно загружать данные из Firebase?

ответ

8

На обычной JVM вы бы сделали это с помощью обычных примитивов синхронизации Java.

Например:

// create a java.util.concurrent.Semaphore with 0 initial permits 
final Semaphore semaphore = new Semaphore(0); 

// attach a value listener to a Firebase reference 
ref.addValueEventListener(new ValueEventListener() { 
    // onDataChange will execute when the current value loaded and whenever it changes 
    @Override 
    public void onDataChange(DataSnapshot dataSnapshot) { 
     // TODO: do whatever you need to do with the dataSnapshot 

     // tell the caller that we're done 
     semaphore.release(); 
    } 

    @Override 
    public void onCancelled(FirebaseError firebaseError) { 

    } 
}); 

// wait until the onDataChange callback has released the semaphore 
semaphore.acquire(); 

// send our response message 
ref.push().setValue("Oh really? Here is what I think of that"); 

Но это не будет работать на Android. И это хорошая вещь, потому что это плохая идея использовать этот тип блокирующего подхода во всем, что влияет на пользовательский интерфейс. Единственная причина, по которой я лежала в этом коде, - это то, что мне нужно было провести единичный тест.

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

// attach a value listener to a Firebase reference 
ref.addValueEventListener(new ValueEventListener() { 
    // onDataChange will execute when the current value loaded and whenever it changes 
    @Override 
    public void onDataChange(DataSnapshot dataSnapshot) { 
     // TODO: do whatever you need to do with the dataSnapshot 

     // send our response message 
     ref.push().setValue("Oh really? Here is what I think of that!"); 
    } 

    @Override 
    public void onCancelled(FirebaseError firebaseError) { 

    } 
}); 

Конечным результатом является точно такой же, но этот код не требует синхронизации и не блокирует Android.

+0

Thanks Frank! Я согласен с подходом, основанным на блокировании и событиях. Тем не менее, я должен выполнить несколько обновлений для WebView, так же, как это делает WebView.loadDataWithBaseURL() и HTTP-запросы (которые WebView выполняет) также блокируют. Причина, по которой я должен сделать это, а не сам WebView, заключается в том, что сервер недоступен для приложения, и я использую Firebase как «туннель». Любое лучшее решение будет высоко оценено. Кроме того, не разрешается ли решение семафора, если секция блокировки находится внутри события, которое может запускаться каждый раз, когда WebView запрашивает обновление? – Dani

+0

Я почти уверен, что вышеприведенный фрагмент адреса показывает пример чата, который вы дали. Если вы используете-кейс на самом деле разные, предоставьте http://stackoverflow.com/help/mcve, пожалуйста. Код использует описание этого кода каждый день недели. –

+0

Да, действительно! Спасибо @Frank! – Dani

22

package com.google.android.gms.tasks;

Tasks.await (taskFromFirebase);

+4

На самом деле это должен быть принятый ответ. –

+1

Действительно спасибо, я тоже искал это; но как это решить «выполнить операции блокировки, которые будут * возвращать необходимые данные»? Слушатели событий не являются задачами, и как вы это делаете? –

+0

http://stackoverflow.com/questions/37215071/firebase-android-how-to-read-from-different-references-sequently –

3

Я придумал другой способ получения данных синхронно. Пререквизитом должно быть не на тему пользовательского интерфейса.

final TaskCompletionSource<List<Objects>> tcs = new TaskCompletionSource<>(); 

firebaseDatabase.getReference().child("objects").addListenerForSingleValueEvent(new ValueEventListener() { 

      @Override 
      public void onDataChange(DataSnapshot dataSnapshot) { 
       Mapper<DataSnapshot, List<Object>> mapper = new SnapshotToObjects(); 
       tcs.setResult(mapper.map(dataSnapshot)); 
      } 

      @Override 
      public void onCancelled(DatabaseError databaseError) { 
       tcs.setException(databaseError.toException()); 
      } 

     }); 

Task<List<Object>> t = tcs.getTask(); 

try { 
    Tasks.await(t); 
} catch (ExecutionException | InterruptedException e) { 
    t = Tasks.forException(e); 
} 

if(t.isSuccessful()) { 
    List<Object> result = t.getResult(); 
} 

Я протестировал свое решение, и оно отлично работает, но, пожалуйста, докажите, что я ошибаюсь!

+0

, что этот новый SnapshotToObjects(); это любой встроенный класс, или вы создали свой собственный класс. если это ваш собственный класс, вы можете любезно рассказать об этом и его параметр –

+0

Он реализован мной и является всего лишь картографом для DataSnapshot -> List . – juls

+0

можете ли вы любезно показать, что метод –

Смежные вопросы