Record Android sessions
Use the sessionReplay module in Android applications.
Prerequisites
You must have an enterprise subscription.
You must use Splunk RUM agent version
2.0.0or higher.
Introduction
This page explains how to use the sessionReplay module in Android applications. This module provides several APIs so that you can choose which ones work best for your application.
Supported languages Java, Kotlin compileVersion 34 minVersion 21 targetVersion 31 Java version 1.8 Kotlin version 1.7.20 - APIs
- Most APIs are methods within
SplunkRum.instance.sessionReplay:In addition, there are some Kotlin-specific extensions (properties) of standard Android classes, such as:
SplunkRum.instance.sessionReplay
Get a sessionReplay instance, which is used to configure and start the recording.
The instance doesn't automatically start recording when you create it. To start recording, call its start() method.
- Kotlin
val sessionReplay = SplunkRum.instance.sessionReplay- Java
SessionReplay sessionReplay = SessionReplay.getInstance();
Start recording
- Kotlin
SplunkRum.instance.sessionReplay.start()- Java
SessionReplay.getInstance().start();
Stop recording
There is no need to manually stop the recording when the application enters the background; the recording process automatically stops, and the sessionReplay module provides the final data chunk. When the application returns to the foreground, the sessionReplay module resumes recording automatically, provided it was active before the application was previously suspended.
If the application is forcibly terminated (not just suspended to the background) or crashes, the behavior of the sessionReplay module depends upon the platform:
If the system permits some time to process the incident, the last data chunk is immediately made available.
If not, the most recent replay data chunk prior to the incident is reconstructed and published when the
sessionReplaymodule is re-initialized during the application's next run. This includes the corresponding metadata (the actual start and end time stamps of the reconstructed chunk).
After the incident, the sessionReplay module doesn't resume recording automatically.
- Kotlin
SplunkRum.instance.sessionReplay.stop()- Java
SessionReplay.getInstance().stop();
Get preferences
- Kotlin
SplunkRum.instance.sessionReplay.preferences- Java
SessionReplay.getInstance().getPreferences();
Set the rendering mode
When your application displays sensitive or unnecessary data that you don’t want to record, you can set a rendering mode to protect or hide that information. Valid values:
NATIVE: Regularly captures the application screen which thesessionReplaymodule immediately processes to remove sensitive data. The frames are then complied to make the session recording. The representation of the recording is video.Note: IfRenderingModeisNATIVE, both the video and wireframe recordings operate simultaneously due to the nature of the implementation. Consequently, both modes are accessible to the player.WIREFRAME_ONLY: Renders the application using only a wireframe representation of the screen data. No user data is recorded. The representation of the recording is in JSON format.
- Kotlin
enum class RenderingMode { NATIVE, WIREFRAME_ONLY }val preferences = SplunkRum.instance.sessionReplay.preferences val renderingMode: RenderingMode = preferences.renderingMode preferences.renderingMode = RenderingMode.NATIVE- Java
public enum RenderingMode { NATIVE, WIREFRAME_ONLY }Preferences preferences = SessionReplay.getInstance().getPreferences(); RenderingMode renderingMode = preferences.getRenderingMode(); preferences.setRenderingMode(RenderingMode.NATIVE);
Set the recording mask
In cases where areas of the app shouldn't be recorded, but cannot be defined by a View, you can use the RecordingMask object:
- Kotlin
val recordingMask = RecordingMask( listOf( RecordingMask.Element( Rect(left, top, right, bottom), RecordingMask.Element.Type.COVERING|RecordingMask.Element.Type.ERASING ) ) ) SplunkRum.instance.sessionReplay.recordingMask = recordingMask- Java
ArrayList<RecordingMask.Element> elements = new ArrayList<>(); elements.add( new RecordingMask.Element( new Rect(left, top, right, bottom), RecordingMask.Element.Type.COVERING|RecordingMask.Element.Type.ERASING ) ); RecordingMask recordingMask = new RecordingMask(elements); SessionReplay.getInstance().setRecordingMask(recordingMask);You can only have one
RecordingMaskset at a time, but it can contain a list ofRecordingMask.Elementobjects to cover multiple areas at once.The
RecordingMask.Elementcan be one of two types:RecordingMask.Element.Type.COVERING: The area defined by the elementRectis not recorded.RecordingMask.Element.Type.ERASING: The area defined by the elementRectis recorded even if a previousRecordingMask.Elementinside a list wascoveringthe area.
The following screenshots describe a
RecordingMaskin action.On the left:
The blue box represents a
video_itemelement.The red box represents a
video_item_imageelement.
On the right:
The
video_itemelement (blue box) has a.COVERINGvalue. The.COVERINGvalue masks the element in the session recording.The
video_item_imageelement (red box) has an.ERASINGvalue. The image is visible in the session recording because the.ERASINGvalue cancels the.COVERINGvalue.
Set the sensitivity of a UI element or view
When you mark UI elements or views as sensitive, the sessionReplay module masks them in the recording directly on the device. The way to mark elements as sensitive depends on the element type:
Jetpack Compose layout elements can be marked as sensitive using the
isSensitivitemodifier:Text( text = "I'm sensitive text", modifier = Modifier .sessionReplay( isSensitive = true ) )XML layouts:
ViewYou can set the sensitivity on any
Viewinstance:- Kotlin
sampleView.isSensitive = true|false|null- Java
SessionReplay.getInstance().getSensitivity().setViewInstanceSensitivity(sampleView, true|false|null);
You can also tag any
Viewdirectly in theXMLlayout file:<View> <tag android:id="@id/sr_sensitivity" android:value="true|false"/> </View>XML layouts:
ClassYou can set the sensitivity on all instances of a
Classthat extends aViewrather than setting the sensitivity on a specificView:- Kotlin
SampleViewClass::class.isSensitive = true|false|null- Java
SessionReplay.getInstance().getSensitivity().setViewInstanceSensitivity(view, true|false|null);
Default sensitive classes
By default, the
EditTextclass is set to sensitive. To override the sensitivity at the class or instance level, instance sensitivity needs to be set tofalseornull.Sensitivity prioritization
When determining if the
Viewinstance is sensitive, the resolution process checks the sensitivity in a strict order.Viewinstances are not recorded if:The XML has the
sl_sensitivitytag set totrue.The
sensitivityis set totrue.the
Classsensitivity is set totrue.
Class hierarchy and sensitivity
Sensitivity set to a more specific class (deeper in the inheritance tree) has higher priority. Let's demonstrate this principle in the example using the inheritance tree:
If
TextViewis set to be sensitive andRadioButtonis explicitly set to not be sensitive:- Kotlin
TextView::class.isSensitive = true RadioButton::class.isSensitive = false- Java
SessionReplay.getInstance().getSensitivity().setViewClassSensitivity(TextView.class, true); SessionReplay.getInstance().getSensitivity().setViewClassSensitivity(RadioButton.class, false);
These statements are factual if we assume no
Viewinstance-specific sensitivity is set:All instances of
TextView,Button,CompoundButton,RadioButton,Switch, andToggleButtonare sensitive.All instances of
RadioButtonare not sensitive, even thoughRadioButtoninherits from the sensitive classTextView.
WebView layouts
WebViewisn't sensitive by default, but if you need to handle sensitivity within aWebViewclass, you can mark all sensitive elements on the displayed website as sensitive so that they are hidden. Make these HTML elements part of the CSS.session-replay-hideclass:<div class='session-replay-hide'> This will be hidden. </div>All inputs are hidden by default except
buttonandsubmit. If some hidden inputs should be recorded, make them part of the CSS.session-replay-showclass:<input type="text" class='session-replay-show'>
Get the state
- Kotlin
SplunkRum.instance.sessionReplay.state- Java
SessionReplay.getInstance().getState();
Get the rendering mode
- Kotlin
val state = SplunkRum.instance.sessionReplay.state val renderingMode: RenderingMode = state.renderingMode- Java
State state = SessionReplay.getInstance().getState(); RenderingMode renderingMode = state.getRenderingMode();
Get the recording status
- Kotlin
val status = SplunkRum.instance.state.status when (status) { is Status.Recording -> { println("Recording in progress...") } is Status.NotRecording.NotStarted -> { println("Recording has not started.") } is Status.NotRecording.Stopped -> { println("Recording was stopped.") } is Status.NotRecording.BelowMinSDKVersion -> { println("Cannot record: below minimum SDK version.") } is Status.NotRecording.StorageLimitReached -> { println("Cannot record: storage limit reached.") } is Status.NotRecording.InternalError -> { println("Cannot record: internal error.") } } if (status.isRecording) { println("Do something only if we are recording") }- Java
Status status = SessionReplay.getInstance().getState().getStatus(); if (status instanceof Status.Recording) { System.out.println("Recording..."); } else if (status instanceof Status.NotRecording.NotStarted) { System.out.println("Not started."); } else if (status instanceof Status.NotRecording.Stopped) { System.out.println("Stopped."); } else if (status instanceof Status.NotRecording.BelowMinSDKVersion) { System.out.println("Below SDK."); } else if (status instanceof Status.NotRecording.StorageLimitReached) { System.out.println("Storage limit reached."); } else if (status instanceof Status.NotRecording.InternalError) { System.out.println("Internal error."); } if (status.getIsRecording()) { System.out.println("Do something because it's recording"); }
Procedure
Set the module's rendering mode, recording mask, and sensitivity.
Results
The sessionReplay module autonomously captures session data at its own rate and periodically publishes the data, typically several times per minute, through its API. To replay a user session, use replay player in the Splunk RUM UI.