从Google App Engine调用Firebase数据库 [英] Call Firebase database from Google App Engine
问题描述
我遵循这个教程来设置我的Google App Engine实例,我也在使用Firebase。我的目标是将所有的计算放在Google App Engine上。我想调用下面的这个函数:
MyEndpoint:
package productions.widowmaker110.backend;
$ b $ **我们公开的端点类* /
@Api(
name =myApi,
version =v1,
namespace = @ApiNamespace(
ownerDomain =backend.widowmaker110.productions,
ownerName =backend.widowmaker110.productions,
packagePath =
)
)
public class MyEndpoint {
/ **一个简单的端点方法,它接受一个名字并说Hi返回* /
@ApiMethod(name =sayHi)
公共MyBean sayHi(@Named(name)字符串名称){
//写入消息到数据库
FirebaseDatabase数据库= FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(message);
//从数据库读取
myRef.addValueEventListener(new ValueEventListener(){
@Override
public void onDataChange(DataSnapshot dataSnapshot){
/ /这个方法被初始值调用一次,再次
//每当这个位置的数据被更新。
String value = dataSnapshot.getValue(String.class);
Log.d( TAG,Value is:+ value);
}
@Override
public void onCancelled(DatabaseError error){
//读取值失败
Log.w(TAG,Failed to read value。,error.toException());
}
});
MyBean response = new MyBean();
response.setData(Hi,+ name);
返回响应;
$ b
MainActivity:
package productions.widowmaker110.gpsweather;
object。
// imports ...
$ b $ public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
endpointsAsyncTask()。execute(new Pair< Context,String>(this,Manfred));
}
class EndpointsAsyncTask extends AsyncTask< Pair< Context,String> ;, Void,String> {
private MyApi myApiService = null;
私有上下文上下文;
$ b @Override
保护字符串doInBackground(Pair< Context,String> ... params){
if(myApiService == null){//只做一次
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),$ b $ new AndroidJsonFactory(),null)
//针对本地devappserver运行的选项
// - 10.0。 2.2是Android模拟器中的localhost的IP地址
// - 在运行本地devappserver时关闭压缩
.setRootUrl(http://10.0.2.2:8080/_ah/api/)
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer(){
@Override $ b $ public void initialize(AbstractGoogleClientRequest<> abstract> abstractGoogleClientRequest)throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
//结束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();
$ b $ @覆盖
保护无效onPostExecute(字符串结果){
Toast.makeText(上下文,结果,Toast.LENGTH_LONG).show ();
$ b我知道上面的代码Firebase是Android专用的,因此在Google App Engine实例上运行不起作用。我想知道是否有人知道如何从Google App Engine后端对Firebase数据库执行CRUD
操作。任何帮助都是值得赞赏的。解决方案您需要使用Firebase Server SDK进行服务器端调用。您可以在这里找到有关设置和使用的信息:
使用Firebase和Google Cloud Endpoints时,请注意,您需要将Firebase方法与任务API 。由于Firebase方法未被阻止,因此如果您不使用任务,则在您对Firebase进行的调用有机会返回其结果之前,您的Endpoint将返回。有关使用任务的简要介绍,请查看下面的链接。这是在Google I / O 2016上的一次演讲。演讲者正在讨论Android上的任务和Firebase,但在服务器上使用任务和Firebase时,概念是相同的。请注意,他们已经将Tasks API与Firebase Server SDK一起使用。我跳到了直接处理任务的部分。
Firebase SDK for Android:技术深度挖掘
下面的示例是您需要在您的端点之前处理Firebase读/写操作的结果返回值,或者其他代码取决于Firebase读/写操作的结果。这些是服务器端的例子。我会假设你在乎写操作是否成功。可能有更好的方法来执行这些服务器端操作,但这是我迄今为止所做的。
使用setValue() / strong>
DatabaseReference ref = FirebaseDatabase.getInstance()。getReference();
YourModelClass obj = new YourModelClass();
当调用
setValue()
方法返回一个任务
对象,保留对它的引用。
任务<无效> setValueTask = ref.setValue(obj);
创建一个
TaskCompletionSource
这个对象应该用你选择的结果类型参数化。我将在这个例子中使用Boolean
作为结果类型。
final TaskCompletionSource< Boolean> tcs = new TaskCompletionSource<>();
生成任务
TaskCompletionSource
是在步骤2中创建的。同样,生成的Task
应该使用与<$ c相同的参数类型$ c> TaskCompletionSource任务< Boolean> tcsTask = tcs.getTask();
为任务添加一个完成监听器
是通过调用 setValue()
生成的。在完成监听器中,在步骤3中创建的 Task
中设置相应的结果。在<$ $ $>上调用 setResult()
c $ c> TaskCompletionSouce 对象会将从它创建的 Task
标记为完整。
setValueTask.addOnCompleteListener(new OnCompleteListener< Void>(){
@Override
public void onComplete(@NonNull Task< Void> task){
if(task.isSuccessful()){
tcs.setResult(true);
} else {
tcs.setResult(false);
}
}
});
调用 Task.await()
阻止当前线程,直到您感兴趣的 Task
已经完成。我们正在等待由 TaskCompletionSource
对象生成的 Task
被标记为完成。当我们在 TaskCompletionSource $>上调用
setResult()
时,这个 Task
c $ c>用来生成 Task
,就像我们在第4步所做的那样。一旦完成,它将返回结果。
try {
布尔结果= Tasks.await(tcsTask);
catch(ExecutionException e){
//处理异常
} catch(InterruptedException e){
//处理异常
}
$ b就是这样,当前线程会阻塞,直到
任务.await()
返回一个值。你也可以(也应该)在Tasks.await()
方法上设置一个超时值,如果你想保持当前线程无限期地被阻塞。 b
$ b如果您只关心由
setValue()$ c $>生成的
任务
c>完成,并不关心它是否成功,那么你可以跳过创建TaskCompletionSource
,并使用Tasks.await()
直接在任务
上。同样的作品updateChildren()
。如果你喜欢,你可以使用方法调用updateChilden()
或setValue()
,它们使用DatabaseReference.CompletionListener
以及一个TaskCompletionListener。
等待读取操作完成是类似的。
$ b使用addListenerForSingleValueEvent()
DatabaseReference ref = FirebaseDatabase.getInstance()。getReference();
YourModelClass mModelClassObject;
创建
TaskCompletionSource
object参数化了你期望从
任务
中得到的结果。最后的TaskCompletionSource< YourModelClass> tcs = new TaskCompletionSource<>();
生成任务
TaskCompletionSource
object任务< YourModelClass> tcsTask = tcs.getTask();
调用 addListenerForSingleValueEvent()
我们的 DatabaseReference
并在 Task
上调用 setResult()
pre $ ref $ add $ value $ $ $ $ $ $ public void onDataChange(new ValueEventListener(){
@Override
public void onDataChange (DataSnapshot dataSnapshot){
YourModelClass result = dataSnapshot.getValue(YourModelClass.class); $ b $ if(result!= null){
tcs.setResult(result);
}
$ b @Override
public void onCancelled(DatabaseError databaseError){
//处理错误
}
});
调用 Tasks.await()
阻止当前线程,直到您感兴趣的 Task
已经完成。当我们调用 setResult()
的时候, Task
会被认为是完整的,并且会返回结果。
try {
mModelClassObject = Tasks.await(tcsTask);
catch(ExecutionException e){
//处理异常
} catch(InterruptedException e){
//处理异常
}
如上所述,您可以使用
Tasks.await ()
方法和一个超时值,以防止当前线程无限期地被阻塞。
就像我刚刚发现的那样Firebase不会终止用于其操作的后台线程。这意味着GAE实例从不闲置。请参阅此主题了解更多信息:
I followed this tutorial for setting up my Google App Engine instance and I am also using Firebase. My goal is to put all of the "computation" on Google App Engine. I want to call a function like this one below:
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(); } } }
I understand the above code for Firebase is Android specific so running on a Google App Engine instance doesn't work. I was wondering if any one knows how to perform CRUD operations on a firebase database from Google App Engine backend. Any help is appreciated.
解决方案You'll need to use the Firebase Server SDK to make server side calls. You can find info on setting that up and using it here:
Firebase Server SDK Installation & Setup
When using Firebase with Google Cloud Endpoints be aware that you'll need to use Firebase methods in conjunction with the Tasks API. Since Firebase methods are not blocking, if you don't use Tasks your Endpoint will return before the call you made to Firebase has a chance to return its result. For a brief intro on using tasks check out the link below. Its a talk given at Google I/O 2016. The speaker is talking about Tasks and Firebase on Android but the concepts are the same when using Tasks and Firebase on a server. Note that they have included the Tasks API with the Firebase Server SDK. I've skipped to the portion of the talk which deals directly with tasks.
Firebase SDK for Android: A tech deep dive
The samples below are if you need to process the result from your Firebase read/write operation before your Endpoint returns a value or if other code depends on the result of the Firebase read/write operation. These are server-side examples. I’m going to assume you care whether or not the write operation was successful. There may be better ways to perform these server-side operations but this is what I’ve done so far.
Sample write operation using setValue():
DatabaseReference ref = FirebaseDatabase.getInstance().getReference(); YourModelClass obj = new YourModelClass();
When a call is made to the
setValue()
method it returns aTask
object, keep a reference to it.Task<Void> setValueTask = ref.setValue(obj);
Create a
TaskCompletionSource
object. This object should be parameterized with the result type of your choice. I’m going to useBoolean
as the result type for this example.final TaskCompletionSource<Boolean> tcs = new TaskCompletionSource<>();
Generate a
Task
from theTaskCompletionSource
that was created in step 2. Again, the generatedTask
should use the same parameter type as theTaskCompletionSource
object.Task<Boolean> tcsTask = tcs.getTask();
Add a completion listener to the
Task
that was generated by the call tosetValue()
. In the completion listener set the appropriate result on theTask
created in step 3. CallingsetResult()
on yourTaskCompletionSouce
object will mark theTask
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); } } });
Call
Task.await()
to block the current thread until theTask
you are interested in has completed. We are waiting for theTask
generated by theTaskCompletionSource
object to be marked complete. ThisTask
will be considered complete when we callsetResult()
on theTaskCompletionSource
used to generate theTask
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 }
That’s it, the current thread will block until
Tasks.await()
returns a value. You can also (and should) set a timeout value on theTasks.await()
method if you want to keep the current thread from being blocked indefinitely.If you were only interested in whether or not the
Task
generated bysetValue()
completed and didn’t care if it was successful or not then you can skip the creation of theTaskCompletionSource
and just useTasks.await()
directly on thatTask
. The same works forupdateChildren()
. And if you like you can just use the method calls forupdateChilden()
orsetValue()
which use aDatabaseReference.CompletionListener
along with a TaskCompletionListener.Waiting for a read operation to complete is similar.
Sample read operation using addListenerForSingleValueEvent()
DatabaseReference ref = FirebaseDatabase.getInstance().getReference(); YourModelClass mModelClassObject;
Create a
TaskCompletionSource
object parameterized with the result you expect from theTask
that will be generated from it.final TaskCompletionSource<YourModelClass> tcs = new TaskCompletionSource<>();
Generate a
Task
from theTaskCompletionSource
objectTask<YourModelClass> tcsTask = tcs.getTask();
Call
addListenerForSingleValueEvent()
on ourDatabaseReference
and callsetResult()
on theTask
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 } });
Call
Tasks.await()
to block the current thread until theTask
you are interested in has completed. TheTask
will be considered complete when we callsetResult()
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 }
As mentioned above you can use the
Tasks.await()
method along with a timeout value to prevent the current thread from being blocked indefinitely.Just as a heads up I've found that Firebase doesn't kill the background thread that is uses for its operations. This means that the GAE Instance is never idle. Check out this thread for more info:
Firebase, Background Threads, and App Engine
这篇关于从Google App Engine调用Firebase数据库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!