OneAgent SDK for Android

OneAgent SDK for Android can be used to report additional details about the mobile user sessions of your app. OneAgent SDK for Android enables you to create custom user actions, measure web requests, report errors, and tag specific users. This topic explains how to enable these capabilities.

OneAgent SDK is automatically added by the Dynatrace Android Gradle plugin. Alternatively, you can add the OneAgent SDK when you want to use standalone manual instrumentation for your Android application project.

Note: All technical information is available at JavaDoc for OneAgent SDK.

Start OneAgent

If you've disabled auto-start with the property autoStart.enabled or you're using standalone manual instrumentation instead of auto-instrumentation, start OneAgent manually in the Application.onCreate method.

public class YourApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Dynatrace.startup(this, new DynatraceConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconUrl>")
            ... // additional configuration
            .buildConfiguration());
    }
}

If your app supports Direct Boot, ensure that Dynatrace.startup is never called from a Direct Boot aware component. You should also see Adjust OneAgent communication to ensure that OneAgent is able to transmit the data to the cluster.

Configure OneAgent

Use the DynatraceConfigurationBuilder class to customize all advanced OneAgent settings with different API methods.

new DynatraceConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconURL>")
    .withUserOptIn(true)
    .withCrashReporting(true)
    .buildConfiguration();

Note:
If you use a combination of manual and auto-instrumentation, the auto-instrumentation injects a Dynatrace.startup call into the Application.onCreate method. In this case, you will lose your manual configuration because the Dynatrace.startup call from the auto-instrumentation is called before your Dynatrace.startup call.

The autoStart.enabled property allows you to deactivate the auto-start feature from the auto-instrumentation. You can then define a manual Dynatrace.startup call in the Application.onCreate method. In this case, you can override the values preconfigured from the auto-instrumentor.

User action monitoring

With user action monitoring you can define and report your own custom user actions. These user actions can be enriched with the following monitoring operations:

Manually created user actions are different from user actions created with the Dynatrace Android Gradle plugin. OneAgent not only adds additional events (such as web requests) to your user action, it will also not automatically close your user actions after a specific time period.

Create custom user actions

You can define your own custom user actions. First, you have to create them. Then you can enhance them with additional information, and finally, you must close them. OneAgent discards all action-related monitoring data when the action isn't closed.

You must call enterAction to start each action and leaveAction to close each action. Timing is measured automatically.

// start user action
DTXAction action = Dynatrace.enterAction("exampleName");

// ...do some work here...

// end the action after the search completed
action.leaveAction();

Child actions

Child actions are similar to parent actions. When the parent action is closed, OneAgent automatically closes all nested/child actions of the parent action.

Child actions are generated using the method Dynatrace.enterAction(String, DTXAction).

// start parent user action
DTXAction parentAction = Dynatrace.enterAction("parent_user_action_name");

// ...do some work here...

// start child user action
DTXAction childAction = Dynatrace.enterAction("child_user_action_name", parentAction);

// ...do some work here...

// end the child action
childAction.leaveAction();

// ...do some work here...

// end the parent action
parentAction.leaveAction();

User action sample

The following code snippet shows a sample instrumentation of the fictional method search, which makes a web request to an instrumented server and parses the received result. The following instrumentation actions are part of the code snippet:

  1. creates a user action
  2. reports a custom metric
  3. reports a handled exception
  4. monitors a web request
  5. creates a child action
