从Google App Engine调用Firebase数据库

时间:2016-06-18 01:07:38

标签: java android google-app-engine firebase

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

1 个答案:

答案 0 :(得分:11)

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

Add Firebase to your Server

Firebase Server SDK Installation & Setup

将Firebase与Google Cloud Endpoints一起使用时,请注意您需要将Firebase方法与Tasks API结合使用。由于Firebase方法没有阻止,如果您不使用“任务”,则在您对Firebase进行的通话有机会返回其结果之前,您的终端将返回。有关使用任务的简要介绍,请查看以下链接。这是在Google I / O 2016上发表的演讲。演讲者正在讨论Android上的任务和Firebase,但在服务器上使用Tasks和Firebase时的概念是相同的。请注意,他们已将Tasks API与Firebase Server SDK一起包含在内。我已经跳过直接涉及任务的谈话部分。

Firebase SDK for Android: A tech deep dive

以下示例是您需要在Endpoint返回值之前处理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. 从步骤2中创建的Task生成TaskCompletionSource。同样,生成的Task应使用与TaskCompletionSource对象相同的参数类型

    Task<Boolean> tcsTask = tcs.getTask();
    
  4. Task调用生成的setValue()添加完成侦听器。在完成侦听器中,在步骤3中创建的Task上设置相应的结果。在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. 调用Task.await()阻止当前线程,直到您感兴趣的Task完成为止。我们正在等待Task对象生成的TaskCompletionSource标记为完成。当我们在Task上用setResult()调用TaskCompletionSource时,Task将被视为已完成,就像我们在第4步中所做的那样。一旦完成,它将返回结果。< / p>

    try {
        Boolean result = Tasks.await(tcsTask);
    }catch(ExecutionException e){
        //handle exception 
    }catch (InterruptedException e){
        //handle exception
    }
    
  6. 就是这样,当前线程将阻塞,直到Tasks.await()返回一个值。如果要保持当前线程无限期地被阻止,您还可以(并且应该)在Tasks.await()方法上设置超时值。

    如果您只对Task生成的setValue()是否已完成并且不关心它是否成功感兴趣,那么您可以跳过创建TaskCompletionSource并直接在Tasks.await()上使用Task。同样适用于updateChildren()。如果您愿意,可以使用updateChilden()setValue()的方法调用,它使用DatabaseReference.CompletionListener和TaskCompletionListener。

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

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

    DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
    YourModelClass mModelClassObject;
    
    1. 创建一个TaskCompletionSource对象,该对象使用您希望从Task生成的结果参数化。

      final TaskCompletionSource<YourModelClass> tcs = new TaskCompletionSource<>();
      
    2. Task对象

      生成TaskCompletionSource
      Task<YourModelClass> tcsTask = tcs.getTask();
      
    3. 在我们的addListenerForSingleValueEvent()上致电DatabaseReference,然后致电第{2}代中生成的setResult()上的Task

      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. 调用Tasks.await()阻止当前线程,直到您感兴趣的Task完成为止。当我们在步骤3中调用Task并将返回结果时,setResult()将被视为已完成。

      try {
          mModelClassObject = Tasks.await(tcsTask);
      }catch(ExecutionException e){
          //handle exception 
      }catch (InterruptedException e){
          //handle exception
      }
      
    5. 如上所述,您可以使用Tasks.await()方法和超时值来防止当前线程被无限期阻塞。

      正如抬头一样,我发现Firebase并没有杀死用于其操作的后台线程。这意味着GAE实例永远不会空闲。查看此主题以获取更多信息:

      Firebase, Background Threads, and App Engine