Skip to content

Migrate firebase-inappmessaging SDK to go/firebase-android-executors. #4440

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

Merged
merged 10 commits into from
Dec 13, 2022
Merged
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
1 change: 1 addition & 0 deletions firebase-inappmessaging/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Unreleased
* [changed] Migrate firebase-inappmessaging SDK to use common executor pool.

# 20.2.0
* [fixed] Fixed a bug that prevented marking more than one message as
Expand Down
1 change: 1 addition & 0 deletions firebase-inappmessaging/firebase-inappmessaging.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ dependencies {
testImplementation "io.grpc:grpc-testing:$grpcVersion"
testImplementation 'com.google.guava:guava:30.1-android'
testImplementation 'androidx.test:core:1.2.0'
testImplementation project(":integ-testing")

androidTestImplementation project(':integ-testing')
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.analytics.connector.AnalyticsConnector;
import com.google.firebase.concurrent.TestOnlyExecutors;
import com.google.firebase.events.Subscriber;
import com.google.firebase.inappmessaging.CommonTypesProto.Event;
import com.google.firebase.inappmessaging.CommonTypesProto.Priority;
Expand All @@ -65,6 +66,7 @@
import com.google.firebase.inappmessaging.internal.TestDeviceHelper;
import com.google.firebase.inappmessaging.internal.injection.modules.AppMeasurementModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ApplicationModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ExecutorsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.GrpcClientModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ProgrammaticContextualTriggerFlowableModule;
import com.google.firebase.inappmessaging.model.BannerMessage;
Expand Down Expand Up @@ -270,7 +272,9 @@ public void setUp() {
.testSystemClockModule(new TestSystemClockModule(NOW))
.programmaticContextualTriggerFlowableModule(
new ProgrammaticContextualTriggerFlowableModule(
new ProgramaticContextualTriggers()));
new ProgramaticContextualTriggers()))
.executorsModule(
new ExecutorsModule(TestOnlyExecutors.background(), TestOnlyExecutors.blocking()));

TestUniversalComponent universalComponent = universalComponentBuilder.build();

Expand Down Expand Up @@ -315,6 +319,8 @@ public void onAppOpen_whenAnalyticsAbsent_notifiesSubscriber() {
TestUniversalComponent analyticsLessUniversalComponent =
universalComponentBuilder
.appMeasurementModule(new AppMeasurementModule(handler -> {}, firebaseEventSubscriber))
.executorsModule(
new ExecutorsModule(TestOnlyExecutors.background(), TestOnlyExecutors.blocking()))
.build();
TestAppComponent appComponent =
appComponentBuilder.universalComponent(analyticsLessUniversalComponent).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.google.firebase.inappmessaging.internal.injection.modules.AnalyticsEventsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.AppMeasurementModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ApplicationModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ExecutorsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ProgrammaticContextualTriggerFlowableModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ProtoStorageClientModule;
import com.google.firebase.inappmessaging.internal.injection.modules.RateLimitModule;
Expand All @@ -41,5 +42,6 @@
ProtoStorageClientModule.class,
RateLimitModule.class,
AppMeasurementModule.class,
ExecutorsModule.class,
})
public interface TestUniversalComponent extends UniversalComponent {}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@
import com.google.firebase.abt.FirebaseABTesting;
import com.google.firebase.abt.component.AbtComponent;
import com.google.firebase.analytics.connector.AnalyticsConnector;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.annotations.concurrent.Blocking;
import com.google.firebase.components.Component;
import com.google.firebase.components.ComponentContainer;
import com.google.firebase.components.ComponentRegistrar;
import com.google.firebase.components.Dependency;
import com.google.firebase.components.Qualified;
import com.google.firebase.events.Subscriber;
import com.google.firebase.inappmessaging.internal.AbtIntegrationHelper;
import com.google.firebase.inappmessaging.internal.ProgramaticContextualTriggers;
Expand All @@ -37,13 +40,15 @@
import com.google.firebase.inappmessaging.internal.injection.modules.ApiClientModule;
import com.google.firebase.inappmessaging.internal.injection.modules.AppMeasurementModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ApplicationModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ExecutorsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.GrpcClientModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ProgrammaticContextualTriggerFlowableModule;
import com.google.firebase.inject.Deferred;
import com.google.firebase.installations.FirebaseInstallationsApi;
import com.google.firebase.platforminfo.LibraryVersionComponent;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;

