我在背景线程上的SyncAdapter
上执行了一些代码,看起来像这样
Task<DocumentSnapshot> docTask = FirebaseFirestore.
getInstance().
collection("users_dummy").
document("ID1").
get();
完成此任务后如何继续后台线程?
我在get
之后尝试了.addOnCompleteListener
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>()
{
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task)
{
// DOESN'T WORK, THIS IS THE MAIN THREAD
}
});
但如上所述here,回调是在UI或主线程上完成的,实际上会抛出一个
java.lang.IllegalStateException:不能在main上调用 申请线程
执行Tasks.await(some task)
这里的完整示例(随意忽略):
private void performTheCodeThatDoesNotWork()
{
Task<DocumentSnapshot> docTask = FirebaseFirestore.getInstance().collection("users_dummy")
.document("ID1").get()
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>()
{
@Override
public void onComplete(@NonNull Task<DocumentSnapshot> task)
{
try
{
// Query the second one but block this time
Task<DocumentSnapshot> secondId = FirebaseFirestore.getInstance().collection("users_dummy")
.document("ID2").get();
/* THIS WILL THROW THE EXCEPTION ->*/ Tasks.await(secondId);
DocumentSnapshot result = secondId.getResult();
}
catch (Exception ex)
{
ex.printStackTrace();
}
Log.i("TAG", "Result is there but never reached");
}
});
Log.i("TAG", "Im here quickly, because async");
}
我已尝试Task.continueWith
,但结果相同。我是否需要为执行人提供便利,并像this doc stating leaking activities一样使用addOnCompleteListener
调用HiveException: Hive Runtime Error while processing row
?
或者我错过了关于这个问题的一些微不足道的事情?在某些线程上继续难以继续但是在那里执行阻塞调用吗?
答案 0 :(得分:2)
如果希望在主线程以外的线程上调用回调,则必须为Executor提供将在非主线程上调度侦听器的侦听器。这是您唯一的选择。
Tasks.await()几乎从来都不是正确的用法,特别是在主线程上,因为它可能会阻塞。它仅用于后台线程,并且只有当您完全知道需要阻塞行为时才会使用。
答案 1 :(得分:1)
我也遇到了这个问题,但是看到firebase addOncompletion侦听器具有您可以提供的执行程序,因此您可以与执行程序保持相同的线程。所以我创建了以下内容:
class ExecuteOnCaller: Executor {
private val threadLocalHandler = object : ThreadLocal<Handler>() {
override fun initialValue(): Handler {
var looper = Looper.myLooper()
if (looper == null)
looper = Looper.getMainLooper()
return Handler(looper)
}
}
private val handler = threadLocalHandler.get()
override fun execute(command: Runnable?) {
handler?.post(command)
}
}
然后您可以像这样使用执行器:
FirebaseAuth.getInstance().currentUser?.getIdToken(true)?.addOnCompleteListener(ExecutOnCaller(),object: OnCompleteListener<GetTokenResult> {
override fun onComplete(task: Task<GetTokenResult>) {
when {
task.isSuccessful -> {
task.result.token?.let {
//do whatever you want
}
else -> {
task.exception?.let {//some error occured}
}
}
}
})
注意我如何将执行程序传递给addoncompleteListener。如果我不这样做,firebase将在mainThread上运行它。注意不要在这里进行UI工作。有关更多信息,请查看this post确实对我有帮助