Skip to content

Commit b7b129b

Browse files
authored
Merge 1853552 into 12a49fe
2 parents 12a49fe + 1853552 commit b7b129b

File tree

11 files changed

+142
-30
lines changed

11 files changed

+142
-30
lines changed

firebase-common/src/main/java/com/google/firebase/concurrent/ExecutorsRegistrar.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737

3838
@SuppressLint("ThreadPoolCreation")
3939
public class ExecutorsRegistrar implements ComponentRegistrar {
40-
private static final Lazy<ScheduledExecutorService> BG_EXECUTOR =
40+
static final Lazy<ScheduledExecutorService> BG_EXECUTOR =
4141
new Lazy<>(
4242
() ->
4343
scheduled(
@@ -46,15 +46,15 @@ public class ExecutorsRegistrar implements ComponentRegistrar {
4646
factory(
4747
"Firebase Background", Process.THREAD_PRIORITY_BACKGROUND, bgPolicy()))));
4848

49-
private static final Lazy<ScheduledExecutorService> LITE_EXECUTOR =
49+
static final Lazy<ScheduledExecutorService> LITE_EXECUTOR =
5050
new Lazy<>(
5151
() ->
5252
scheduled(
5353
Executors.newFixedThreadPool(
5454
Math.max(2, Runtime.getRuntime().availableProcessors()),
5555
factory("Firebase Lite", Process.THREAD_PRIORITY_DEFAULT, litePolicy()))));
5656

57-
private static final Lazy<ScheduledExecutorService> BLOCKING_EXECUTOR =
57+
static final Lazy<ScheduledExecutorService> BLOCKING_EXECUTOR =
5858
new Lazy<>(
5959
() ->
6060
scheduled(

firebase-functions/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Unreleased
2+
* [changed] Avoid executing code on the UI thread as much as possible.
23

34
# 20.2.1
45
* [changed] Updated dependency of `firebase-iid` to its latest

firebase-functions/firebase-functions.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ android {
5050
}
5151

5252
dependencies {
53+
implementation project(':firebase-annotations')
5354
implementation project(':firebase-common')
5455
implementation project(':firebase-components')
5556
implementation project(':appcheck:firebase-appcheck-interop')
@@ -73,6 +74,7 @@ dependencies {
7374
javadocClasspath 'org.codehaus.mojo:animal-sniffer-annotations:1.19'
7475
javadocClasspath 'com.google.auto.value:auto-value-annotations:1.6.6'
7576

77+
androidTestImplementation project(":integ-testing")
7678
androidTestImplementation 'junit:junit:4.13.2'
7779
androidTestImplementation "com.google.truth:truth:$googleTruthVersion"
7880
androidTestImplementation 'androidx.test:runner:1.3.0'

firebase-functions/src/androidTest/java/com/google/firebase/functions/CallTest.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121
import static org.junit.Assert.assertTrue;
2222

2323
import androidx.test.InstrumentationRegistry;
24-
import androidx.test.runner.AndroidJUnit4;
24+
import androidx.test.ext.junit.runners.AndroidJUnit4;
2525
import com.google.android.gms.tasks.Task;
2626
import com.google.android.gms.tasks.Tasks;
2727
import com.google.firebase.FirebaseApp;
28+
import com.google.firebase.concurrent.TestOnlyExecutors;
2829
import com.google.firebase.functions.FirebaseFunctionsException.Code;
2930
import java.util.Arrays;
3031
import java.util.HashMap;
@@ -91,7 +92,9 @@ public void testToken() throws InterruptedException, ExecutionException {
9192
() -> {
9293
HttpsCallableContext context = new HttpsCallableContext("token", null, null);
9394
return Tasks.forResult(context);
94-
});
95+
},
96+
TestOnlyExecutors.lite(),
97+
TestOnlyExecutors.ui());
9598

9699
HttpsCallableReference function = functions.getHttpsCallable("tokenTest");
97100
Task<HttpsCallableResult> result = function.call(new HashMap<>());
@@ -112,7 +115,9 @@ public void testInstanceId() throws InterruptedException, ExecutionException {
112115
() -> {
113116
HttpsCallableContext context = new HttpsCallableContext(null, "iid", null);
114117
return Tasks.forResult(context);
115-
});
118+
},
119+
TestOnlyExecutors.lite(),
120+
TestOnlyExecutors.ui());
116121

117122
HttpsCallableReference function = functions.getHttpsCallable("instanceIdTest");
118123
Task<HttpsCallableResult> result = function.call(new HashMap<>());
@@ -133,7 +138,9 @@ public void testAppCheck() throws InterruptedException, ExecutionException {
133138
() -> {
134139
HttpsCallableContext context = new HttpsCallableContext(null, null, "appCheck");
135140
return Tasks.forResult(context);
136-
});
141+
},
142+
TestOnlyExecutors.lite(),
143+
TestOnlyExecutors.ui());
137144

