@react-native-firebase/perf 16.6.0 → 17.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }