从Google App Engine调用Firebase数据库

8
我按照这个教程设置了我的Google App Engine实例,并且我也在使用Firebase。我的目标是将所有的“计算”都放在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();
 }
 }
}

我明白上面的Firebase代码是专门为Android编写的,因此在Google应用引擎实例上运行不起作用。 我想知道是否有人知道如何从Google应用引擎后端执行CRUD操作以访问Firebase数据库。 如果您能提供任何帮助,将不胜感激。

2个回答

11
您需要使用Firebase服务器SDK进行服务器端调用。您可以在此处找到有关设置和使用它的信息: 将Firebase添加到您的服务器 Firebase服务器SDK安装和设置 使用Google Cloud Endpoints时,请注意您需要与Tasks API一起使用Firebase方法。由于Firebase方法不是阻塞的,如果您不使用任务,则您的终端节点将在您对Firebase发出的调用返回其结果之前返回。有关使用任务的简要介绍,请查看下面的链接。这是在Google I/O 2016上发表的演讲。演讲者正在谈论Android上的任务和Firebase,但是在服务器上使用任务和Firebase时概念是相同的。请注意,他们已经在Firebase服务器SDK中包含了Tasks API。我跳过了直接处理任务的部分。 Firebase Android SDK:技术深入挖掘 以下示例是为了在终端点返回值之前处理Firebase读写操作的结果,或者如果其他代码依赖于Firebase读写操作的结果。这些是服务器端示例。我假设您关心写入操作是否成功。执行这些服务器端操作可能有更好的方法,但这是我目前所做的。

使用setValue()的样本写入操作:

DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
YourModelClass obj = new YourModelClass();
  1. When a call is made to the setValue() method it returns a Task object, keep a reference to it.

    Task<Void> setValueTask = ref.setValue(obj);
    
  2. Create a TaskCompletionSource object. This object should be parameterized with the result type of your choice. I’m going to use Boolean as the result type for this example.

    final TaskCompletionSource<Boolean> tcs = new TaskCompletionSource<>();
    
  3. Generate a Task from the TaskCompletionSource that was created in step 2. Again, the generated Task should use the same parameter type as the TaskCompletionSource object.

    Task<Boolean> tcsTask = tcs.getTask();
    
  4. Add a completion listener to the Task that was generated by the call to setValue(). In the completion listener set the appropriate result on the Task created in step 3. Calling setResult() on your TaskCompletionSouce object will mark the Task created from it as complete. This is important for step 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. Call Task.await() to block the current thread until the Task you are interested in has completed. We are waiting for the Task generated by the TaskCompletionSource object to be marked complete. This Task will be considered complete when we call setResult() on the TaskCompletionSource used to generate the Task as we did in step 4. Once complete it will return the result.

    try {
        Boolean result = Tasks.await(tcsTask);
    }catch(ExecutionException e){
        //handle exception 
    }catch (InterruptedException e){
        //handle exception
    }
    
这就是了,当前线程会一直阻塞,直到 Tasks.await() 返回一个值。如果你不想让当前线程无限期地被阻塞,你也可以(而且应该)在 Tasks.await() 方法上设置超时值。
如果你只对由 setValue() 生成的 Task 是否完成感兴趣,并且不关心它是否成功,那么你可以跳过创建 TaskCompletionSource 的步骤,直接在那个 Task 上直接使用 Tasks.await()。对于 updateChildren() 也是同样的道理。如果你愿意,你可以直接使用带有 DatabaseReference.CompletionListener 和 TaskCompletionListener 的 updateChilden()setValue() 方法调用。
等待读取操作完成的过程类似。 使用addListenerForSingleValueEvent()的示例读取操作
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
YourModelClass mModelClassObject;
  1. Create a TaskCompletionSource object parameterized with the result you expect from the Task that will be generated from it.

    final TaskCompletionSource<YourModelClass> tcs = new TaskCompletionSource<>();
    
  2. Generate a Task from the TaskCompletionSource object

    Task<YourModelClass> tcsTask = tcs.getTask();
    
  3. Call addListenerForSingleValueEvent() on our DatabaseReference and call setResult() on the Task generated in step 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. Call Tasks.await() to block the current thread until the Task you are interested in has completed. The Task will be considered complete when we call setResult() as we did in step 3 and will return the result.

    try {
        mModelClassObject = Tasks.await(tcsTask);
    }catch(ExecutionException e){
        //handle exception 
    }catch (InterruptedException e){
        //handle exception
    }
    
如上所述,您可以使用Tasks.await()方法以及超时值来防止当前线程无限制地被阻止。
请注意,我发现Firebase不会终止用于其操作的后台线程。这意味着GAE实例从未处于空闲状态。有关更多信息,请查看此主题: Firebase、后台线程和App Engine

我理解其中涉及到的多线程,但是我很难理解如何实现任务API。就像你上面所说的,当你有机会分享这些信息时,我会非常感激的。 - booky99
哇!感谢更新。我今晚会试一试。非常深入。 - booky99
1
@KevinO'Neil 很棒的回答。非常全面。 - Codevalley
@Codevalley 谢谢! - Kevin O'Neil
嘿@KevinO'Neil,感谢您的详细回答!但这是否也适用于标准应用程序引擎环境,还是仅适用于弹性环境? - Niels
显示剩余4条评论

0

更新2023

尽管Kevin在2016年的答案是正确的,但事情从那时起发生了很大变化。

SDK在5.4.0之后提供了两个功能。

setValueAsync(Object value)

并且

setValue(Object, CompletionListener)

尽管有些答案(here)似乎表明第一个是异步的,而第二个是阻塞的,但在我对Google Cloud App Engine上的测试中并非如此。
实际情况是它们都是非阻塞的,旧的函数似乎是为基于回调的编程风格而存在的。
新的函数"setValueAsync"基于ApiFuture接口。虽然是异步的,但我们可以通过调用返回的ApiFuture中的get()方法轻松等待其完成。因此,Kevin的代码变得简单了:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
ApiFuture future = ref.setValueAsync(YourObject);
future.get();

你的代码在调用get()函数后不会继续执行,直到ApiFuture返回。
阅读Firebase: 使用Admin Java SDK进行异步操作以获取更多关于这些变化的详细信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接