我需要从正在进行交易的asynctask访问Firebase数据库。
所以我的设置是,我有一个AsyncTask。在其doInBackground
方法中,我调用另一个方法,其中包含运行事务的逻辑。在事务的doTransaction
方法中,我需要从不同的路径访问数据并生成新数据(如某些统计信息)。
它看起来像这样:
public class MyAsyncTask extends AsyncTask<String,Void,Void> {
@Override
protected Void doInBackground(String... params) {
String para1 = params[0];
String para2 = params[1];
performCalculations(para1,para2);
return null;
}
public void performCalculation(String para1, String para2) {
DatbaseReference ref = FirebaseDatabase.getInstance().getReference().child(para1);
// Run the transaction here
ref.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
TaskCompletionSource<DataSnapshot> taskCompletionSource = new TaskCompletionSource<>();
DatabaseReference ref2 = FirebaseDatabase.getInstance().getReference().child(param2);
ref2.addListenerForSingleValueEvent(new MyValueEventListener(taskCompletionSource));
try {
dataSnapshot = Tasks.await(taskCompletionSource.getTask());
// DOING SOME AWESOME THINGS HERE
// And updating the mutable Data
} catch (ExecutionException | InterruptedException e) {
Log.v(TAG, "Exception Catched");
e.printStackTrace();
}
mutableData.setValue(stats);
return Transaction.success(mutableData);
}
@Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
if (databaseError != null) {
Log.d(TAG, "onComplete: Error: " + databaseError.getMessage());
} else {
Log.d(TAG, "onComplete: Transaction was successful");
}
}
}
}
private class MyValueEventListener implements ValueEventListener {
private final TaskCompletionSource<DataSnapshot> taskSource;
public MyValueEventListener(TaskCompletionSource<DataSnapshot> taskSource) {
this.taskSource = taskSource;
}
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.v(TAG, "Looks like MyValueEventListener received the data");
taskSource.setResult(dataSnapshot);
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.v(TAG, "Looks like MyValueEventListener thrown an error");
taskSource.setException(databaseError.toException());
}
}
}
将日志放在一处,我遇到了它到达Tasks.await
语句。但是后来永远不会调用onDataChanged
回调。
在那一点上停滞不前。没有错误。没有例外。
最大的问题是,从那时起,它会停止整个应用程序的所有回调。我的所有活动都被屏蔽了,因为它永远不会调用onDataChanged
。看起来它在内部崩溃或被线程阻塞。
发生了什么事?感谢。
修改
因此,为了从事务中获取ref的最新数据,我已经将该变量作为该类的一部分。并将值事件侦听器附加到它。所以它会一直保持更新。但是,如果在触发值事件侦听器之前,脱机排队事务将开始运行,该怎么办?离线一段时间后上线后首先会执行什么优先级?
这是我更新的代码:
public class MyAsyncTask extends AsyncTask<String,Void,Void> {
DataSnapshot mDataSnapshot;
ValueEventListener mListener;
DatabaseReference ref2;
@Override
protected Void doInBackground(String... params) {
String para1 = params[0];
String para2 = params[1];
performCalculations(para1,para2);
return null;
}
public void performCalculation(String para1, String para2) {
TaskCompletionSource<DataSnapshot> taskCompletionSource = new TaskCompletionSource<>();
ref2 = FirebaseDatabase.getInstance().getReference().child(param2);
ref2.addListenerForSingleValueEvent(new MyValueEventListener(taskCompletionSource));
try {
// Get the data first time before you proceed
dataSnapshot = Tasks.await(taskCompletionSource.getTask());
} catch (ExecutionException | InterruptedException e) {
Log.v(TAG, "Exception Catched");
e.printStackTrace();
}
// Register the value event listener to keep the data updated
mListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
mDataSnapshot = dataSnapshot
Log.v(TAG, "Data updated");
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
};
ref2.addValueEventListener(mListener);
DatbaseReference ref = FirebaseDatabase.getInstance().getReference().child(para1);
// Run the transaction here
ref.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
// Perform some stuff based on mDataSanpshot
mutableData.setValue(stats);
return Transaction.success(mutableData);
}
@Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
if (databaseError != null) {
Log.d(TAG, "onComplete: Error: " + databaseError.getMessage());
} else {
Log.d(TAG, "onComplete: Transaction was successful");
ref2.removeEventListener(mListener);
}
}
}
}
// My value event listener here ...
}
答案 0 :(得分:1)
您可能会从Firebaser获得更明智的答案,例如FvP。在那之前,我会提出我很确定你在runTransaction()
中等待的任务正在造成死锁。您可以使用与此类似的Log
语句查看回调运行的线程:
Log.i(TAG, "doTransaction: " + Thread.currentThread().getName());
doTransaction()
在FirebaseDatabaseWorker
主题上运行。监听器回调在主(UI)线程上运行,并且(我猜)是由从DB工作线程调度的事件驱动的。
阻止数据库工作线程将导致侦听器回调永远不会触发。任务永远不会完成,因此永远不会释放Task.await()
。
答案 1 :(得分:1)
我还没有对TaskCompletionSource做任何事情,所以不要马上看到那里发生了什么。但总的来说,我试图避开这样的结构,因为它们使代码复杂化而没有显着的好处。
Firebase数据库客户端在单独的线程上执行所有网络和磁盘I / O,然后在主线程上显示对方法的调用。通过使用它并重新排序一些调用,我认为你可以废除很多代码:
public void performCalculation(String para1, String para2) {
DatbaseReference ref = FirebaseDatabase.getInstance().getReference().child(para1);
DatabaseReference ref2 = FirebaseDatabase.getInstance().getReference().child(para2);
ref2.addListenerForSingleValueEvent(new ValueEventListener() { }
public void onDataChange(DataSnapshot dataSnapshot) {
ref.runTransaction(new Transaction.Handler() {
public Transaction.Result doTransaction(MutableData mutableData) {
// DOING SOME AWESOME THINGS HERE
// And updating the mutable Data
}
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
if (databaseError != null) {
Log.d(TAG, "onComplete: Error: " + databaseError.getMessage());
} else {
Log.d(TAG, "onComplete: Transaction was successful");
}
}
});
}
);