从 Google App Engine 调用 Firebase 数据库

2024-04-15

我跟着这个tutorial https://cloud.google.com/appengine/docs/java/endpoints/helloendpoints-android-studio用于设置我的 Google App Engine 实例,我也在使用 Firebase。我的目标是将所有“计算”放在 Google App Engine 上。我想调用一个像下面这样的函数:

我的端点:

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;
 }

}

主要活动:

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 App Engine 实例上运行不起作用。我想知道是否有人知道如何执行 CRUD 从 Google App Engine 后端对 firebase 数据库进行操作。任何帮助表示赞赏。


您需要使用 Firebase Server SDK 进行服务器端调用。您可以在此处找到有关设置和使用它的信息:

将 Firebase 添加到您的服务器 https://firebase.google.com/docs/server/setup

Firebase 服务器 SDK 安装和设置 https://firebase.google.com/docs/database/server/start

将 Firebase 与 Google Cloud Endpoints 结合使用时,请注意,您需要将 Firebase 方法与任务API https://firebase.google.com/docs/reference/serverreference/com/google/firebase/tasks/package-summary。由于 Firebase 方法不会阻塞,因此如果您不使用任务,您的端点将在您对 Firebase 进行的调用有机会返回其结果之前返回。有关使用任务的简要介绍,请查看下面的链接。这是 Google I/O 2016 上的一次演讲。演讲者谈论的是 Android 上的任务和 Firebase,但在服务器上使用任务和 Firebase 时的概念是相同的。请注意,他们已将任务 API 包含在 Firebase Server SDK 中。我已经跳到了演讲中直接涉及任务的部分。

适用于 Android 的 Firebase SDK:技术深入探讨 https://youtu.be/AJqakuas_6g?t=386

以下示例适用于您是否需要在端点返回值之前处理 Firebase 读/写操作的结果,或者其他代码取决于 Firebase 读/写操作的结果。这些是服务器端示例。我假设您关心写入操作是否成功。可能有更好的方法来执行这些服务器端操作,但这就是我到目前为止所做的。

使用 setValue() 的示例写入操作:

DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
YourModelClass obj = new YourModelClass();
  1. 当拨打电话时setValue()方法它返回一个Task对象,保留对其的引用。

    Task<Void> setValueTask = ref.setValue(obj);
    
  2. 创建一个TaskCompletionSource目的。应使用您选择的结果类型对该对象进行参数化。我要使用Boolean作为本示例的结果类型。

    final TaskCompletionSource<Boolean> tcs = new TaskCompletionSource<>();
    
  3. 生成一个Task来自TaskCompletionSource这是在步骤 2 中创建的。同样,生成的Task应使用与TaskCompletionSource object.

    Task<Boolean> tcsTask = tcs.getTask();
    
  4. 添加一个完成监听器Task这是通过调用生成的setValue()。在完成监听器中设置适当的结果Task在步骤 3 中创建。 调用setResult()在你的TaskCompletionSouce对象将标记Task从它创建为完整的。这对于第 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()阻塞当前线程直到Task您感兴趣的已完成。我们正在等待Task由产生的TaskCompletionSource要标记为完成的对象。这Task当我们调用时将被认为完成setResult() on the TaskCompletionSource用于生成Task正如我们在步骤 4 中所做的那样。完成后,它将返回结果。

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

就是这样,当前线程将阻塞,直到Tasks.await()返回一个值。您还可以(并且应该)设置一个超时值Tasks.await()如果您想防止当前线程无限期地阻塞,请使用此方法。

如果您只关心是否Task产生于setValue()已完成,不关心是否成功,则可以跳过创建TaskCompletionSource只需使用Tasks.await()直接在那Task。同样适用于updateChildren()。如果你愿意,你可以使用方法调用updateChilden() or setValue()其中使用一个DatabaseReference.CompletionListener以及 TaskCompletionListener。

等待读取操作完成的情况类似。

使用 addListenerForSingleValueEvent() 的示例读取操作

DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
YourModelClass mModelClassObject;
  1. 创建一个TaskCompletionSource对象参数化为您期望的结果Task将从中生成。

    final TaskCompletionSource<YourModelClass> tcs = new TaskCompletionSource<>();
    
  2. 生成一个Task来自TaskCompletionSource object

    Task<YourModelClass> tcsTask = tcs.getTask();
    
  3. Call addListenerForSingleValueEvent() on our DatabaseReference并打电话setResult() on the Task在步骤 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()阻塞当前线程直到Task您感兴趣的已完成。这Task当我们调用时将被认为完成setResult()正如我们在步骤 3 中所做的那样,并将返回结果。

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

如上所述,您可以使用Tasks.await()方法以及超时值以防止当前线程无限期地阻塞。

需要注意的是,我发现 Firebase 不会终止用于其操作的后台线程。这意味着 GAE 实例永远不会空闲。查看此线程以获取更多信息:

Firebase、后台线程和 App Engine https://groups.google.com/forum/#!topic/google-appengine/U2f9dLHsck4

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从 Google App Engine 调用 Firebase 数据库 的相关文章

随机推荐