138145
HttpsCallableReference function = functions.getHttpsCallable("appCheckTest");
139146
Task<HttpsCallableResult> result = function.call(new HashMap<>());

firebase-functions/src/androidTest/java/com/google/firebase/functions/FirebaseContextProviderTest.java

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.android.gms.tasks.Tasks;
2121
import com.google.firebase.appcheck.interop.InternalAppCheckTokenProvider;
2222
import com.google.firebase.auth.internal.InternalAuthProvider;
23+
import com.google.firebase.concurrent.TestOnlyExecutors;
2324
import com.google.firebase.iid.internal.FirebaseInstanceIdInternal;
2425
import com.google.firebase.inject.Deferred;
2526
import com.google.firebase.inject.Provider;
@@ -54,7 +55,10 @@ public void getContext_whenAuthAndAppCheckAreNotAvailable_shouldContainOnlyIid()
5455
throws ExecutionException, InterruptedException {
5556
FirebaseContextProvider contextProvider =
5657
new FirebaseContextProvider(
57-
absentProvider(), providerOf(fixedIidProvider), absentDeferred());
58+
absentProvider(),
59+
providerOf(fixedIidProvider),
60+
absentDeferred(),
61+
TestOnlyExecutors.lite());
5862

5963
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
6064
assertThat(context.getAuthToken()).isNull();
@@ -67,7 +71,10 @@ public void getContext_whenOnlyAuthIsAvailable_shouldContainOnlyAuthTokenAndIid(
6771
throws ExecutionException, InterruptedException {
6872
FirebaseContextProvider contextProvider =
6973
new FirebaseContextProvider(
70-
providerOf(fixedAuthProvider), providerOf(fixedIidProvider), absentDeferred());
74+
providerOf(fixedAuthProvider),
75+
providerOf(fixedIidProvider),
76+
absentDeferred(),
77+
TestOnlyExecutors.lite());
7178

7279
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
7380
assertThat(context.getAuthToken()).isEqualTo(AUTH_TOKEN);
@@ -80,7 +87,10 @@ public void getContext_whenOnlyAppCheckIsAvailable_shouldContainOnlyAppCheckToke
8087
throws ExecutionException, InterruptedException {
8188
FirebaseContextProvider contextProvider =
8289
new FirebaseContextProvider(
83-
absentProvider(), providerOf(fixedIidProvider), deferredOf(fixedAppCheckProvider));
90+
absentProvider(),
91+
providerOf(fixedIidProvider),
92+
deferredOf(fixedAppCheckProvider),
93+
TestOnlyExecutors.lite());
8494

8595
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
8696
assertThat(context.getAuthToken()).isNull();
@@ -93,7 +103,10 @@ public void getContext_whenOnlyAuthIsAvailableAndNotSignedIn_shouldContainOnlyIi
93103
throws ExecutionException, InterruptedException {
94104
FirebaseContextProvider contextProvider =
95105
new FirebaseContextProvider(
96-
providerOf(anonymousAuthProvider), providerOf(fixedIidProvider), absentDeferred());
106+
providerOf(anonymousAuthProvider),
107+
providerOf(fixedIidProvider),
108+
absentDeferred(),
109+
TestOnlyExecutors.lite());
97110

98111
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
99112
assertThat(context.getAuthToken()).isNull();
@@ -106,7 +119,10 @@ public void getContext_whenOnlyAppCheckIsAvailableAndHasError_shouldContainOnlyI
106119
throws ExecutionException, InterruptedException {
107120
FirebaseContextProvider contextProvider =
108121
new FirebaseContextProvider(
109-
absentProvider(), providerOf(fixedIidProvider), deferredOf(errorAppCheckProvider));
122+
absentProvider(),
123+
providerOf(fixedIidProvider),
124+
deferredOf(errorAppCheckProvider),
125+
TestOnlyExecutors.lite());
110126

111127
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
112128
assertThat(context.getAuthToken()).isNull();
@@ -121,7 +137,8 @@ public void getContext_whenAuthAndAppCheckAreAvailable_shouldContainAuthAppCheck
121137
new FirebaseContextProvider(
122138
providerOf(fixedAuthProvider),
123139
providerOf(fixedIidProvider),
124-
deferredOf(fixedAppCheckProvider));
140+
deferredOf(fixedAppCheckProvider),
141+
TestOnlyExecutors.lite());
125142

