Skip to content

Commit 3cf020a

Browse files
authored
Register executors as components. (#4288)
* Register executors as components. The intent for those is to be used by all Firebase SDKs and forbid creating their own at will. * Add copyrights. * add more copyrights * ktlintformat * gJF * ktlint * Address review comments.
1 parent fe5b2f3 commit 3cf020a

22 files changed

+947
-34
lines changed

firebase-annotations/firebase-annotations.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,7 @@ java {
2929
tasks.withType(JavaCompile) {
3030
options.compilerArgs << "-Werror"
3131
}
32+
33+
dependencies {
34+
implementation 'javax.inject:javax.inject:1'
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.annotations.concurrent;
16+
17+
import java.lang.annotation.ElementType;
18+
import java.lang.annotation.Target;
19+
import javax.inject.Qualifier;
20+
21+
/**
22+
* An executor/coroutine dispatcher for long running tasks including disk IO, heavy CPU
23+
* computations.
24+
*
25+
* <p>For operations that can block for long periods of time, like network requests, use the {@link
26+
* Blocking} executor.
27+
*/
28+
@Qualifier
29+
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
30+
public @interface Background {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.annotations.concurrent;
16+
17+
import java.lang.annotation.ElementType;
18+
import java.lang.annotation.Target;
19+
import javax.inject.Qualifier;
20+
21+
/**
22+
* An executor/coroutine dispatcher for tasks that can block for long periods of time, e.g network
23+
* IO.
24+
*/
25+
@Qualifier
26+
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
27+
public @interface Blocking {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.annotations.concurrent;
16+
17+
import java.lang.annotation.ElementType;
18+
import java.lang.annotation.Target;
19+
import javax.inject.Qualifier;
20+
21+
/**
22+
* An executor/coroutine dispatcher for lightweight tasks that never block (on IO or other tasks).
23+
*/
24+
@Qualifier
25+
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
26+
public @interface Lightweight {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.annotations.concurrent;
16+
17+
import java.lang.annotation.ElementType;
18+
import java.lang.annotation.Target;
19+
import javax.inject.Qualifier;
20+
21+
/** An executor/coroutine dispatcher for work that must run on the UI thread. */
22+
@Qualifier
23+
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
24+
public @interface UiThread {}

firebase-common/firebase-common.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ dependencies {
6666
implementation project(':firebase-components')
6767
implementation 'com.google.android.gms:play-services-basement:18.1.0'
6868
implementation "com.google.android.gms:play-services-tasks:18.0.1"
69+
implementation 'androidx.concurrent:concurrent-futures:1.1.0'
6970

7071
// FirebaseApp references storage, so storage needs to be on classpath when dokka runs.
7172
javadocClasspath project(path: ':firebase-storage')

firebase-common/ktx/ktx.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ android {
3838
dependencies {
3939
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
4040

41+
implementation project(':firebase-annotations')
4142
implementation project(':firebase-common')
4243
implementation project(':firebase-components')
4344
implementation 'androidx.annotation:annotation:1.1.0'

firebase-common/ktx/src/main/kotlin/com/google/firebase/ktx/Firebase.kt

+33-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,18 @@ import android.content.Context
1717
import androidx.annotation.Keep
1818
import com.google.firebase.FirebaseApp
1919
import com.google.firebase.FirebaseOptions
20+
import com.google.firebase.annotations.concurrent.Background
21+
import com.google.firebase.annotations.concurrent.Blocking
22+
import com.google.firebase.annotations.concurrent.Lightweight
23+
import com.google.firebase.annotations.concurrent.UiThread
2024
import com.google.firebase.components.Component
2125
import com.google.firebase.components.ComponentRegistrar
26+
import com.google.firebase.components.Dependency
27+
import com.google.firebase.components.Qualified
2228
import com.google.firebase.platforminfo.LibraryVersionComponent
29+
import java.util.concurrent.Executor
30+
import kotlinx.coroutines.CoroutineDispatcher
31+
import kotlinx.coroutines.asCoroutineDispatcher
2332

2433
/**
2534
* Single access point to all firebase SDKs from Kotlin.
@@ -40,11 +49,11 @@ fun Firebase.initialize(context: Context): FirebaseApp? = FirebaseApp.initialize
4049

4150
/** Initializes and returns a FirebaseApp. */
4251
fun Firebase.initialize(context: Context, options: FirebaseOptions): FirebaseApp =
43-
FirebaseApp.initializeApp(context, options)
52+
FirebaseApp.initializeApp(context, options)
4453

4554
/** Initializes and returns a FirebaseApp. */
4655
fun Firebase.initialize(context: Context, options: FirebaseOptions, name: String): FirebaseApp =
47-
FirebaseApp.initializeApp(context, options, name)
56+
FirebaseApp.initializeApp(context, options, name)
4857

4958
/** Returns options of default FirebaseApp */
5059
val Firebase.options: FirebaseOptions
@@ -57,6 +66,27 @@ internal const val LIBRARY_NAME: String = "fire-core-ktx"
5766
class FirebaseCommonKtxRegistrar : ComponentRegistrar {
5867
override fun getComponents(): List<Component<*>> {
5968
return listOf(
60-
LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME))
69+
LibraryVersionComponent.create(LIBRARY_NAME, BuildConfig.VERSION_NAME),
70+
coroutineDispatcher<Background>(),
71+
coroutineDispatcher<Lightweight>(),
72+
coroutineDispatcher<Blocking>(),
73+
coroutineDispatcher<UiThread>()
74+
)
6175
}
6276
}
77+
78+
private inline fun <reified T : Annotation> coroutineDispatcher(): Component<CoroutineDispatcher> =
79+
Component.builder(
80+
Qualified.qualified(T::class.java, CoroutineDispatcher::class.java)
81+
).add(
82+
Dependency.required(
83+
Qualified.qualified(
84+
T::class.java,
85+
Executor::class.java
86+
)
87+
)
88+
).factory { c ->
89+
c.get(
90+
Qualified.qualified(T::class.java, Executor::class.java)
91+
).asCoroutineDispatcher()
92+
}.build()

firebase-common/src/main/java/com/google/firebase/FirebaseApp.java

+3-20
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
import android.content.Intent;
2424
import android.content.IntentFilter;
2525
import android.os.Build;
26-
import android.os.Handler;
27-
import android.os.Looper;
2826
import android.text.TextUtils;
2927
import android.util.Log;
3028
import androidx.annotation.GuardedBy;
@@ -47,6 +45,7 @@
4745
import com.google.firebase.components.ComponentRegistrar;
4846
import com.google.firebase.components.ComponentRuntime;
4947
import com.google.firebase.components.Lazy;
48+
import com.google.firebase.concurrent.ExecutorsRegistrar;
5049
import com.google.firebase.events.Publisher;
5150
import com.google.firebase.heartbeatinfo.DefaultHeartBeatController;
5251
import com.google.firebase.inject.Provider;
@@ -59,7 +58,6 @@
5958
import java.util.List;
6059
import java.util.Map;
6160
import java.util.concurrent.CopyOnWriteArrayList;
62-
import java.util.concurrent.Executor;
6361
import java.util.concurrent.atomic.AtomicBoolean;
6462
import java.util.concurrent.atomic.AtomicReference;
6563

@@ -97,16 +95,10 @@ public class FirebaseApp {
9795

9896
private static final Object LOCK = new Object();
9997

100-
private static final Executor UI_EXECUTOR = new UiExecutor();
101-
10298
/** A map of (name, FirebaseApp) instances. */
10399
@GuardedBy("LOCK")
104100
static final Map<String, FirebaseApp> INSTANCES = new ArrayMap<>();
105101

106-
private static final String FIREBASE_ANDROID = "fire-android";
107-
private static final String FIREBASE_COMMON = "fire-core";
108-
private static final String KOTLIN = "kotlin";
109-
110102
private final Context applicationContext;
111103
private final String name;
112104
private final FirebaseOptions options;
@@ -427,9 +419,10 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o
427419

428420
FirebaseTrace.pushTrace("Runtime");
429421
componentRuntime =
430-
ComponentRuntime.builder(UI_EXECUTOR)
422+
ComponentRuntime.builder(com.google.firebase.concurrent.UiExecutor.INSTANCE)
431423
.addLazyComponentRegistrars(registrars)
432424
.addComponentRegistrar(new FirebaseCommonRegistrar())
425+
.addComponentRegistrar(new ExecutorsRegistrar())
433426
.addComponent(Component.of(applicationContext, Context.class))
434427
.addComponent(Component.of(this, FirebaseApp.class))
435428
.addComponent(Component.of(options, FirebaseOptions.class))
@@ -712,14 +705,4 @@ public void onBackgroundStateChanged(boolean background) {
712705
}
713706
}
714707
}
715-
716-
private static class UiExecutor implements Executor {
717-
718-
private static final Handler HANDLER = new Handler(Looper.getMainLooper());
719-
720-
@Override
721-
public void execute(@NonNull Runnable command) {
722-
HANDLER.post(command);
723-
}
724-
}
725708
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.concurrent;
16+
17+
import java.util.Locale;
18+
import java.util.concurrent.Executors;
19+
import java.util.concurrent.ThreadFactory;
20+
import java.util.concurrent.atomic.AtomicLong;
21+
22+
class CustomThreadFactory implements ThreadFactory {
23+
private static final ThreadFactory DEFAULT = Executors.defaultThreadFactory();
24+
private final AtomicLong threadCount = new AtomicLong();
25+
private final String namePrefix;
26+
private final int priority;
27+
28+
CustomThreadFactory(String namePrefix, int priority) {
29+
this.namePrefix = namePrefix;
30+
this.priority = priority;
31+
}
32+
33+
@Override
34+
public Thread newThread(Runnable r) {
35+
Thread thread = DEFAULT.newThread(r);
36+
thread.setPriority(priority);
37+
thread.setName(
38+
String.format(Locale.ROOT, "%s Thread #%d", namePrefix, threadCount.getAndIncrement()));
39+
return thread;
40+
}
41+
}

0 commit comments

Comments
 (0)