public boolean search(String query) {
    // [1a] start outer/parent action
    DTXAction searchAction = Dynatrace.enterAction("search");

    // [2] report your own metric
    searchAction.reportValue("query", query);

    URL url;
    try {
        url = new URL("https://www.example.com/?query=" + query);
    } catch (MalformedURLException e) {
        // [3] report an error
        searchAction.reportError("invalid url", e);

        // [1b] end outer action
        searchAction.leaveAction();
        return false;
    }

    // [4.1] Generate a new unique tag associated with the user action "search"
    String uniqueRequestTag = searchAction.getRequestTag();
    // [4.2] Generate a WebRequestTiming object based on the unique tag
    WebRequestTiming timing = Dynatrace.getWebRequestTiming(uniqueRequestTag);

    Request request = new Request.Builder()
            .url(url)
            // [4.3] Place the Dynatrace HTTP header on your web request
            .addHeader(Dynatrace.getRequestTagHeader(), uniqueRequestTag)
            .build();

    // [4.4] Start web request timing before the HTTP request is sent
    timing.startWebRequestTiming();
    try (Response response = client.newCall(request).execute()) {
        if (!response.isSuccessful()) {
            // [4.5] Stop web request timing when a connection exception occurs
            timing.stopWebRequestTiming(url, response.code(), response.message());
            return false;
        }
        String body = response.body().string();

        // [4.5] Stop web request timing when the HTTP response is received and the response body was obtained
        timing.stopWebRequestTiming(url, response.code(), response.message());

        // [5a] start inner action
        DTXAction parseAction = Dynatrace.enterAction("parse result", searchAction);

        parseResult(body);

        // [5b] end inner action
        parseAction.leaveAction();

        return true;
    } catch (IOException e) {
        // [4.5] Stop web request timing when a connection exception occurs
        timing.stopWebRequestTiming(url, -1, e.toString());

        return false;
    }
    finally {
        // [1b] end outer action
        searchAction.leaveAction();
    }
}

Custom value reporting

Report event

The report event feature allows you to report the time point of a specific event. The reported event must be part of a user action.

action.reportEvent("event_name");

Report value

The report value feature allows you to report your own metrics. These metrics must be part of a user action. The OneAgent SDK allows you to report

// report int
action.reportValue("int_metrics_key", 5);
// report double
action.reportValue("double_metrics_key", 5.6);
// report string
action.reportValue("string_metrics_key", "exampleValue");

Report errors

The reporting error feature is different from the reporting value feature in that it is specifically identified as an error type event. The OneAgent SDK allows you to report:

// report error code
action.reportError("error_code_key", -1);
// report exception
action.reportError("exception_key", exception);

You can also report errors as stand-alone error events via the class Dynatrace:

// report error code
Dynatrace.reportError("error_code_key", -1);
// report exception
Dynatrace.reportError("exception_key", exception);

Web request monitoring

To track web requests, add the x-dynatrace HTTP header with a unique value to the web request. The tag correlates the server-side monitoring data to the corresponding mobile web request. Additionally, the timing values from the mobile side must be measured.

Perform the following steps to successfully monitor a web request:

Generate a new unique tag.

Generate a WebRequestTiming object based on the tag.

Place the Dynatrace HTTP header on your web request.

Start web request timing before the HTTP request is sent.

Stop web request timing.

The HTTP response is received and the response body is obtained.

A connection exception occurs.

Note: Do not manually and auto-instrument the same web requests. This behavior can lead to incorrect monitoring data.

Web requests can either be:

Note: For monitoring standalone web requests, OneAgent automatically tries to find an appropriate user action. If it finds one, the web request is attached to the user action. The web request is only reported as a standalone web request when no appropriate user action is found.

Attach a web request to the user action

The following sample shows how a synchronous OkHttp web request could be monitored:

URL url = new URL("https://www.example.com");

// First, create a user action
DTXAction webAction = Dynatrace.enterAction("search request");
// [1] Generate a new unique tag associated with the user action
String uniqueRequestTag = webAction.getRequestTag();
// [2] Generate a WebRequestTiming object based on the unique tag
WebRequestTiming timing = Dynatrace.getWebRequestTiming(uniqueRequestTag);

// Define your OkHttp request, this varies greatly depending on your implementation
Request request = new Request.Builder()
        .url(url)
        // Define your headers for the OkHttp request
        .addHeader(yourKey1, yourValue1)
        .addHeader(yourKey2, yourValue2)
        // [3] Place the Dynatrace HTTP header on your web request
        .addHeader(Dynatrace.getRequestTagHeader(), uniqueRequestTag)
        .build();

