Skip to content

Commit 6106045

Browse files
committed
Migrate functions off of UI thread for continuations.
1 parent 12a49fe commit 6106045

File tree

10 files changed

+101
-22
lines changed

10 files changed

+101
-22
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/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: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
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;
25+
2526
import com.google.android.gms.tasks.Task;
2627
import com.google.android.gms.tasks.Tasks;
2728
import com.google.firebase.FirebaseApp;
29+
import com.google.firebase.concurrent.TestOnlyExecutors;
2830
import com.google.firebase.functions.FirebaseFunctionsException.Code;
2931
import java.util.Arrays;
3032
import java.util.HashMap;
@@ -91,7 +93,8 @@ public void testToken() throws InterruptedException, ExecutionException {
9193
() -> {
9294
HttpsCallableContext context = new HttpsCallableContext("token", null, null);
9395
return Tasks.forResult(context);
94-
});
96+
},
97+
TestOnlyExecutors.lite());
9598

9699
HttpsCallableReference function = functions.getHttpsCallable("tokenTest");
97100
Task<HttpsCallableResult> result = function.call(new HashMap<>());
@@ -112,7 +115,8 @@ 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());
116120

117121
HttpsCallableReference function = functions.getHttpsCallable("instanceIdTest");
118122
Task<HttpsCallableResult> result = function.call(new HashMap<>());
@@ -133,7 +137,8 @@ public void testAppCheck() throws InterruptedException, ExecutionException {
133137
() -> {
134138
HttpsCallableContext context = new HttpsCallableContext(null, null, "appCheck");
135139
return Tasks.forResult(context);
136-
});
140+
},
141+
TestOnlyExecutors.lite());
137142

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

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

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ public void getContext_whenAuthAndAppCheckAreNotAvailable_shouldContainOnlyIid()
5454
throws ExecutionException, InterruptedException {
5555
FirebaseContextProvider contextProvider =
5656
new FirebaseContextProvider(
57-
absentProvider(), providerOf(fixedIidProvider), absentDeferred());
57+
absentProvider(),
58+
providerOf(fixedIidProvider),
59+
absentDeferred(),
60+
TestOnlyExecutors.lite());
5861

5962
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
6063
assertThat(context.getAuthToken()).isNull();
@@ -67,7 +70,10 @@ public void getContext_whenOnlyAuthIsAvailable_shouldContainOnlyAuthTokenAndIid(
6770
throws ExecutionException, InterruptedException {
6871
FirebaseContextProvider contextProvider =
6972
new FirebaseContextProvider(
70-
providerOf(fixedAuthProvider), providerOf(fixedIidProvider), absentDeferred());
73+
providerOf(fixedAuthProvider),
74+
providerOf(fixedIidProvider),
75+
absentDeferred(),
76+
TestOnlyExecutors.lite());
7177

7278
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
7379
assertThat(context.getAuthToken()).isEqualTo(AUTH_TOKEN);
@@ -80,7 +86,10 @@ public void getContext_whenOnlyAppCheckIsAvailable_shouldContainOnlyAppCheckToke
8086
throws ExecutionException, InterruptedException {
8187
FirebaseContextProvider contextProvider =
8288
new FirebaseContextProvider(
83-
absentProvider(), providerOf(fixedIidProvider), deferredOf(fixedAppCheckProvider));
89+
absentProvider(),
90+
providerOf(fixedIidProvider),
91+
deferredOf(fixedAppCheckProvider),
92+
TestOnlyExecutors.lite());
8493

8594
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
8695
assertThat(context.getAuthToken()).isNull();
@@ -93,7 +102,10 @@ public void getContext_whenOnlyAuthIsAvailableAndNotSignedIn_shouldContainOnlyIi
93102
throws ExecutionException, InterruptedException {
94103
FirebaseContextProvider contextProvider =
95104
new FirebaseContextProvider(
96-
providerOf(anonymousAuthProvider), providerOf(fixedIidProvider), absentDeferred());
105+
providerOf(anonymousAuthProvider),
106+
providerOf(fixedIidProvider),
107+
absentDeferred(),
108+
TestOnlyExecutors.lite());
97109

98110
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
99111
assertThat(context.getAuthToken()).isNull();
@@ -106,7 +118,10 @@ public void getContext_whenOnlyAppCheckIsAvailableAndHasError_shouldContainOnlyI
106118
throws ExecutionException, InterruptedException {
107119
FirebaseContextProvider contextProvider =
108120
new FirebaseContextProvider(
109-
absentProvider(), providerOf(fixedIidProvider), deferredOf(errorAppCheckProvider));
121+
absentProvider(),
122+
providerOf(fixedIidProvider),
123+
deferredOf(errorAppCheckProvider),
124+
TestOnlyExecutors.lite());
110125

111126
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
112127
assertThat(context.getAuthToken()).isNull();
@@ -121,7 +136,8 @@ public void getContext_whenAuthAndAppCheckAreAvailable_shouldContainAuthAppCheck
121136
new FirebaseContextProvider(
122137
providerOf(fixedAuthProvider),
123138
providerOf(fixedIidProvider),
124-
deferredOf(fixedAppCheckProvider));
139+
deferredOf(fixedAppCheckProvider),
140+
TestOnlyExecutors.lite());
125141

