@react-native-firebase/perf 16.6.0 → 17.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,16 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [17.0.0](https://github.com/invertase/react-native-firebase/compare/v16.7.0...v17.0.0) (2023-02-02)
7
+
8
+ **Note:** Version bump only for package @react-native-firebase/perf
9
+
10
+ ## [16.7.0](https://github.com/invertase/react-native-firebase/compare/v16.6.0...v16.7.0) (2023-01-28)
11
+
12
+ ### Features
13
+
14
+ - **perf:** add custom screen rendering traces for android ([#6588](https://github.com/invertase/react-native-firebase/issues/6588)) ([9f2498d](https://github.com/invertase/react-native-firebase/commit/9f2498d29ee3780cba5a7a69fde8f7c370ad723b))
15
+
6
16
  ## [16.6.0](https://github.com/invertase/react-native-firebase/compare/v16.5.2...v16.6.0) (2023-01-27)
7
17
 
8
18
  **Note:** Version bump only for package @react-native-firebase/perf
@@ -0,0 +1,219 @@
1
+ package io.invertase.firebase.perf;
2
+
3
+ /**
4
+ * Copyright 2021 Google Inc. All Rights Reserved.
5
+ *
6
+ * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
7
+ * except in compliance with the License. You may obtain a copy of the License at
8
+ *
9
+ * <p>http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * <p>Unless required by applicable law or agreed to in writing, software distributed under the
12
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
13
+ * express or implied. See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import android.app.Activity;
17
+ import android.os.Build;
18
+ import android.util.Log;
19
+ import android.util.SparseIntArray;
20
+ import android.view.WindowManager;
21
+ import androidx.core.app.FrameMetricsAggregator;
22
+ import com.google.firebase.perf.FirebasePerformance;
23
+ import com.google.firebase.perf.metrics.Trace;
24
+ import com.google.firebase.perf.util.Constants;
25
+
26
+ /**
27
+ * Utility class to capture Screen rendering information (Slow/Frozen frames) for the {@code
28
+ * Activity} passed to the constructor {@link
29
+ * io.invertase.firebase.perf.ScreenTrace#ScreenTrace(Activity, String)}.
30
+ *
31
+ * <p>Learn more at https://firebase.google.com/docs/perf-mon/screen-traces?platform=android.
32
+ *
33
+ * <p>A slow screen rendering often leads to a UI Jank which creates a bad user experience. Below
34
+ * are some tips and references to understand and fix common UI Jank issues: -
35
+ * https://developer.android.com/topic/performance/vitals/render.html#fixing_jank -
36
+ * https://youtu.be/CaMTIgxCSqU (Why 60fps?) - https://youtu.be/HXQhu6qfTVU (Rendering Performance)
37
+ * - https://youtu.be/1iaHxmfZGGc (Understanding VSYNC) -
38
+ * https://www.youtube.com/playlist?list=PLOU2XLYxmsIKEOXh5TwZEv89aofHzNCiu (Android Performance
39
+ * Patterns)
40
+ *
41
+ * <p>References: - Fireperf Source Code
42
+ */
43
+ public class ScreenTrace {
44
+
45
+ private static final String TAG = "RNFirebasePerf";
46
+ private static final String FRAME_METRICS_AGGREGATOR_CLASSNAME =
47
+ "androidx.core.app.FrameMetricsAggregator";
48
+
49
+ private final Activity activity;
50
+ private final String traceName;
51
+
52
+ private final FrameMetricsAggregator frameMetricsAggregator;
53
+ private Trace perfScreenTrace;
54
+
55
+ /**
56
+ * Default constructor for this class.
57
+ *
58
+ * @param activity for which the screen traces should be recorded.
59
+ * @param tag used as an identifier for the name to be used to log screen rendering information
60
+ * (like "MyFancyScreen").
61
+ * @implNote It requires hardware acceleration to be on or it throws.
62
+ */
63
+ public ScreenTrace(Activity activity, String tag) throws IllegalStateException {
64
+ this.activity = activity;
65
+
66
+ // We don't care about adding the activity name to the trace name
67
+ // because RN doesn't care about activities
68
+ this.traceName = tag;
69
+
70
+ boolean isScreenTraceSupported = checkScreenTraceSupport(activity);
71
+
72
+ if (!isScreenTraceSupported) {
73
+ throw new IllegalStateException(
74
+ "Device does not support screen traces. Hardware acceleration must be enabled and Android"
75
+ + " must not be 8.0 or 8.1.");
76
+ }
77
+
78
+ frameMetricsAggregator = new FrameMetricsAggregator();
79
+ }
80
+
81
+ // region Public APIs
82
+
83
+ /** Starts recording the frame metrics for the screen traces. */
84
+ public void recordScreenTrace() {
85
+ Log.d(TAG, "Recording screen trace " + traceName);
86
+
87
+ frameMetricsAggregator.add(activity);
88
+ perfScreenTrace = FirebasePerformance.startTrace(getScreenTraceName());
89
+ }
90
+
91
+ /**
92
+ * Stops recording screen traces and dispatches the trace capturing information on %age of
93
+ * Slow/Frozen frames.
94
+ *
95
+ * <p>Inspired by fireperf source.
96
+ */
97
+ public void sendScreenTrace() {
98
+ if (perfScreenTrace == null) return;
99
+
100
+ int totalFrames = 0;
101
+ int slowFrames = 0;
102
+ int frozenFrames = 0;
103
+
104
+ // Stops recording metrics for this Activity and returns the currently-collected metrics
105
+ SparseIntArray[] arr = frameMetricsAggregator.reset();
106
+
107
+ if (arr != null) {
108
+ SparseIntArray frameTimes = arr[FrameMetricsAggregator.TOTAL_INDEX];
109
+
110
+ if (frameTimes != null) {
111
+ for (int i = 0; i < frameTimes.size(); i++) {
112
+ int frameTime = frameTimes.keyAt(i);
113
+ int numFrames = frameTimes.valueAt(i);
114
+
115
+ totalFrames += numFrames;
116
+
117
+ if (frameTime > Constants.FROZEN_FRAME_TIME) {
118
+ // Frozen frames mean the app appear frozen. The recommended thresholds is 700ms
119
+ frozenFrames += numFrames;
120
+ }
121
+
122
+ if (frameTime > Constants.SLOW_FRAME_TIME) {
123
+ // Slow frames are anything above 16ms (i.e. 60 frames/second)
124
+ slowFrames += numFrames;
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
+ // Only incrementMetric if corresponding metric is non-zero.
131
+ if (totalFrames > 0) {
132
+ perfScreenTrace.putMetric(Constants.CounterNames.FRAMES_TOTAL.toString(), totalFrames);
133
+ }
134
+ if (slowFrames > 0) {
135
+ perfScreenTrace.putMetric(Constants.CounterNames.FRAMES_SLOW.toString(), slowFrames);
136
+ }
137
+ if (frozenFrames > 0) {
138
+ perfScreenTrace.putMetric(Constants.CounterNames.FRAMES_FROZEN.toString(), frozenFrames);
139
+ }
140
+
141
+ Log.d(
142
+ TAG,
143
+ new StringBuilder()
144
+ .append("sendScreenTrace ")
145
+ .append(traceName)
146
+ .append(", name: ")
147
+ .append(getScreenTraceName())
148
+ .append(", total_frames: ")
149
+ .append(totalFrames)
150
+ .append(", slow_frames: ")
151
+ .append(slowFrames)
152
+ .append(", frozen_frames: ")
153
+ .append(frozenFrames)
154
+ .toString());
155
+
156
+ // Stop and record trace
157
+ perfScreenTrace.stop();
158
+ }
159
+
160
+ // endregion
161
+
162
+ // region Helper Functions
163
+
164
+ private static boolean checkScreenTraceSupport(Activity activity) {
165
+ boolean isValidSDKVersion = checkSDKVersion();
166
+ boolean hasFrameMetricsAggregatorClass = checkFrameMetricsAggregatorClass();
167
+ boolean isActivityHardwareAccelerated =
168
+ activity.getWindow() != null
169
+ && ((activity.getWindow().getAttributes().flags
170
+ & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
171
+ != 0);
172
+
173
+ boolean supported =
174
+ isValidSDKVersion && hasFrameMetricsAggregatorClass && isActivityHardwareAccelerated;
175
+
176
+ Log.d(
177
+ TAG,
178
+ new StringBuilder()
179
+ .append("isValidSDKVersion: ")
180
+ .append(isValidSDKVersion)
181
+ .append("isScreenTraceSupported(")
182
+ .append(activity)
183
+ .append("): ")
184
+ .append(supported)
185
+ .append(" [hasFrameMetricsAggregatorClass: ")
186
+ .append(hasFrameMetricsAggregatorClass)
187
+ .append(", isActivityHardwareAccelerated: ")
188
+ .append(isActivityHardwareAccelerated)
189
+ .append("]")
190
+ .toString());
191
+
192
+ return supported;
193
+ }
194
+
195
+ private static boolean checkSDKVersion() {
196
+ if (Build.VERSION.SDK_INT == 26 || Build.VERSION.SDK_INT == 27) {
197
+ return false;
198
+ }
199
+
200
+ return true;
201
+ }
202
+
203
+ /** Inspired by fireperf source. */
204
+ private static boolean checkFrameMetricsAggregatorClass() {
205
+ try {
206
+ Class<?> initializerClass = Class.forName(FRAME_METRICS_AGGREGATOR_CLASSNAME);
207
+ return true;
208
+ } catch (ClassNotFoundException e) {
209
+ return false;
210
+ }
211
+ }
212
+
213
+ /** Inspired by fireperf source. */
214
+ private String getScreenTraceName() {
215
+ return Constants.SCREEN_TRACE_PREFIX + traceName;
216
+ }
217
+
218
+ // endregion
219
+ }
@@ -17,6 +17,7 @@ package io.invertase.firebase.perf;
17
17
  *
18
18
  */
19
19
 
20
+ import android.app.Activity;
20
21
  import android.content.Context;
21
22
  import android.os.Bundle;
22
23
  import android.util.SparseArray;
@@ -33,6 +34,7 @@ import java.util.Set;
33
34
 
34
35
  public class UniversalFirebasePerfModule extends UniversalFirebaseModule {
35
36
  private static SparseArray<Trace> traces = new SparseArray<>();
37
+ private static SparseArray<ScreenTrace> screenTraces = new SparseArray<>();
36
38
  private static SparseArray<HttpMetric> httpMetrics = new SparseArray<>();
37
39
 
38
40
  UniversalFirebasePerfModule(Context context, String serviceName) {
@@ -44,6 +46,7 @@ public class UniversalFirebasePerfModule extends UniversalFirebaseModule {
44
46
  super.onTearDown();
45
47
  traces.clear();
46
48
  httpMetrics.clear();
49
+ screenTraces.clear();
47
50
  }
48
51
 
49
52
  @Override
@@ -100,6 +103,28 @@ public class UniversalFirebasePerfModule extends UniversalFirebaseModule {
100
103
  });
101
104
  }
102
105
 
106
+ Task<Void> startScreenTrace(Activity activity, int id, String identifier) {
107
+ return Tasks.call(
108
+ () -> {
109
+ ScreenTrace screenTrace = new ScreenTrace(activity, identifier);
110
+ screenTrace.recordScreenTrace();
111
+ screenTraces.put(id, screenTrace);
112
+
113
+ return null;
114
+ });
115
+ }
116
+
117
+ Task<Void> stopScreenTrace(int id) {
118
+ return Tasks.call(
119
+ () -> {
120
+ ScreenTrace trace = screenTraces.get(id);
121
+ trace.sendScreenTrace();
122
+ screenTraces.remove(id);
123
+
124
+ return null;
125
+ });
126
+ }
127
+
103
128
  Task<Void> startHttpMetric(int id, String url, String httpMethod) {
104
129
  return Tasks.call(
105
130
  () -> {
@@ -17,6 +17,7 @@ package io.invertase.firebase.perf;
17
17
  *
18
18
  */
19
19
 
20
+ import android.app.Activity;
20
21
  import com.facebook.react.bridge.Arguments;
21
22
  import com.facebook.react.bridge.Promise;
22
23
  import com.facebook.react.bridge.ReactApplicationContext;
@@ -85,6 +86,42 @@ public class ReactNativeFirebasePerfModule extends ReactNativeFirebaseModule {
85
86
  });
86
87
  }
87
88
 
89
+ @ReactMethod
90
+ public void startScreenTrace(int id, String identifier, Promise promise) {
91
+ Activity currentActivity = getCurrentActivity();
92
+
93
+ // protect against NPEs
94
+ if (currentActivity == null) {
95
+ promise.resolve(null);
96
+ return;
97
+ }
98
+
99
+ module
100
+ .startScreenTrace(currentActivity, id, identifier)
101
+ .addOnCompleteListener(
102
+ task -> {
103
+ if (task.isSuccessful()) {
104
+ promise.resolve(task.getResult());
105
+ } else {
106
+ rejectPromiseWithExceptionMap(promise, task.getException());
107
+ }
108
+ });
109
+ }
110
+
111
+ @ReactMethod
112
+ public void stopScreenTrace(int id, Promise promise) {
113
+ module
114
+ .stopScreenTrace(id)
115
+ .addOnCompleteListener(
116
+ task -> {
117
+ if (task.isSuccessful()) {
118
+ promise.resolve(task.getResult());
119
+ } else {
120
+ rejectPromiseWithExceptionMap(promise, task.getException());
121
+ }
122
+ });
123
+ }
124
+
88
125
  @ReactMethod
89
126
  public void startHttpMetric(int id, String url, String httpMethod, Promise promise) {
90
127
  module
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ import { isIOS } from '@react-native-firebase/app/lib/common';
19
+
20
+ let id = 0;
21
+
22
+ export default class ScreenTrace {
23
+ constructor(native, identifier) {
24
+ this.native = native;
25
+ this._identifier = identifier;
26
+ this._id = id++;
27
+ this._started = false;
28
+ this._stopped = false;
29
+ }
30
+
31
+ start() {
32
+ if (isIOS) {
33
+ return Promise.reject(new Error('Custom screentraces are currently not supported on iOS.'));
34
+ }
35
+ if (this._started) {
36
+ return Promise.resolve(null);
37
+ }
38
+ this._started = true;
39
+
40
+ return this.native.startScreenTrace(this._id, this._identifier);
41
+ }
42
+
43
+ stop() {
44
+ if (!this._started || this._stopped) {
45
+ return Promise.resolve(null);
46
+ }
47
+ this._stopped = true;
48
+
49
+ return this.native.stopScreenTrace(this._id);
50
+ }
51
+ }
package/lib/index.d.ts CHANGED
@@ -211,6 +211,48 @@ export namespace FirebasePerformanceTypes {
211
211
  stop(): Promise<null>;
212
212
  }
213
213
 
214
+ /**
215
+ * ScreenTrace allows you to record a custom screen rendering trace of slow and frozen frames.
216
+ * Throws on constructor if hardware acceleration is off or if Android is 9.0 or 9.1.
217
+ *
218
+ * @platform android Android !== 9.0.0 && Adnroid !== 9.1.0
219
+ */
220
+ export class ScreenTrace {
221
+ /**
222
+ * Starts a new screen trace. Does nothing if already started.
223
+ *
224
+ * #### Example
225
+ *
226
+ * ```js
227
+ * try {
228
+ * const trace = firebase.perf().newScreenTrace('FooScreen');
229
+ * await trace.start();
230
+ * } catch (e) {
231
+ *
232
+ * }
233
+ * ```
234
+ * @platform android Android >= 9.0.0
235
+ */
236
+ start(): Promise<null>;
237
+ /**
238
+ * Stops and sends the screen trace.
239
+ *
240
+ * #### Example
241
+ *
242
+ * ```js
243
+ * try {
244
+ * const trace = firebase.perf().newScreenTrace('FooScreen');
245
+ * await trace.start();
246
+ * await trace.stop();
247
+ * } catch (e) {
248
+ *
249
+ * }
250
+ * ```
251
+ * @platform android Android >= 9.0.0
252
+ */
253
+ stop(): Promise<null>;
254
+ }
255
+
214
256
  /**
215
257
  * Metric used to collect data for network requests/responses. A new instance must be used for every request/response.
216
258
  */
@@ -426,6 +468,45 @@ export namespace FirebasePerformanceTypes {
426
468
  */
427
469
  startTrace(identifier: string): Promise<Trace>;
428
470
 
471
+ /**
472
+ * Creates a ScreenTrace instance with the given identifier.
473
+ * Throws if hardware acceleration is diabled or if Android is 9.0 or 9.1.
474
+ *
475
+ * #### Example
476
+ *
477
+ * ```js
478
+ * try {
479
+ * const trace = firebase.perf().newScreenTrace('FooScreen');
480
+ * await trace.start();
481
+ * } catch (e) {
482
+ *
483
+ * }
484
+ * ```
485
+ *
486
+ * @param identifier Name of the trace, no leading or trailing whitespace allowed, no leading underscore '_' character allowed, max length is 100.
487
+ */
488
+ newScreenTrace(identifier: string): ScreenTrace;
489
+
490
+ /**
491
+ * Creates a ScreenTrace instance with the given identifier and immediately starts it.
492
+ * Throws if hardware acceleration is diabled or if Android is 9.0 or 9.1.
493
+ *
494
+ * #### Example
495
+ *
496
+ * ```js
497
+ * try {
498
+ * const trace = await firebase.perf().startScreenTrace('FooScreen');
499
+ * await trace.stop();
500
+ * } catch (e) {
501
+ *
502
+ * }
503
+ * ```
504
+ * @platform android Android !== 9.0.0 && Android !== 9.1.0
505
+ *
506
+ * @param identifier Name of the screen
507
+ */
508
+ startScreenTrace(identifier: string): Promise<ScreenTrace>;
509
+
429
510
  /**
430
511
  * Creates a HttpMetric instance for collecting network performance data for a single request/response
431
512
  *
package/lib/index.js CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  } from '@react-native-firebase/app/lib/internal';
24
24
  import HttpMetric from './HttpMetric';
25
25
  import Trace from './Trace';
26
+ import ScreenTrace from './ScreenTrace';
26
27
  import version from './version';
27
28
 
28
29
  const statics = {};
@@ -80,6 +81,21 @@ class FirebasePerfModule extends FirebaseModule {
80
81
  return trace.start().then(() => trace);
81
82
  }
82
83
 
84
+ newScreenTrace(identifier) {
85
+ if (!isString(identifier) || identifier.length > 100) {
86
+ throw new Error(
87
+ "firebase.perf().newScreenTrace(*) 'identifier' must be a string with a maximum length of 100 characters.",
88
+ );
89
+ }
90
+
91
+ return new ScreenTrace(this.native, identifier);
92
+ }
93
+
94
+ startScreenTrace(identifier) {
95
+ const screenTrace = this.newScreenTrace(identifier);
96
+ return screenTrace.start().then(() => screenTrace);
97
+ }
98
+
83
99
  newHttpMetric(url, httpMethod) {
84
100
  if (!isString(url)) {
85
101
  throw new Error("firebase.perf().newHttpMetric(*, _) 'url' must be a string.");
package/lib/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- module.exports = '16.6.0';
2
+ module.exports = '17.0.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native-firebase/perf",
3
- "version": "16.6.0",
3
+ "version": "17.0.0",
4
4
  "author": "Invertase <oss@invertase.io> (http://invertase.io)",
5
5
  "description": "React Native Firebase - React Native Firebase provides native integration with Performance Monitoring to gain insight into key performance characteristics within your React Native application.",
6
6
  "main": "lib/index.js",
@@ -29,7 +29,7 @@
29
29
  "performance monitoring"
30
30
  ],
31
31
  "peerDependencies": {
32
- "@react-native-firebase/app": "16.6.0"
32
+ "@react-native-firebase/app": "17.0.0"
33
33
  },
34
34
  "dependencies": {
35
35
  "@expo/config-plugins": "^5.0.4"
@@ -37,5 +37,5 @@
37
37
  "publishConfig": {
38
38
  "access": "public"
39
39
  },
40
- "gitHead": "6663dc24dc5697190b930aa510085a681fe81203"
40
+ "gitHead": "53a436278f3c4cb23e1ddb7d2c9f14a65ce4abc1"
41
41
  }