Skip to content

WIP Use dagger for common. #4390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions firebase-common/firebase-common.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

plugins {
id 'firebase-library'
id 'firebase-vendor'
}

firebaseLibrary {
Expand Down Expand Up @@ -68,6 +69,12 @@ dependencies {
implementation "com.google.android.gms:play-services-tasks:18.0.1"
implementation 'androidx.concurrent:concurrent-futures:1.1.0'

implementation 'javax.inject:javax.inject:1'
vendor ('com.google.dagger:dagger:2.43.2') {
exclude group: "javax.inject", module: "javax.inject"
}
annotationProcessor 'com.google.dagger:dagger-compiler:2.43.2'

// FirebaseApp references storage, so storage needs to be on classpath when dokka runs.
javadocClasspath project(path: ':firebase-storage')
javadocClasspath 'com.google.auto.value:auto-value-annotations:1.6.6'
Expand Down
160 changes: 160 additions & 0 deletions firebase-common/src/main/java/com/google/firebase/AppComponent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase;

import android.content.Context;
import androidx.core.os.UserManagerCompat;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.components.ComponentDiscovery;
import com.google.firebase.components.ComponentDiscoveryService;
import com.google.firebase.components.ComponentRegistrar;
import com.google.firebase.components.ComponentRuntime;
import com.google.firebase.components.Qualified;
import com.google.firebase.concurrent.ExecutorsRegistrar;
import com.google.firebase.events.Publisher;
import com.google.firebase.heartbeatinfo.DefaultHeartBeatController;
import com.google.firebase.heartbeatinfo.HeartBeatConsumer;
import com.google.firebase.heartbeatinfo.HeartBeatController;
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
import com.google.firebase.inject.Provider;
import com.google.firebase.platforminfo.DefaultUserAgentPublisher;
import com.google.firebase.platforminfo.GlobalLibraryVersionRegistrar;
import com.google.firebase.platforminfo.LibraryVersionsModule;
import com.google.firebase.platforminfo.UserAgentPublisher;
import com.google.firebase.provider.FirebaseInitProvider;
import com.google.firebase.tracing.ComponentMonitor;
import com.google.firebase.tracing.FirebaseTrace;
import dagger.BindsInstance;
import dagger.Component;
import dagger.Module;
import dagger.Provides;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import javax.inject.Named;
import javax.inject.Singleton;

/**
* Main Dagger entry point, used to initialize {@link FirebaseApp}s.
*
* <p>Use by {@link FirebaseApp#initializeApp(Context, FirebaseOptions, String)}.
*
* @hide
*/
@Component(modules = {AppComponent.MainModule.class, LibraryVersionsModule.class})
@Singleton
interface AppComponent {

FirebaseApp getApp();

@Component.Builder
interface Builder {
@BindsInstance
Builder setApplicationContext(Context ctx);

@BindsInstance
Builder setName(@Named("appName") String name);

@BindsInstance
Builder setOptions(FirebaseOptions options);

AppComponent build();
}

@Module
interface MainModule {
@Provides
@Named("persistenceKey")
static String providesPersistenceKey(@Named("appName") String name, FirebaseOptions options) {
return FirebaseApp.getPersistenceKey(name, options);
}

@Provides
@Singleton
static ComponentRuntime provideComponentRuntime(
Context applicationContext,
FirebaseOptions options,
javax.inject.Provider<FirebaseApp> app,
DefaultUserAgentPublisher userAgentPublisher,
DefaultHeartBeatController heartBeatController) {
FirebaseTrace.pushTrace("ComponentDiscovery");
List<Provider<ComponentRegistrar>> registrars =
ComponentDiscovery.forContext(applicationContext, ComponentDiscoveryService.class)
.discoverLazy();
FirebaseTrace.popTrace(); // ComponentDiscovery

FirebaseTrace.pushTrace("Runtime");
ComponentRuntime.Builder builder =
ComponentRuntime.builder(com.google.firebase.concurrent.UiExecutor.INSTANCE)
.addLazyComponentRegistrars(registrars)
.addComponentRegistrar(new FirebaseCommonRegistrar())
.addComponentRegistrar(new ExecutorsRegistrar())
.addComponent(
com.google.firebase.components.Component.of(
userAgentPublisher, UserAgentPublisher.class))
.addComponent(
com.google.firebase.components.Component.of(
heartBeatController,
DefaultHeartBeatController.class,
HeartBeatController.class,
HeartBeatInfo.class))
.addComponent(
com.google.firebase.components.Component.of(applicationContext, Context.class))
.addComponent(
com.google.firebase.components.Component.builder(FirebaseApp.class)
.factory(c -> app.get())
.build())
.addComponent(
com.google.firebase.components.Component.of(options, FirebaseOptions.class))
.setProcessor(new ComponentMonitor());

StartupTime startupTime = FirebaseInitProvider.getStartupTime();
// Don't provide StartupTime in direct boot mode or if Firebase was manually started
if (UserManagerCompat.isUserUnlocked(applicationContext)
&& FirebaseInitProvider.isCurrentlyInitializing()) {
builder.addComponent(
com.google.firebase.components.Component.of(startupTime, StartupTime.class));
}

ComponentRuntime componentRuntime = builder.build();
FirebaseTrace.popTrace(); // Runtime
return componentRuntime;
}

@Provides
@Singleton
static Publisher providesPublisher(ComponentRuntime runtime) {
return runtime.get(Publisher.class);
}

@Provides
@Singleton
static Set<HeartBeatConsumer> providesHeartBeatConsumers(ComponentRuntime runtime) {
return runtime.setOf(HeartBeatConsumer.class);
}

@Provides
@Background
@Singleton
static Executor providesBgExecutor(ComponentRuntime runtime) {
return runtime.get(Qualified.qualified(Background.class, Executor.class));
}

@Provides
static GlobalLibraryVersionRegistrar providesGlobalLibraryRegistrar() {
return GlobalLibraryVersionRegistrar.getInstance();
}
}
}
100 changes: 30 additions & 70 deletions firebase-common/src/main/java/com/google/firebase/FirebaseApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,9 @@
import com.google.android.gms.common.internal.Preconditions;
import com.google.android.gms.common.util.PlatformVersion;
import com.google.android.gms.common.util.ProcessUtils;
import com.google.firebase.components.Component;
import com.google.firebase.components.ComponentDiscovery;
import com.google.firebase.components.ComponentDiscoveryService;
import com.google.firebase.components.ComponentRegistrar;
import com.google.firebase.components.ComponentRuntime;
import com.google.firebase.components.Lazy;
import com.google.firebase.concurrent.ExecutorsRegistrar;
import com.google.firebase.events.Publisher;
import com.google.firebase.heartbeatinfo.DefaultHeartBeatController;
import com.google.firebase.inject.Provider;
import com.google.firebase.internal.DataCollectionConfigStorage;
import com.google.firebase.provider.FirebaseInitProvider;
import com.google.firebase.tracing.ComponentMonitor;
import com.google.firebase.tracing.FirebaseTrace;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
Expand All @@ -61,6 +50,9 @@
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

/**
* The entry point of Firebase SDKs. It holds common configuration and state for Firebase APIs. Most
Expand Down Expand Up @@ -88,6 +80,7 @@
* Use of Firebase in processes other than the main process is not supported and will likely cause
* problems related to resource contention.
*/
@Singleton
public class FirebaseApp {

private static final String LOG_TAG = "FirebaseApp";
Expand All @@ -109,8 +102,8 @@ public class FirebaseApp {
// enabled is a backwards incompatible change.
private final AtomicBoolean automaticResourceManagementEnabled = new AtomicBoolean(false);
private final AtomicBoolean deleted = new AtomicBoolean();
private final Lazy<DataCollectionConfigStorage> dataCollectionConfigStorage;
private final Provider<DefaultHeartBeatController> defaultHeartBeatController;
private final javax.inject.Provider<DataCollectionConfigStorage> dataCollectionConfigStorage;
private final javax.inject.Provider<DefaultHeartBeatController> defaultHeartBeatController;
private final List<BackgroundStateChangeListener> backgroundStateChangeListeners =
new CopyOnWriteArrayList<>();
private final List<FirebaseAppLifecycleListener> lifecycleListeners =
Expand Down Expand Up @@ -293,7 +286,14 @@ public static FirebaseApp initializeApp(
"FirebaseApp name " + normalizedName + " already exists!");

Preconditions.checkNotNull(applicationContext, "Application context cannot be null.");
firebaseApp = new FirebaseApp(applicationContext, normalizedName, options);

firebaseApp =
DaggerAppComponent.builder()
.setApplicationContext(applicationContext)
.setName(name)
.setOptions(options)
.build()
.getApp();
INSTANCES.put(normalizedName, firebaseApp);
}

Expand Down Expand Up @@ -401,61 +401,24 @@ public void setDataCollectionDefaultEnabled(boolean enabled) {
}

/**
* Default constructor.
* Default constructor
*
* @hide
*/
protected FirebaseApp(Context applicationContext, String name, FirebaseOptions options) {
this.applicationContext = Preconditions.checkNotNull(applicationContext);
this.name = Preconditions.checkNotEmpty(name);
this.options = Preconditions.checkNotNull(options);
StartupTime startupTime = FirebaseInitProvider.getStartupTime();

FirebaseTrace.pushTrace("Firebase");

FirebaseTrace.pushTrace("ComponentDiscovery");
List<Provider<ComponentRegistrar>> registrars =
ComponentDiscovery.forContext(applicationContext, ComponentDiscoveryService.class)
.discoverLazy();
FirebaseTrace.popTrace(); // ComponentDiscovery

FirebaseTrace.pushTrace("Runtime");
ComponentRuntime.Builder builder =
ComponentRuntime.builder(com.google.firebase.concurrent.UiExecutor.INSTANCE)
.addLazyComponentRegistrars(registrars)
.addComponentRegistrar(new FirebaseCommonRegistrar())
.addComponentRegistrar(new ExecutorsRegistrar())
.addComponent(Component.of(applicationContext, Context.class))
.addComponent(Component.of(this, FirebaseApp.class))
.addComponent(Component.of(options, FirebaseOptions.class))
.setProcessor(new ComponentMonitor());

// Don't provide StartupTime in direct boot mode or if Firebase was manually started
if (UserManagerCompat.isUserUnlocked(applicationContext)
&& FirebaseInitProvider.isCurrentlyInitializing()) {
builder.addComponent(Component.of(startupTime, StartupTime.class));
}

componentRuntime = builder.build();
FirebaseTrace.popTrace(); // Runtime

dataCollectionConfigStorage =
new Lazy<>(
() ->
new DataCollectionConfigStorage(
applicationContext,
getPersistenceKey(),
componentRuntime.get(Publisher.class)));
defaultHeartBeatController = componentRuntime.getProvider(DefaultHeartBeatController.class);

addBackgroundStateChangeListener(
background -> {
if (!background) {
defaultHeartBeatController.get().registerHeartBeat();
}
});

FirebaseTrace.popTrace(); // Firebase
@Inject
protected FirebaseApp(
Context applicationContext,
@Named("appName") String name,
FirebaseOptions options,
ComponentRuntime componentRuntime,
javax.inject.Provider<DataCollectionConfigStorage> dataCollectionConfigStorage,
javax.inject.Provider<DefaultHeartBeatController> heartBeatController) {
this.applicationContext = applicationContext;
this.name = name;
this.options = options;
this.componentRuntime = componentRuntime;
this.dataCollectionConfigStorage = dataCollectionConfigStorage;
this.defaultHeartBeatController = heartBeatController;
}

private void checkNotDeleted() {
Expand Down Expand Up @@ -522,10 +485,7 @@ public void removeBackgroundStateChangeListener(BackgroundStateChangeListener li
*/
@KeepForSdk
public String getPersistenceKey() {
return encodeUrlSafeNoPadding(getName().getBytes(Charset.defaultCharset()))
+ "+"
+ encodeUrlSafeNoPadding(
getOptions().getApplicationId().getBytes(Charset.defaultCharset()));
return getPersistenceKey(getName(), getOptions());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
import android.os.Build;
import com.google.firebase.components.Component;
import com.google.firebase.components.ComponentRegistrar;
import com.google.firebase.heartbeatinfo.DefaultHeartBeatController;
import com.google.firebase.platforminfo.DefaultUserAgentPublisher;
import com.google.firebase.platforminfo.KotlinDetector;
import com.google.firebase.platforminfo.LibraryVersionComponent;
import java.util.ArrayList;
Expand All @@ -42,8 +40,6 @@ public class FirebaseCommonRegistrar implements ComponentRegistrar {
@Override
public List<Component<?>> getComponents() {
List<Component<?>> result = new ArrayList<>();
result.add(DefaultUserAgentPublisher.component());
result.add(DefaultHeartBeatController.component());
result.add(
LibraryVersionComponent.create(FIREBASE_ANDROID, String.valueOf(Build.VERSION.SDK_INT)));
result.add(LibraryVersionComponent.create(FIREBASE_COMMON, BuildConfig.VERSION_NAME));
Expand Down
Loading