// [4] Start web request timing before the HTTP request is sent
timing.startWebRequestTiming();
try (Response response = client.newCall(request).execute()) {
    if (response.isSuccessful()) {
        // handle response
        String body = response.body().string();
    }

    // [5.1] Stop web request timing when the HTTP response is received and the response body was obtained
    timing.stopWebRequestTiming(url, response.code(), response.message());
} catch (IOException e) {
    // [5.2] Stop web request timing when a connection exception occurs
    timing.stopWebRequestTiming(url, -1, e.toString());

    // user-defined exception handling
}
finally {
    // Lastly, leave the action
    webAction.leaveAction();
}

Standalone web request

The following sample shows how a synchronous OkHttp web request can be monitored:

URL url = new URL("https://www.example.com");

// [1] Generate a new unique tag
String uniqueRequestTag = Dynatrace.getRequestTag();
// [2] Generate a WebRequestTiming object based on the unique tag
WebRequestTiming timing = Dynatrace.getWebRequestTiming(uniqueRequestTag);

// Define your OkHttp request, this varies greatly depending on your implementation
Request request = new Request.Builder()
        .url(url)
        // Define your headers for the OkHttp request
        .addHeader(yourKey1, yourValue1)
        .addHeader(yourKey2, yourValue2)
        // [3] Place the Dynatrace HTTP header on your web request
        .addHeader(Dynatrace.getRequestTagHeader(), uniqueRequestTag)
        .build();

// [4] Start web request timing before the HTTP request is sent
timing.startWebRequestTiming();
try (Response response = client.newCall(request).execute()) {
    if (response.isSuccessful()) {
        // handle response
        String body = response.body().string();
    }

    // [5.1] Stop web request timing when the HTTP response is received and the response body was obtained
    timing.stopWebRequestTiming(url, response.code(), response.message());
} catch (IOException e) {
    // [5.2] Stop web request timing when a connection exception occurs
    timing.stopWebRequestTiming(url, -1, e.toString());

    // user-defined exception handling
}

HttpURLConnection

For monitoring HttpURLConnection web requests, OneAgent SDK contains an additional API method: Dynatrace.getWebRequestTiming(HttpURLConnection). It automatically generates a unique tag and places it on the HttpURLConnection connection. You can also use the WebRequestTiming.stopWebRequestTiming() method, because OneAgent can automatically determine the status code and message from the specified HttpURLConnection.

URL url = new URL("https://www.example.com");
HttpURLConnection conn = null;
WebRequestTiming timing = null;

// First, create an action
DTXAction webAction = Dynatrace.enterAction("search request");
try {
    conn = (HttpURLConnection) url.openConnection();

    // [1], [2], [3] Once the connection object is obtained, tag it automatically and receive a WebRequestTiming instance
    timing = Dynatrace.getWebRequestTiming(conn);

    // [4] Stop web request timing when the HTTP response is received and the response body was obtained
    timing.startWebRequestTiming();
    InputStream stream = new BufferedInputStream(conn.getInputStream());
    readStream(stream);

    // [5.1] Stop web request timing when the HTTP response is received and the response body was obtained
    timing.stopWebRequestTiming();
} catch (Exception e) {
    if(timing != null) {
        // [5.2] Stop web request timing when a connection exception occurs
        timing.stopWebRequestTiming(url, -1, e.toString());
    }

    // user-defined exception handling
} finally {
    if (conn != null) {
        conn.disconnect();
    }
    // Lastly, leave the action
    webAction.leaveAction();
}

Other samples

Crash reporting

OneAgent captures all unhandled exceptions and errors and sends the crash report immediately to the server. The Android crash report includes the occurrence time and the full stack trace of the exception.

Disable crash reporting

You can also deactivate crash reporting with the withCrashReporting method:

new DynatraceConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconURL>")
    .withCrashReporting(false)
    .buildConfiguration();

Tag specific users

You can tag each user of your mobile apps with a unique user name. This enables you to search and filter specific user sessions and analyze individual user behavior over time. The following steps explain how to manually tag an individual user via the Dynatrace API.

Dynatrace.identifyUser("john.doe@example.com");

Data privacy

To dynamically adjust privacy settings and build your apps in compliance with data protection laws and regulations, enable the userOptIn flag via the DSL from the Dynatrace Android Gradle plugin or use the ConfigurationBuilder.withUserOptIn method.

The privacy API methods allow you to dynamically activate and deactivate crash reporting and to change the data-collection level based on the individual preferences of your users. Each user can select from the following data-privacy levels:

  • off: Monitoring data isn't captured.
  • performance: Only anonymous performance data is captured. Monitoring data that can be used to identify individual users, such as user tags and custom values, isn't captured.
  • user behavior: Performance data and user data is captured. In this mode, OneAgent recognizes and reports users who revisit in the future.

By default, crash reporting is deactivated and data-collection level is set to off. Based on your end user's individual preference, they can change their privacy settings when the app starts for the first time. Dynatrace doesn't provide a consent banner or any similar UI component. You must integrate the network dialog into your app. We recommend that you display the dialog before your app is displayed and then apply the user's privacy preference. You must also allow your users to change their privacy settings in the future.

With the Dynatrace.applyUserPrivacyOptions method, you can adjust privacy settings based on the user's decision. OneAgent persists the privacy settings and automatically applies them when the app is restarted. Additionally, a new session is created whenever the privacy settings are changed via the Dynatrace.applyUserPrivacyOptions method.

You can retrieve the privacy settings with the Dynatrace.getUserPrivacyOptions method. However, you won't be able to retrieve the correct values if you use this method before OneAgent starts.

Dynatrace.applyUserPrivacyOptions(UserPrivacyOptions.builder()
    .withDataCollectionLevel(DataCollectionLevel.USER_BEHAVIOR)
    .withCrashReportingOptedIn(true)
    .build()
);

Hybrid apps that use the RUM JavaScript tag inside a WebView

For hybrid applications that use the RUM JavaScript tag, cookies must be set for each instrumented domain or server that the application communicates with. When the hybrid app monitoring feature is enabled, OneAgent generates these cookies for every specified domain and stores them in CookieManager.

You can activate the hybrid app monitoring feature with the withHybridMonitoring method. All used domains, hosts, and IP addresses must be specified via the withMonitoredDomains method. Domains and sub-domains must start with a dot (.).

new DynatraceConfigurationBuilder("<YourApplicationID>", "<ProvidedBeaconURL>")
    .withHybridMonitoring(true)
    .withMonitoredDomains("<domain1>", "<domain2>")
    .buildConfiguration();

Instrument WebView

OneAgent SDK for Android version 8.191

To enable communication between the RUM JavaScript tag and OneAgent for Android, all WebView objects must be instrumented before the URL is loaded with WebView.loadUrl(String). The Dynatrace.instrumentWebView method must be instrumented for every WebView that contains the RUM JavaScript tag. Without this method call, the monitoring data may not be associated with the same session.

WebView myWebView = (WebView) findViewById(R.id.webview);
Dynatrace.instrumentWebView(myWebView);
myWebView.loadUrl("http://www.example.com");

Note: Due to security reasons, this method call is ignored on devices with API versions 15 and 16.

Preserve cookies

OneAgent SDK for Android version 8.191

Call this method when you delete all cookies via CookieManager#removeAllCookies(ValueCallback) or CookieManager#removeSessionCookies(ValueCallback). Without the Dynatrace-related cookies, data from the RUM JavaScript tag and OneAgent for Android can't be combined into a single session.

CookieManager.getInstance().removeAllCookies(null);
Dynatrace.restoreCookies();