126142
HttpsCallableContext context = Tasks.await(contextProvider.getContext());
127143
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: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.android.gms.tasks.TaskCompletionSource;
2828
import com.google.android.gms.tasks.Tasks;
2929
import com.google.firebase.FirebaseApp;
30+
import com.google.firebase.annotations.concurrent.Lightweight;
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,10 @@ public class FirebaseFunctions {
8993
Context context,
9094
String projectId,
9195
String regionOrCustomDomain,
92-
ContextProvider contextProvider) {
96+
ContextProvider contextProvider,
97+
@Lightweight Executor executor) {
9398
this.app = app;
99+
this.executor = executor;
94100
this.client = new OkHttpClient();
95101
this.serializer = new Serializer();
96102
this.contextProvider = Preconditions.checkNotNull(contextProvider);
@@ -269,8 +275,9 @@ public void useEmulator(@NonNull String host, int port) {
269275
Task<HttpsCallableResult> call(String name, @Nullable Object data, HttpsCallOptions options) {
270276
return providerInstalled
271277
.getTask()
272-
.continueWithTask(task -> contextProvider.getContext())
278+
.continueWithTask(executor, task -> contextProvider.getContext())
273279
.continueWithTask(
280+
executor,
274281
task -> {
275282
if (!task.isSuccessful()) {
276283
return Tasks.forException(task.getException());
@@ -291,8 +298,9 @@ Task<HttpsCallableResult> call(String name, @Nullable Object data, HttpsCallOpti
291298
Task<HttpsCallableResult> call(URL url, @Nullable Object data, HttpsCallOptions options) {
292299
return providerInstalled
293300
.getTask()
294-
.continueWithTask(task -> contextProvider.getContext())
301+
.continueWithTask(executor, task -> contextProvider.getContext())
295302
.continueWithTask(
303+
executor,
296304
task -> {
297305
if (!task.isSuccessful()) {
298306
return Tasks.forException(task.getException());
@@ -305,7 +313,7 @@ Task<HttpsCallableResult> call(URL url, @Nullable Object data, HttpsCallOptions
305313
/**
306314
* Calls a Callable HTTPS trigger endpoint.
307315
*
308-
* @param name The name of the HTTPS trigger.
316+
* @param url The name of the HTTPS trigger.
309317
* @param data Parameters to pass to the function. Can be anything encodable as JSON.
310318
* @param context Metadata to supply with the function call.
311319
* @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: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
import android.content.Context;
1818
import androidx.annotation.GuardedBy;
1919
import com.google.firebase.FirebaseApp;
20+
import com.google.firebase.annotations.concurrent.Lightweight;
2021
import java.util.HashMap;
2122
import java.util.Map;
23+
import java.util.concurrent.Executor;
2224

2325
/** Multi-resource container for Functions. */
2426
class FunctionsMultiResourceComponent {
@@ -33,12 +35,17 @@ class FunctionsMultiResourceComponent {
3335
private final Context applicationContext;
3436
private final ContextProvider contextProvider;
3537
private final FirebaseApp app;
38+
private final Executor executor;
3639

3740
FunctionsMultiResourceComponent(
38-
Context applicationContext, ContextProvider contextProvider, FirebaseApp app) {
41+
Context applicationContext,
42+
ContextProvider contextProvider,
43+
FirebaseApp app,
44+
@Lightweight Executor executor) {
3945
this.applicationContext = applicationContext;
4046
this.contextProvider = contextProvider;
4147
this.app = app;
48+
this.executor = executor;
4249
}
4350

4451
synchronized FirebaseFunctions get(String regionOrCustomDomain) {
@@ -48,7 +55,7 @@ synchronized FirebaseFunctions get(String regionOrCustomDomain) {
4855
if (functions == null) {
4956
functions =
5057
new FirebaseFunctions(
51-
app, applicationContext, projectId, regionOrCustomDomain, contextProvider);
58+
app, applicationContext, projectId, regionOrCustomDomain, contextProvider, executor);
5259
instances.put(regionOrCustomDomain, functions);
5360
}
5461
return functions;

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@
1717
import android.content.Context;
1818
import androidx.annotation.Keep;
1919
import com.google.firebase.FirebaseApp;
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.components.Component;
2324
import com.google.firebase.components.ComponentRegistrar;
2425
import com.google.firebase.components.Dependency;
26+
import com.google.firebase.components.Qualified;
2527
import com.google.firebase.iid.internal.FirebaseInstanceIdInternal;
2628
import com.google.firebase.platforminfo.LibraryVersionComponent;
2729
import java.util.Arrays;
2830
import java.util.List;
31+
import java.util.concurrent.Executor;
2932

3033
/**
3134
* Registers {@link FunctionsMultiResourceComponent}.
@@ -38,6 +41,7 @@ public class FunctionsRegistrar implements ComponentRegistrar {
3841

3942
@Override
4043
public List<Component<?>> getComponents() {
44+
Qualified<Executor> liteExecutor = Qualified.qualified(Lightweight.class, Executor.class);
4145
return Arrays.asList(
4246
Component.builder(ContextProvider.class)
4347
.add(Dependency.optionalProvider(InternalAuthProvider.class))
@@ -48,7 +52,8 @@ public List<Component<?>> getComponents() {
4852
new FirebaseContextProvider(
4953
c.getProvider(InternalAuthProvider.class),
5054
c.getProvider(FirebaseInstanceIdInternal.class),
51-
c.getDeferred(InternalAppCheckTokenProvider.class)))
55+
c.getDeferred(InternalAppCheckTokenProvider.class),
56+
c.get(liteExecutor)))
5257
.build(),
5358
Component.builder(FunctionsMultiResourceComponent.class)
5459
.name(LIBRARY_NAME)
@@ -60,7 +65,8 @@ public List<Component<?>> getComponents() {
6065
new FunctionsMultiResourceComponent(
6166
c.get(Context.class),
6267
c.get(ContextProvider.class),
63-
c.get(FirebaseApp.class)))
68+
c.get(FirebaseApp.class),
69+
c.get(liteExecutor)))
6470
.build(),
6571
LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME));
6672
}

integ-testing/integ-testing.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ android {
3030
}
3131

3232
dependencies {
33+
implementation project(":firebase-common")
34+
implementation project(":firebase-components")
3335
implementation 'junit:junit:4.13'
3436
implementation 'androidx.test:runner:1.3.0'
3537
}

0 commit comments

Comments
 (0)