126143
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
127144
assertThat(context.getAuthToken()).isEqualTo(AUTH_TOKEN);

firebase-functions/src/main/java/com/google/firebase/functions/FirebaseContextProvider.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
import android.util.Log;
1818
import com.google.android.gms.tasks.Task;
1919
import com.google.android.gms.tasks.Tasks;
20+
import com.google.firebase.annotations.concurrent.Lightweight;
2021
import com.google.firebase.appcheck.interop.InternalAppCheckTokenProvider;
2122
import com.google.firebase.auth.internal.InternalAuthProvider;
2223
import com.google.firebase.iid.internal.FirebaseInstanceIdInternal;
2324
import com.google.firebase.inject.Deferred;
2425
import com.google.firebase.inject.Provider;
2526
import com.google.firebase.internal.api.FirebaseNoSignedInUserException;
27+
import java.util.concurrent.Executor;
2628
import java.util.concurrent.atomic.AtomicReference;
2729

2830
/** A ContextProvider that uses FirebaseAuth to get the token. */
@@ -33,13 +35,16 @@ class FirebaseContextProvider implements ContextProvider {
3335
private final Provider<FirebaseInstanceIdInternal> instanceId;
3436
private final AtomicReference<InternalAppCheckTokenProvider> appCheckRef =
3537
new AtomicReference<>();
38+
private final Executor executor;
3639

3740
FirebaseContextProvider(
3841
Provider<InternalAuthProvider> tokenProvider,
3942
Provider<FirebaseInstanceIdInternal> instanceId,
40-
Deferred<InternalAppCheckTokenProvider> appCheckDeferred) {
43+
Deferred<InternalAppCheckTokenProvider> appCheckDeferred,
44+
@Lightweight Executor executor) {
4145
this.tokenProvider = tokenProvider;
4246
this.instanceId = instanceId;
47+
this.executor = executor;
4348
appCheckDeferred.whenAvailable(
4449
p -> {
4550
InternalAppCheckTokenProvider appCheck = p.get();
@@ -59,6 +64,7 @@ public Task<HttpsCallableContext> getContext() {
5964
Task<String> appCheckToken = getAppCheckToken();
6065
return Tasks.whenAll(authToken, appCheckToken)
6166
.onSuccessTask(
67+
executor,
6268
v ->
6369
Tasks.forResult(
6470
new HttpsCallableContext(
@@ -74,6 +80,7 @@ private Task<String> getAuthToken() {
7480
}
7581
return auth.getAccessToken(false)
7682
.continueWith(
83+
executor,
7784
task -> {
7885
String authToken = null;
7986
if (!task.isSuccessful()) {
@@ -98,6 +105,7 @@ private Task<String> getAppCheckToken() {
98105
return appCheck
99106
.getToken(false)
100107
.onSuccessTask(
108+
executor,
101109
result -> {
102110
if (result.getError() != null) {
103111
// If there was an error getting the App Check token, do NOT send the placeholder

firebase-functions/src/main/java/com/google/firebase/functions/FirebaseFunctions.java

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
package com.google.firebase.functions;
1616

1717
import android.content.Context;
18-
import android.os.Handler;
1918
import android.util.Log;
2019
import androidx.annotation.NonNull;
2120
import androidx.annotation.Nullable;
@@ -27,6 +26,8 @@
2726
import com.google.android.gms.tasks.TaskCompletionSource;
2827
import com.google.android.gms.tasks.Tasks;
2928
import com.google.firebase.FirebaseApp;
29+
import com.google.firebase.annotations.concurrent.Lightweight;
30+
import com.google.firebase.annotations.concurrent.UiThread;
3031
import com.google.firebase.emulators.EmulatedServiceSettings;
3132
import com.google.firebase.functions.FirebaseFunctionsException.Code;
3233
import java.io.IOException;
@@ -35,6 +36,7 @@
3536
import java.net.URL;
3637
import java.util.HashMap;
3738
import java.util.Map;
39+
import java.util.concurrent.Executor;
3840
import okhttp3.Call;
3941
import okhttp3.Callback;
4042
import okhttp3.MediaType;
@@ -69,6 +71,8 @@ public class FirebaseFunctions {
6971
// A provider of client metadata to include with calls.
7072
private final ContextProvider contextProvider;
7173

74+
private final Executor executor;
75+
7276
// The projectId to use for all functions references.
7377
private final String projectId;
7478

@@ -89,8 +93,11 @@ public class FirebaseFunctions {
8993
Context context,
9094
String projectId,
9195
String regionOrCustomDomain,
92-
ContextProvider contextProvider) {
96+
ContextProvider contextProvider,
97+
@Lightweight Executor executor,
98+
@UiThread Executor uiExecutor) {
9399
this.app = app;
100+
this.executor = executor;
94101
this.client = new OkHttpClient();
95102
this.serializer = new Serializer();
96103
this.contextProvider = Preconditions.checkNotNull(contextProvider);
@@ -112,15 +119,16 @@ public class FirebaseFunctions {
112119
this.customDomain = regionOrCustomDomain;
113120
}
114121

115-
maybeInstallProviders(context);
122+
maybeInstallProviders(context, uiExecutor);
116123
}
117124

118125
/**
119126
* Runs ProviderInstaller.installIfNeededAsync once per application instance.
120127
*
121128
* @param context The application context.
129+
* @param uiExecutor
122130
*/
123-
private static void maybeInstallProviders(Context context) {
131+
private static void maybeInstallProviders(Context context, Executor uiExecutor) {
124132
// Make sure this only runs once.
125133
synchronized (providerInstalled) {
126134
if (providerInstallStarted) {
@@ -131,7 +139,7 @@ private static void maybeInstallProviders(Context context) {
131139

132140
// Package installIfNeededAsync into a Runnable so it can be run on the main thread.
133141
// installIfNeededAsync checks to make sure it is on the main thread, and throws otherwise.
134-
Runnable runnable =
142+
uiExecutor.execute(
135143
() ->
136144
ProviderInstaller.installIfNeededAsync(
137145
context,
@@ -146,10 +154,7 @@ public void onProviderInstallFailed(int i, android.content.Intent intent) {
146154
Log.d("FirebaseFunctions", "Failed to update ssl context");
147155
providerInstalled.setResult(null);
148156
}
149-
});
150-
151-
Handler handler = new Handler(context.getMainLooper());
152-
handler.post(runnable);
157+
}));
153158
}
154159

155160
/**
@@ -269,8 +274,9 @@ public void useEmulator(@NonNull String host, int port) {
269274
Task<HttpsCallableResult> call(String name, @Nullable Object data, HttpsCallOptions options) {
270275
return providerInstalled
271276
.getTask()
272-
.continueWithTask(task -> contextProvider.getContext())
277+
.continueWithTask(executor, task -> contextProvider.getContext())
273278
.continueWithTask(
279+
executor,
274280
task -> {
275281
if (!task.isSuccessful()) {
276282
return Tasks.forException(task.getException());
@@ -291,8 +297,9 @@ Task<HttpsCallableResult> call(String name, @Nullable Object data, HttpsCallOpti
291297
Task<HttpsCallableResult> call(URL url, @Nullable Object data, HttpsCallOptions options) {
292298
return providerInstalled
293299
.getTask()
294-
.continueWithTask(task -> contextProvider.getContext())
300+
.continueWithTask(executor, task -> contextProvider.getContext())
295301
.continueWithTask(
302+
executor,
296303
task -> {
297304
if (!task.isSuccessful()) {
298305
return Tasks.forException(task.getException());
@@ -305,7 +312,7 @@ Task<HttpsCallableResult> call(URL url, @Nullable Object data, HttpsCallOptions
305312
/**
306313
* Calls a Callable HTTPS trigger endpoint.
307314
*
308-
* @param name The name of the HTTPS trigger.
315+
* @param url The name of the HTTPS trigger.
309316
* @param data Parameters to pass to the function. Can be anything encodable as JSON.
310317
* @param context Metadata to supply with the function call.
311318
* @return A Task that will be completed when the request is complete.

firebase-functions/src/main/java/com/google/firebase/functions/FunctionsMultiResourceComponent.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616

1717
import android.content.Context;
1818
import androidx.annotation.GuardedBy;
19+
import androidx.annotation.UiThread;
1920
import com.google.firebase.FirebaseApp;
21+
import com.google.firebase.annotations.concurrent.Lightweight;
2022
import java.util.HashMap;
2123
import java.util.Map;
24+
import java.util.concurrent.Executor;
2225

2326
/** Multi-resource container for Functions. */
2427
class FunctionsMultiResourceComponent {
@@ -33,12 +36,20 @@ class FunctionsMultiResourceComponent {
3336
private final Context applicationContext;
3437
private final ContextProvider contextProvider;
3538
private final FirebaseApp app;
39+
private final Executor liteExecutor;
40+
private final Executor uiExecutor;
3641

3742
FunctionsMultiResourceComponent(
38-
Context applicationContext, ContextProvider contextProvider, FirebaseApp app) {
43+
Context applicationContext,
44+
ContextProvider contextProvider,
45+
FirebaseApp app,
46+
@Lightweight Executor liteExecutor,
47+
@UiThread Executor uiExecutor) {
3948
this.applicationContext = applicationContext;
4049
this.contextProvider = contextProvider;
4150
this.app = app;
51+
this.liteExecutor = liteExecutor;
52+
this.uiExecutor = uiExecutor;
4253
}
4354

4455
synchronized FirebaseFunctions get(String regionOrCustomDomain) {
@@ -48,7 +59,13 @@ synchronized FirebaseFunctions get(String regionOrCustomDomain) {
4859
if (functions == null) {
4960
functions =
5061
new FirebaseFunctions(
51-
app, applicationContext, projectId, regionOrCustomDomain, contextProvider);
62+
app,
63+
applicationContext,
64+
projectId,
65+
regionOrCustomDomain,
66+
contextProvider,
67+
liteExecutor,
68+
uiExecutor);
5269
instances.put(regionOrCustomDomain, functions);
5370
}
5471
return functions;

0 commit comments

Comments
 (0)