Skip to content

Commit 23fc235

Browse files
Make FirebasePerformance initialization lazy. (#2518)
* Make FirebasePerformane initialization lazy. * Fix failing tests. * Add tests for FirebasePerformance initialization. * Add FirebasePerformanceInitializer and register it to AppStateMonitor cold start. * Hide FirebasePerformanceInitializer api * Add tests for appStateMonitor. * Address comments.
1 parent 59d18b7 commit 23fc235

File tree

6 files changed

+173
-41
lines changed

6 files changed

+173
-41
lines changed

firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfRegistrar.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,14 @@ public List<Component<?>> getComponents() {
5252
.add(Dependency.required(FirebaseInstallationsApi.class))
5353
.add(Dependency.requiredProvider(TransportFactory.class))
5454
.factory(FirebasePerfRegistrar::providesFirebasePerformance)
55-
// Since the SDK is eager(auto starts at app start), we use "lazy" dependency for some
56-
// components that are not required during initialization so as not to force initialize
57-
// them at app startup (refer
58-
// https://github.com/google/guice/wiki/InjectingProviders#providers-for-lazy-loading).
59-
.eagerInDefaultApp()
6055
.build(),
56+
/**
57+
* Fireperf SDK is lazily by {@link FirebasePerformanceInitializer} during {@link
58+
* com.google.firebase.perf.application.AppStateMonitor#onActivityResumed(Activity)}. we use
59+
* "lazy" dependency for some components that are not required during initialization so as
60+
* not to force initialize them at app startup (refer
61+
* https://github.com/google/guice/wiki/InjectingProviders#providers-for-lazy-loading)*
62+
*/
6163
LibraryVersionComponent.create("fire-perf", BuildConfig.VERSION_NAME));
6264
}
6365

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2021 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.perf;
16+
17+
import com.google.firebase.perf.application.AppStateMonitor;
18+
19+
/**
20+
* FirebasePerformanceInitializer to initialize FirebasePerformance during app cold start
21+
*
22+
* @hide
23+
*/
24+
/** @hide */
25+
public final class FirebasePerformanceInitializer implements AppStateMonitor.AppColdStartCallback {
26+
27+
/** @hide */
28+
@Override
29+
public void onAppColdStart() {
30+
// Initialize FirebasePerformance when app cold starts.
31+
FirebasePerformance.getInstance();
32+
}
33+
}

