2016-06-18 46 views
8

Ho seguito questo tutorial per impostare l'istanza di Google App Engine e sto anche utilizzando Firebase. Il mio obiettivo è mettere tutto il "computo" su Google App Engine. Voglio chiamare una funzione come questa qui sotto:Database call Firebase da Google App Engine

MyEndpoint:

package productions.widowmaker110.backend; 

/** An endpoint class we are exposing */ 
@Api(
name = "myApi", 
version = "v1", 
namespace = @ApiNamespace(
ownerDomain = "backend.widowmaker110.productions", 
ownerName = "backend.widowmaker110.productions", 
packagePath="" 
) 
) 
public class MyEndpoint { 

/** A simple endpoint method that takes a name and says Hi back */ 
@ApiMethod(name = "sayHi") 
public MyBean sayHi(@Named("name") String name) { 

// Write a message to the database 
FirebaseDatabase database = FirebaseDatabase.getInstance(); 
DatabaseReference myRef = database.getReference("message"); 

// Read from the database 
myRef.addValueEventListener(new ValueEventListener() { 
@Override 
public void onDataChange(DataSnapshot dataSnapshot) { 
// This method is called once with the initial value and again 
// whenever data at this location is updated. 
String value = dataSnapshot.getValue(String.class); 
Log.d(TAG, "Value is: " + value); 
} 

@Override 
public void onCancelled(DatabaseError error) { 
// Failed to read value 
Log.w(TAG, "Failed to read value.", error.toException()); 
} 
}); 

MyBean response = new MyBean(); 
response.setData("Hi, " + name); 

return response; 
} 

} 

MainActivity:

package productions.widowmaker110.gpsweather; 

// imports... 

public class MainActivity extends AppCompatActivity { 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    new EndpointsAsyncTask().execute(new Pair<Context, String>(this, "Manfred")); 
} 

class EndpointsAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> { 
    private MyApi myApiService = null; 
    private Context context; 

@Override 
protected String doInBackground(Pair<Context, String>... params) { 
    if(myApiService == null) { // Only do this once 
     MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(), 
     new AndroidJsonFactory(), null) 
     // options for running against local devappserver 
     // - 10.0.2.2 is localhost's IP address in Android emulator 
     // - turn off compression when running against local devappserver 
    .setRootUrl("http://10.0.2.2:8080/_ah/api/") 
    .setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() { 
@Override 
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException { 
    abstractGoogleClientRequest.setDisableGZipContent(true); 
} 
}); 
// end options for devappserver 

myApiService = builder.build(); 
} 

context = params[0].first; 
String name = params[0].second; 

try { 
return myApiService.sayHi(name).execute().getData(); 
} catch (IOException e) { 
return e.getMessage(); 
} 
} 

@Override 
protected void onPostExecute(String result) { 
Toast.makeText(context, result, Toast.LENGTH_LONG).show(); 
} 
} 
} 

Capisco il codice qui sopra per Firebase è specifico Android in modo che funziona su un Google App Engine l'istanza non funziona. Mi chiedevo se qualcuno sa come eseguire le operazioni CRUD su un database Firebase dal back-end di Google App Engine. Qualsiasi aiuto è apprezzato.

risposta

11

È necessario utilizzare l'SDK del server Firebase per effettuare chiamate lato server. È possibile trovare informazioni sulla sua configurazione e l'utilizzo qui:

Add Firebase to your Server

Firebase Server SDK Installation & Setup

Quando si utilizza Firebase con Google Cloud endpoint essere consapevoli che è necessario utilizzare metodi Firebase in combinazione con il Tasks API . Poiché i metodi Firebase non si bloccano, se non si utilizzano le attività l'endpoint tornerà prima che la chiamata effettuata a Firebase abbia la possibilità di restituire il risultato. Per una breve introduzione sull'uso delle attività controlla il link sottostante. È un discorso tenuto a Google I/O 2016. L'oratore parla di Tasks e Firebase su Android ma i concetti sono gli stessi quando si utilizzano Tasks e Firebase su un server. Si noti che hanno incluso l'API Tasks con l'SDK di Firebase Server. Ho saltato alla parte del discorso che si occupa direttamente delle attività.

Firebase SDK for Android: A tech deep dive

I campioni che seguono sono, se è necessario elaborare il risultato dalla tua Firebase operazioni di lettura/scrittura prima della Endpoint restituisce un valore o se altro codice dipende dal risultato della Firebase di lettura/scrittura. Questi sono esempi lato server. Immagino che ti interessi se l'operazione di scrittura abbia avuto o meno esito positivo. Ci possono essere modi migliori per eseguire queste operazioni sul lato server, ma questo è quello che ho fatto finora.

funzionamento Esempio di scrittura utilizzando setValue():

