1

Do Firebase Cloud Functions run off of the main thread on Android when initiated similar to Firestore calls?

For Firestore database calls background threading is handled by default.

i.e. Do we need to use background thread for retrieving data using firebase?

The Firebase Database client performs all network and disk operations off the main thread.

The Firebase Database client invokes all callbacks to your code on the main thread.

So network and disk access for the database are no reason to spin up your own threads or use background tasks. But if you do disk, network I/O or CPU intensive operations in the callback, you might need to perform those off the main thread yourself.

Observe

A Firebase Cloud Function is launched on an IO thread within a ViewModel in Android using Kotlin coroutines and returned on the Main thread. However, if Cloud Functions are not run on the main thread by default flowOn(Dispatchers.IO) and withContext(Dispatchers.Main) are not required to specify the threads.

SomeViewModel.kt

fun someMethod() {
    repository.someCloudFunction().onEach { resource ->
        withContext(Dispatchers.Main) {
            // Do something with returned resource here.
        }
    }.flowOn(Dispatchers.IO).launchIn(viewModelScope)
}

SomeRepository.kt

fun someCloudFunction(contentSelected: FeedViewEventType.ContentSelected) = flow {
    try {
        val content = contentSelected.content
        FirebaseFunctions.getInstance(firebaseApp(true))
                .getHttpsCallable("SOME_CLOUD_FUNCTION").call(
                        hashMapOf(
                                BUILD_TYPE_PARAM to BuildConfig.BUILD_TYPE,
                                CONTENT_ID_PARAM to content.id,
                                CONTENT_TITLE_PARAM to content.title,
                                CONTENT_PREVIEW_IMAGE_PARAM to content.previewImage))
                .continueWith { task ->
                    (task.result?.data as HashMap<String, String>)
                }
                // Use 'await' to convert callback to coroutine.
                .await().also { response ->
                    // Do something with response here.
                }
    } catch (error: FirebaseFunctionsException) {
        // Do something with error here.
    }
}

Expect

The explicit call to run the cloud function on the IO thread and return the response on the Main thread can be removed safely given that the cloud function does not run on the main thread by default.

SomeViewModel.kt

fun someMethod() {
    repository.someCloudFunction().onEach { resource ->
        // Do something with returned resource here.
    }.launchIn(viewModelScope)
}
AdamHurwitz
  • 9,758
  • 10
  • 72
  • 134

2 Answers2

2

When you execute:

FirebaseFunctions.getInstance(...).getHttpsCallable(...).call()

The call returns asynchronously, and the work of accessing the function happens in a non-main thread managed by the SDK. You can't change this behavior, and launching it in a different coroutine scope doesn't really change anything.

When you add a continuation with continueWith, by default the callback happens on the main thread.

It's not until you call await() on the returned Task that anything happens in the coroutine scope that you used to launch the Task. When you await the reuslts, the result is handed to the coroutine for more processing.

For your code, I wouldn't bother trying to use a continuation, since bouncing the result to the main thread isn't helpful at all here. Just await the Task returned by call, and do what you want with the raw result in the coroutine.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
1

My answer that you linked is about the Firebase Realtime Database client, which is separate from the Firebase Functions client.

That said, all Firebase clients should follow the same rules, so I'd expect the Functions client to also execute all network I/O off the main thread.

Is that not what you're seeing?


The code for Android SDK for Functions lives here in the open-source SDK.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • Thank you for the sanity check. I wanted to be sure before I refactor the coroutines. I'm seeing the data returned on the main thread as expected when using `Thread.currentThread().name` to log the current thread through each step of the method. It could be useful to include a quick mention to the thread strategy in [Call functions from your app](https://firebase.google.com/docs/functions/callable) or [Call functions via HTTP requests](https://firebase.google.com/docs/functions/http-events). – AdamHurwitz Jul 11 '20 at 22:53
  • 1
    Great idea . There should be a feedback link in those pages that go straight to our tech writers. – Frank van Puffelen Jul 11 '20 at 23:40
  • ✅ I've submitted feedback on each page and updated [my notes doc](https://docs.google.com/document/d/11JsjEDdTBtwhoNgYcGakLjeJktPO0lQbgaO4Z9rPERo) with documentation and outside links for future reference. – AdamHurwitz Jul 12 '20 at 00:44