firebase-perf/src/main/java/com/google/firebase/perf/application/AppStateMonitor.java

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,9 @@ public static AppStateMonitor getInstance() {
8585

8686
private ApplicationProcessState mCurrentState = ApplicationProcessState.BACKGROUND;
8787

88-
private Set<WeakReference<AppStateCallback>> mClients =
88+
private Set<WeakReference<AppStateCallback>> appStateSubscribers =
8989
new HashSet<WeakReference<AppStateCallback>>();
90+
private Set<AppColdStartCallback> appColdStartSubscribers = new HashSet<AppColdStartCallback>();
9091

9192
private boolean hasFrameMetricsAggregator = false;
9293
private FrameMetricsAggregator mFrameMetricsAggregator;
@@ -207,6 +208,7 @@ public synchronized void onActivityResumed(Activity activity) {
207208
updateAppState(ApplicationProcessState.FOREGROUND);
208209
if (mIsColdStart) {
209210
// case 1: app startup.
211+
sendAppColdStartUpdate();
210212
mIsColdStart = false;
211213
} else {
212214
// case 2: app switch from background to foreground.
@@ -234,36 +236,50 @@ public ApplicationProcessState getAppState() {
234236
}
235237

236238
/**
237-
* Register a client to receive app state update.
239+
* Register a subscriber to receive app state update.
238240
*
239241
* @param client an AppStateCallback instance.
240242
* @hide
241243
*/
242244
/** @hide */
243-
public void registerForAppState(WeakReference<AppStateCallback> client) {
244-
synchronized (mClients) {
245-
mClients.add(client);
245+
public void registerForAppState(WeakReference<AppStateCallback> subscriber) {
246+
synchronized (appStateSubscribers) {
247+
appStateSubscribers.add(subscriber);
246248
}
247249
}
248250

249251
/**
250-
* Unregister the client to stop receiving app state update.
252+
* Unregister the subscriber to stop receiving app state update.
251253
*
252-
* @param client an AppStateCallback instance.
254+
* @param subscriber an AppStateCallback instance.
253255
* @hide
254256
*/
255257
/** @hide */
256-
public void unregisterForAppState(WeakReference<AppStateCallback> client) {
257-
synchronized (mClients) {
258-
mClients.remove(client);
258+
public void unregisterForAppState(WeakReference<AppStateCallback> subscriber) {
259+
synchronized (appStateSubscribers) {
260+
appStateSubscribers.remove(subscriber);
259261
}
260262
}
261263

262-
/** Send update state update to registered clients. */
264+
/**
265+
* Register a subscriber to receive app cold start update.
266+
*
267+
* @param subscriber the {@link AppColdStartCallback} instance.
268+
* @hide
269+
*/
270+
/** @hide */
271+
public void registerForAppColdStart(AppColdStartCallback subscriber) {
272+
synchronized (appStateSubscribers) {
273+
appColdStartSubscribers.add(subscriber);
274+
}
275+
}
276+
277+
/** Send update state update to registered subscribers. */
263278
private void updateAppState(ApplicationProcessState newState) {
264279
mCurrentState = newState;
265-
synchronized (mClients) {
266-
for (Iterator<WeakReference<AppStateCallback>> i = mClients.iterator(); i.hasNext(); ) {
280+
synchronized (appStateSubscribers) {
281+
for (Iterator<WeakReference<AppStateCallback>> i = appStateSubscribers.iterator();
282+
i.hasNext(); ) {
267283
AppStateCallback callback = i.next().get();
268284
if (callback != null) {
269285
callback.onUpdateAppState(mCurrentState);
@@ -276,6 +292,18 @@ private void updateAppState(ApplicationProcessState newState) {
276292
}
277293
}
278294

295+
/** Send cold start update to registered subscribers. */
296+
private void sendAppColdStartUpdate() {
297+
synchronized (appStateSubscribers) {
298+
for (Iterator<AppColdStartCallback> i = appColdStartSubscribers.iterator(); i.hasNext(); ) {
299+
AppColdStartCallback callback = i.next();
300+
if (callback != null) {
301+
callback.onAppColdStart();
302+
}
303+
}
304+
}
305+
}
306+
279307
/**
280308
* Return app is in foreground or not.
281309
*
@@ -425,7 +453,7 @@ private boolean hasFrameMetricsAggregatorClass() {
425453
}
426454

427455
/**
428-
* An interface to be implemented by clients which needs to receive app state update.
456+
* An interface to be implemented by subscribers which needs to receive app state update.
429457
*
430458
* @hide
431459
*/
@@ -436,6 +464,15 @@ public static interface AppStateCallback {
436464
public void onUpdateAppState(ApplicationProcessState newState);
437465
}
438466

467+
/**
468+
* An interface to be implemented by subscribers which needs to receive app cold start update.
469+
*
470+
* @hide
471+
*/
472+
public static interface AppColdStartCallback {
473+
public void onAppColdStart();
474+
}
475+
439476
/**
440477
* Screen trace name is prefix "_st_" concatenates with Activity's class name.
441478
*

firebase-perf/src/main/java/com/google/firebase/perf/provider/FirebasePerfProvider.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import androidx.annotation.Nullable;
2929
import com.google.android.gms.common.internal.Preconditions;
3030
import com.google.android.gms.common.util.VisibleForTesting;
31+
import com.google.firebase.perf.FirebasePerformanceInitializer;
3132
import com.google.firebase.perf.application.AppStateMonitor;
3233
import com.google.firebase.perf.config.ConfigResolver;
3334
import com.google.firebase.perf.metrics.AppStartTrace;
@@ -61,7 +62,10 @@ public void attachInfo(Context context, ProviderInfo info) {
6162
ConfigResolver configResolver = ConfigResolver.getInstance();
6263
configResolver.setContentProviderContext(getContext());
6364

64-
AppStateMonitor.getInstance().registerActivityLifecycleCallbacks(getContext());
65+
AppStateMonitor appStateMonitor = AppStateMonitor.getInstance();
66+
appStateMonitor.registerActivityLifecycleCallbacks(getContext());
67+
appStateMonitor.registerForAppColdStart(new FirebasePerformanceInitializer());
68+
6569
AppStartTrace appStartTrace = AppStartTrace.getInstance();
6670
appStartTrace.registerActivityLifecycleCallbacks(getContext());
6771

firebase-perf/src/test/java/com/google/firebase/perf/FirebasePerfRegistrarTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,6 @@ public void testGetComponents() {
4848
Dependency.required(FirebaseInstallationsApi.class),
4949
Dependency.requiredProvider(TransportFactory.class));
5050

51-
assertThat(firebasePerfComponent.isEagerInDefaultApp()).isTrue();
51+
assertThat(firebasePerfComponent.isLazy()).isTrue();
5252
}
5353
}

firebase-perf/src/test/java/com/google/firebase/perf/application/AppStateMonitorTest.java

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import android.content.Context;
3333
import android.os.Bundle;
3434
import android.view.WindowManager.LayoutParams;
35+
import com.google.firebase.perf.FirebasePerformanceInitializer;
3536
import com.google.firebase.perf.FirebasePerformanceTestBase;
3637
import com.google.firebase.perf.config.ConfigResolver;
3738
import com.google.firebase.perf.config.DeviceCacheManager;
@@ -675,51 +676,106 @@ public void backgroundTrace_perfMonDeactivated_traceCreated() {
675676
}
676677

677678
@Test
678-
public void activityStateChanges_singleClient_callbackIsCalled() {
679+
public void activityStateChanges_singleSubscriber_callbackIsCalled() {
679680
AppStateMonitor monitor = new AppStateMonitor(transportManager, mClock);
680-
Map<Integer, ApplicationProcessState> clientState = new HashMap<>();
681+
Map<Integer, ApplicationProcessState> subscriberState = new HashMap<>();
681682

682-
final int client1 = 1;
683+
final int subscriber1 = 1;
683684
monitor.registerForAppState(
684-
new WeakReference<>(newState -> clientState.put(client1, newState)));
685+
new WeakReference<>(newState -> subscriberState.put(subscriber1, newState)));
685686

686687
// Activity comes to Foreground
687688
monitor.onActivityResumed(activity1);
688-
assertThat(clientState.get(client1)).isEqualTo(ApplicationProcessState.FOREGROUND);
689+
assertThat(subscriberState.get(subscriber1)).isEqualTo(ApplicationProcessState.FOREGROUND);
689690

690691
// Activity goes to Background
691692
monitor.onActivityStopped(activity1);
692-
assertThat(clientState.get(client1)).isEqualTo(ApplicationProcessState.BACKGROUND);
693+
assertThat(subscriberState.get(subscriber1)).isEqualTo(ApplicationProcessState.BACKGROUND);
693694
}
694695

695696
@Test
696-
public void activityStateChanges_multipleClients_callbackCalledOnEachClient() {
697+
public void activityStateChanges_multipleSubscribers_callbackCalledOnEachSubscriber() {
697698
AppStateMonitor monitor = new AppStateMonitor(transportManager, mClock);
698-
Map<Integer, ApplicationProcessState> clientState = new HashMap<>();
699+
Map<Integer, ApplicationProcessState> subscriberState = new HashMap<>();
699700

700-
final int client1 = 1;
701+
final int subscriber1 = 1;
701702
monitor.registerForAppState(
702-
new WeakReference<>(newState -> clientState.put(client1, newState)));
703+
new WeakReference<>(newState -> subscriberState.put(subscriber1, newState)));
703704

704-
final int client2 = 2;
705+
final int subscriber2 = 2;
705706
monitor.registerForAppState(
706-
new WeakReference<>(newState -> clientState.put(client2, newState)));
707+
new WeakReference<>(newState -> subscriberState.put(subscriber2, newState)));
707708

708-
final int client3 = 3;
709+
final int subscriber3 = 3;
709710
monitor.registerForAppState(
710-
new WeakReference<>(newState -> clientState.put(client3, newState)));
711+
new WeakReference<>(newState -> subscriberState.put(subscriber3, newState)));
711712

712713
// Activity comes to Foreground
713714
monitor.onActivityResumed(activity1);
714-
assertThat(clientState.get(client1)).isEqualTo(ApplicationProcessState.FOREGROUND);
715-
assertThat(clientState.get(client2)).isEqualTo(ApplicationProcessState.FOREGROUND);
716-
assertThat(clientState.get(client3)).isEqualTo(ApplicationProcessState.FOREGROUND);
715+
assertThat(subscriberState.get(subscriber1)).isEqualTo(ApplicationProcessState.FOREGROUND);
716+
assertThat(subscriberState.get(subscriber2)).isEqualTo(ApplicationProcessState.FOREGROUND);
717+
assertThat(subscriberState.get(subscriber3)).isEqualTo(ApplicationProcessState.FOREGROUND);
717718

718719
// Activity goes to Background
719720
monitor.onActivityStopped(activity1);
720-
assertThat(clientState.get(client1)).isEqualTo(ApplicationProcessState.BACKGROUND);
721-
assertThat(clientState.get(client2)).isEqualTo(ApplicationProcessState.BACKGROUND);
722-
assertThat(clientState.get(client3)).isEqualTo(ApplicationProcessState.BACKGROUND);
721+
assertThat(subscriberState.get(subscriber1)).isEqualTo(ApplicationProcessState.BACKGROUND);
722+
assertThat(subscriberState.get(subscriber2)).isEqualTo(ApplicationProcessState.BACKGROUND);
723+
assertThat(subscriberState.get(subscriber3)).isEqualTo(ApplicationProcessState.BACKGROUND);
724+
}
725+
726+
@Test
727+
public void appColdStart_singleSubscriber_callbackIsCalled() {
728+
AppStateMonitor monitor = new AppStateMonitor(transportManager, mClock);
729+
FirebasePerformanceInitializer mockInitializer = mock(FirebasePerformanceInitializer.class);
730+
monitor.registerForAppColdStart(mockInitializer);
731+
732+
// Activity comes to Foreground
733+
monitor.onActivityResumed(activity1);
734+
verify(mockInitializer, times(1)).onAppColdStart();
735+
}
736+
737+
@Test
738+
public void appHotStart_singleSubscriber_callbackIsNotCalled() {
739+
AppStateMonitor monitor = new AppStateMonitor(transportManager, mClock);
740+
FirebasePerformanceInitializer mockInitializer = mock(FirebasePerformanceInitializer.class);
741+
monitor.registerForAppColdStart(mockInitializer);
742+
743+
// Activity comes to Foreground
744+
monitor.onActivityResumed(activity1);
745+
verify(mockInitializer, times(1)).onAppColdStart();
746+
747+
// Activity goes to Background
748+
monitor.onActivityStopped(activity1);
749+
750+
// Activity comes to Foreground
751+
monitor.onActivityResumed(activity1);
752+
verify(mockInitializer, times(1)).onAppColdStart();
753+
}
754+
755+
@Test
756+
public void appColdStart_multipleSubscriber_callbackIsCalled() {
757+
AppStateMonitor monitor = new AppStateMonitor(transportManager, mClock);
758+
FirebasePerformanceInitializer mockInitializer1 = mock(FirebasePerformanceInitializer.class);
759+
FirebasePerformanceInitializer mockInitializer2 = mock(FirebasePerformanceInitializer.class);
760+
monitor.registerForAppColdStart(mockInitializer1);
761+
monitor.registerForAppColdStart(mockInitializer2);
762+
763+
// Activity comes to Foreground
764+
monitor.onActivityResumed(activity1);
765+
verify(mockInitializer1, times(1)).onAppColdStart();
766+
verify(mockInitializer2, times(1)).onAppColdStart();
767+
}
768+
769+
@Test
770+
public void appColdStart_singleSubscriberRegistersForMultipleTimes_oneCallbackIsCalled() {
771+
AppStateMonitor monitor = new AppStateMonitor(transportManager, mClock);
772+
FirebasePerformanceInitializer mockInitializer1 = mock(FirebasePerformanceInitializer.class);
773+
monitor.registerForAppColdStart(mockInitializer1);
774+
monitor.registerForAppColdStart(mockInitializer1);
775+
776+
// Activity comes to Foreground
777+
monitor.onActivityResumed(activity1);
778+
verify(mockInitializer1, times(1)).onAppColdStart();
723779
}
724780

725781
private static Activity createFakeActivity(boolean isHardwareAccelerated) {

0 commit comments

Comments
 (0)