/**
* Registers {@link FirebaseInAppMessaging}.
Expand All @@ -53,6 +58,10 @@
@Keep
public class FirebaseInAppMessagingRegistrar implements ComponentRegistrar {
private static final String LIBRARY_NAME = "fire-fiam";
private Qualified<Executor> backgroundExecutor =
Qualified.qualified(Background.class, Executor.class);
private Qualified<Executor> blockingExecutor =
Qualified.qualified(Blocking.class, Executor.class);

@Override
@Keep
Expand All @@ -67,6 +76,8 @@ public List<Component<?>> getComponents() {
.add(Dependency.deferred(AnalyticsConnector.class))
.add(Dependency.required(TransportFactory.class))
.add(Dependency.required(Subscriber.class))
.add(Dependency.required(backgroundExecutor))
.add(Dependency.required(blockingExecutor))
.factory(this::providesFirebaseInAppMessaging)
.eagerInDefaultApp()
.build(),
Expand All @@ -91,6 +102,9 @@ private FirebaseInAppMessaging providesFirebaseInAppMessaging(ComponentContainer
.programmaticContextualTriggerFlowableModule(
new ProgrammaticContextualTriggerFlowableModule(
new ProgramaticContextualTriggers()))
.executorsModule(
new ExecutorsModule(
container.get(backgroundExecutor), container.get(blockingExecutor)))
.build();

AppComponent instance =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,26 @@

package com.google.firebase.inappmessaging.internal;

import android.annotation.SuppressLint;
import androidx.annotation.VisibleForTesting;
import com.google.firebase.abt.AbtException;
import com.google.firebase.abt.AbtExperimentInfo;
import com.google.firebase.abt.FirebaseABTesting;
import com.google.firebase.annotations.concurrent.Blocking;
import com.google.firebase.inappmessaging.ExperimentPayloadProto;
import com.google.firebase.inappmessaging.internal.injection.scopes.FirebaseAppScope;
import com.google.internal.firebase.inappmessaging.v1.CampaignProto;
import com.google.internal.firebase.inappmessaging.v1.sdkserving.FetchEligibleCampaignsResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.inject.Inject;

/** @hide */
@FirebaseAppScope
public class AbtIntegrationHelper {
private final FirebaseABTesting abTesting;

// TODO(b/258280977): Migrate to go/firebase-android-executors
@SuppressLint("ThreadPoolCreation")
@VisibleForTesting
Executor executor = Executors.newSingleThreadExecutor();
@Inject @Blocking @VisibleForTesting Executor executor;

@Inject
public AbtIntegrationHelper(FirebaseABTesting abTesting) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@

package com.google.firebase.inappmessaging.internal;

import android.annotation.SuppressLint;
import androidx.annotation.NonNull;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.inappmessaging.FirebaseInAppMessagingClickListener;
import com.google.firebase.inappmessaging.FirebaseInAppMessagingDismissListener;
import com.google.firebase.inappmessaging.FirebaseInAppMessagingDisplayCallbacks;
Expand All @@ -25,13 +24,7 @@
import com.google.firebase.inappmessaging.model.InAppMessage;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
* A class used to manage and schedule events to registered (ie: developer-defined) or expensive
Expand All @@ -42,11 +35,7 @@
@SuppressWarnings("JavaDoc")
public class DeveloperListenerManager {

// We limit to 1 so there is minimial impact to device performance
private static final int POOL_SIZE = 1;
// Keep alive to minimize chance of having to restart a thread to handle both impression and click
private static final int KEEP_ALIVE_TIME_SECONDS = 15;
public static DeveloperListenerManager instance = new DeveloperListenerManager();
private final Executor backgroundExecutor;
private Map<FirebaseInAppMessagingClickListener, ClicksExecutorAndListener>
registeredClickListeners = new HashMap<>();
private Map<FirebaseInAppMessagingDismissListener, DismissExecutorAndListener>
Expand All @@ -56,28 +45,15 @@ public class DeveloperListenerManager {
private Map<FirebaseInAppMessagingImpressionListener, ImpressionExecutorAndListener>
registeredImpressionListeners = new HashMap<>();

private static BlockingQueue<Runnable> mCallbackQueue = new LinkedBlockingQueue<>();

// TODO(b/258280977): Migrate to go/firebase-android-executors
@SuppressLint("ThreadPoolCreation")
private static final ThreadPoolExecutor CALLBACK_QUEUE_EXECUTOR =
new ThreadPoolExecutor(
POOL_SIZE,
POOL_SIZE,
KEEP_ALIVE_TIME_SECONDS,
TimeUnit.SECONDS,
mCallbackQueue,
new FIAMThreadFactory("EventListeners-"));

static {
CALLBACK_QUEUE_EXECUTOR.allowCoreThreadTimeOut(true);
public DeveloperListenerManager(@Background Executor backgroundExecutor) {
this.backgroundExecutor = backgroundExecutor;
}

// Used internally by MetricsLoggerClient
public void impressionDetected(InAppMessage inAppMessage) {
for (ImpressionExecutorAndListener listener : registeredImpressionListeners.values()) {
listener
.withExecutor(CALLBACK_QUEUE_EXECUTOR)
.withExecutor(backgroundExecutor)
.execute(() -> listener.getListener().impressionDetected(inAppMessage));
}
}
Expand All @@ -87,23 +63,23 @@ public void displayErrorEncountered(
FirebaseInAppMessagingDisplayCallbacks.InAppMessagingErrorReason errorReason) {
for (ErrorsExecutorAndListener listener : registeredErrorListeners.values()) {
listener
.withExecutor(CALLBACK_QUEUE_EXECUTOR)
.withExecutor(backgroundExecutor)
.execute(() -> listener.getListener().displayErrorEncountered(inAppMessage, errorReason));
}
}

public void messageClicked(InAppMessage inAppMessage, Action action) {
for (ClicksExecutorAndListener listener : registeredClickListeners.values()) {
listener
.withExecutor(CALLBACK_QUEUE_EXECUTOR)
.withExecutor(backgroundExecutor)
.execute(() -> listener.getListener().messageClicked(inAppMessage, action));
}
}

public void messageDismissed(InAppMessage inAppMessage) {
for (DismissExecutorAndListener listener : registeredDismissListeners.values()) {
listener
.withExecutor(CALLBACK_QUEUE_EXECUTOR)
.withExecutor(backgroundExecutor)
.execute(() -> listener.getListener().messageDismissed(inAppMessage));
}
}
Expand Down Expand Up @@ -175,29 +151,6 @@ public void removeAllListeners() {
registeredErrorListeners.clear();
}

/** The thread factory for Storage threads. */
static class FIAMThreadFactory implements ThreadFactory {
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String mNameSuffix;

FIAMThreadFactory(@NonNull String suffix) {
mNameSuffix = suffix;
}

@SuppressWarnings("ThreadPriorityCheck")
@Override
// TODO(b/258280977): Migrate to go/firebase-android-executors
@SuppressLint("ThreadPoolCreation")
public Thread newThread(@NonNull Runnable r) {
Thread t = new Thread(r, "FIAM-" + mNameSuffix + threadNumber.getAndIncrement());
t.setDaemon(false);
t.setPriority(
android.os.Process.THREAD_PRIORITY_BACKGROUND
+ android.os.Process.THREAD_PRIORITY_MORE_FAVORABLE);
return t;
}
}

private abstract static class ExecutorAndListener<T> {

private final Executor executor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.google.firebase.inappmessaging.internal.injection.modules.AnalyticsEventsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.AppMeasurementModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ApplicationModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ExecutorsModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ForegroundFlowableModule;
import com.google.firebase.inappmessaging.internal.injection.modules.GrpcChannelModule;
import com.google.firebase.inappmessaging.internal.injection.modules.ProgrammaticContextualTriggerFlowableModule;
Expand Down Expand Up @@ -64,7 +65,8 @@
ProtoStorageClientModule.class,
SystemClockModule.class,
RateLimitModule.class,
AppMeasurementModule.class
AppMeasurementModule.class,
ExecutorsModule.class
})
public interface UniversalComponent {
ProviderInstaller probiderInstaller();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
package com.google.firebase.inappmessaging.internal.injection.modules;

import android.app.Application;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.inappmessaging.internal.DeveloperListenerManager;
import dagger.Module;
import dagger.Provides;
import java.util.concurrent.Executor;
import javax.inject.Singleton;

/**
Expand All @@ -41,7 +43,8 @@ public Application providesApplication() {

@Provides
@Singleton
public DeveloperListenerManager developerListenerManager() {
return new DeveloperListenerManager();
public DeveloperListenerManager developerListenerManager(
@Background Executor backgroundExecutor) {
return new DeveloperListenerManager(backgroundExecutor);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright 2018 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.inappmessaging.internal.injection.modules;

import androidx.annotation.NonNull;
import com.google.firebase.annotations.concurrent.Background;
import com.google.firebase.annotations.concurrent.Blocking;
import dagger.Module;
import dagger.Provides;
import java.util.concurrent.Executor;
import javax.inject.Singleton;

/** Provides executors for running tasks. */
@Module
public class ExecutorsModule {
private final Executor backgroundExecutor;
private final Executor blockingExecutor;

public ExecutorsModule(
@NonNull @Background Executor backgroundExecutor,
@NonNull @Blocking Executor blockingExecutor) {
this.backgroundExecutor = backgroundExecutor;
this.blockingExecutor = blockingExecutor;
}

@Provides
@Singleton
@Background
@NonNull
public Executor providesBackgroundExecutor() {
return backgroundExecutor;
}

@Provides
@Singleton
@Blocking
@NonNull
public Executor providesBlockingExecutor() {
return blockingExecutor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.concurrent.TestOnlyExecutors;
import com.google.firebase.inappmessaging.CommonTypesProto.Event;
import com.google.firebase.inappmessaging.CommonTypesProto.Priority;
import com.google.firebase.inappmessaging.CommonTypesProto.TriggeringCondition;
Expand Down Expand Up @@ -149,7 +150,11 @@ public Builder toBuilder() {
@Mock private DisplayCallbacksFactory displayCallbacksFactory;
@Mock private FirebaseInAppMessagingDisplayCallbacks displayCallbacks;
@Mock private ProgramaticContextualTriggers programaticContextualTriggers;
@Mock DeveloperListenerManager developerListenerManager = new DeveloperListenerManager();

@Mock
DeveloperListenerManager developerListenerManager =
new DeveloperListenerManager(TestOnlyExecutors.background());

FirebaseApp firebaseApp1;
FirebaseOptions options;

Expand Down
Loading