我按照此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数据库进行操作。任何帮助表示赞赏。
答案 0 :(得分:11)
您需要使用Firebase Server SDK进行服务器端调用。您可以在此处找到有关设置和使用它的信息:
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();
当调用setValue()
方法时,它会返回Task
个对象,并保留对它的引用。
Task<Void> setValueTask = ref.setValue(obj);
创建一个TaskCompletionSource
对象。应使用您选择的结果类型对此对象进行参数化。我将使用Boolean
作为此示例的结果类型。
final TaskCompletionSource<Boolean> tcs = new TaskCompletionSource<>();
从步骤2中创建的Task
生成TaskCompletionSource
。同样,生成的Task
应使用与TaskCompletionSource
对象相同的参数类型
Task<Boolean> tcsTask = tcs.getTask();
向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);
}
}
});
调用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
}
就是这样,当前线程将阻塞,直到Tasks.await()
返回一个值。如果要保持当前线程无限期地被阻止,您还可以(并且应该)在Tasks.await()
方法上设置超时值。
如果您只对Task
生成的setValue()
是否已完成并且不关心它是否成功感兴趣,那么您可以跳过创建TaskCompletionSource
并直接在Tasks.await()
上使用Task
。同样适用于updateChildren()
。如果您愿意,可以使用updateChilden()
或setValue()
的方法调用,它使用DatabaseReference.CompletionListener
和TaskCompletionListener。
等待读取操作完成是类似的。
使用addListenerForSingleValueEvent()的示例读取操作
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
YourModelClass mModelClassObject;
创建一个TaskCompletionSource
对象,该对象使用您希望从Task
生成的结果参数化。
final TaskCompletionSource<YourModelClass> tcs = new TaskCompletionSource<>();
从Task
对象
TaskCompletionSource
Task<YourModelClass> tcsTask = tcs.getTask();
在我们的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
}
});
调用Tasks.await()
阻止当前线程,直到您感兴趣的Task
完成为止。当我们在步骤3中调用Task
并将返回结果时,setResult()
将被视为已完成。
try {
mModelClassObject = Tasks.await(tcsTask);
}catch(ExecutionException e){
//handle exception
}catch (InterruptedException e){
//handle exception
}
如上所述,您可以使用Tasks.await()
方法和超时值来防止当前线程被无限期阻塞。
正如抬头一样,我发现Firebase并没有杀死用于其操作的后台线程。这意味着GAE实例永远不会空闲。查看此主题以获取更多信息: