如何在同一个线程上继续Firebase任务

时间:2018-02-26 19:14:36

标签: android multithreading firebase task google-cloud-firestore

我在背景线程上的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

或者我错过了关于这个问题的一些微不足道的事情?在某些线程上继续难以继续但是在那里执行阻塞调用吗?

2 个答案:

答案 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确实对我有帮助