DatabaseReference ref = FirebaseDatabase.getInstance().getReference(); 
YourModelClass obj = new YourModelClass(); 
  1. Quando viene effettuata una chiamata al metodo setValue() restituisce un oggetto Task, mantenere un riferimento ad esso.

    Task<Void> setValueTask = ref.setValue(obj); 
    
  2. Creare un oggetto TaskCompletionSource. Questo oggetto dovrebbe essere parametrizzato con il tipo di risultato che hai scelto. Userò Boolean come tipo di risultato per questo esempio.

    final TaskCompletionSource<Boolean> tcs = new TaskCompletionSource<>(); 
    
  3. generare un Task dal TaskCompletionSource creata nel passaggio 2. Anche in questo caso, il generato Task deve utilizzare lo stesso tipo di parametro come oggetto TaskCompletionSource.

    Task<Boolean> tcsTask = tcs.getTask(); 
    
  4. aggiungere un listener di completamento al Task che è stato generato dalla chiamata a setValue(). Nel listener di completamento è possibile impostare il risultato appropriato su Task creato nel passaggio 3. La chiamata di setResult() sull'oggetto TaskCompletionSouce segnerà il Task creato da esso come completo. Questo è importante per il passo 5.

    setValueTask.addOnCompleteListener(new OnCompleteListener<Void>() { 
        @Override 
        public void onComplete(@NonNull Task<Void> task) { 
         if(task.isSuccessful()){ 
         tcs.setResult(true); 
         }else{ 
         tcs.setResult(false); 
         } 
        } 
    }); 
    
  5. chiamata Task.await() per bloccare il thread corrente fino a quando il Task siete interessati è stata completata. Stiamo aspettando che lo Task generato dall'oggetto TaskCompletionSource sia contrassegnato come completo. Questo Task sarà considerato completo quando chiamiamo setResult() sullo TaskCompletionSource utilizzato per generare lo Task come abbiamo fatto nel passaggio 4. Una volta completato restituirà il risultato.

    try { 
        Boolean result = Tasks.await(tcsTask); 
    }catch(ExecutionException e){ 
        //handle exception 
    }catch (InterruptedException e){ 
        //handle exception 
    } 
    

Ecco, il thread corrente bloccherà fino Tasks.await() restituisce un valore. È anche possibile (e dovrebbe) impostare un valore di timeout sul metodo Tasks.await() se si desidera che il thread corrente venga bloccato per un tempo indefinito.

Se foste interessati solo se il Task generato da setValue() completato e non importava se fosse successo o meno, allora è possibile saltare la creazione del TaskCompletionSource e basta usare Tasks.await() direttamente su quel Task. Lo stesso funziona per updateChildren(). E se ti piace, puoi semplicemente usare il metodo per chiamare updateChilden() o setValue() che usa un DatabaseReference.CompletionListener insieme a TaskCompletionListener.

L'attesa di un'operazione di lettura per il completamento è simile.

Esempio operazione di lettura utilizzando addListenerForSingleValueEvent()

DatabaseReference ref = FirebaseDatabase.getInstance().getReference(); 
YourModelClass mModelClassObject; 
  1. Creare un oggetto TaskCompletionSource parametrizzata con il risultato che ci si aspetta dal Task che sarà generato da esso.

    final TaskCompletionSource<YourModelClass> tcs = new TaskCompletionSource<>(); 
    
  2. generare un Task dal TaskCompletionSource oggetto

    Task<YourModelClass> tcsTask = tcs.getTask(); 
    
  3. chiamata addListenerForSingleValueEvent() sul nostro DatabaseReference e chiamare setResult() sul Task generato nel passaggio 2.

    ref.addListenerForSingleValueEvent(new ValueEventListener() { 
        @Override 
        public void onDataChange(DataSnapshot dataSnapshot) { 
         YourModelClass result = dataSnapshot.getValue(YourModelClass.class); 
         if(result != null){ 
          tcs.setResult(result); 
         } 
        } 
    
        @Override 
        public void onCancelled(DatabaseError databaseError){ 
         //handle error 
        } 
    }); 
    
  4. chiamata Tasks.await() a bloccare il curr ent thread finché non è stato completato il Task che è interessato. Il Task sarà considerato completo quando chiamiamo setResult() come abbiamo fatto nel passaggio 3 e restituiremo il risultato.

    try { 
        mModelClassObject = Tasks.await(tcsTask); 
    }catch(ExecutionException e){ 
        //handle exception 
    }catch (InterruptedException e){ 
        //handle exception 
    } 
    

Come accennato in precedenza è possibile utilizzare il metodo Tasks.await() insieme ad un valore di timeout per evitare che il filo corrente dal blocco indefinitamente.

Proprio come un avviso ho scoperto che Firebase non uccide il thread in background che viene utilizzato per le sue operazioni. Ciò significa che l'istanza GAE non è mai inattiva. Dai un'occhiata a questo thread per ulteriori informazioni:

Firebase, Background Threads, and App Engine

+0

Grazie Kevin, ho intenzione di fare un tentativo in questo fine settimana. Apprezzo l'aiuto. Se questo funziona, ti darò la migliore risposta entro 24 ore. – booky99

+0

Nessun problema, quando avrò la possibilità aggiungerò qualche informazione in più sull'utilizzo delle attività perché può essere un po 'complicato. –

+0

Capisco il multithreading in questione ma ho difficoltà a capire come implementare l'API di Google Task. Come hai detto sopra, quando hai la possibilità di condividere queste informazioni, lo apprezzerei molto. – booky99