Android Agent SDK v8.x

Version 8.x

This is documentation for the most recent version 8.x. It is only supported for AppMon 2018 October.

If you're using older version, see Android manual setup and instrumentation.

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

Android Agent SDK is automatically added by the Dynatrace Android Gradle plugin. Alternatively, you can add the Android Agent 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 Android Agent

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

public class YourApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Dynatrace.startup(this, new AppMonConfigurationBuilder("<YourApplicationID>", "<YourAgentPathUrl>")
            ... // 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 Android Agent communication to ensure that the Agent is able to transmit the data to AppMon.

Configure Android Agent

Use the AppMonConfigurationBuilder class to customize all advanced Android Agent settings with different API methods.

new AppMonConfigurationBuilder("<YourApplicationID>", "<YourAgentPathUrl>")
    .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. Android Agent 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. Android Agent 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, Android Agent 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 Android Agent 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 Android Agent 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, Android Agent 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, Android Agent 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 Android Agent 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

Android Agent 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 AppMonConfigurationBuilder("<YourApplicationID>", "<YourAgentPathUrl>")
    .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

The Android Agent SDK enables you to dynamically adjust data-privacy settings so that you can build your apps in compliance with GDPR data-privacy regulations. To activate this feature, 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/deactivate crash reporting and to change the data-collection level based on the individual preferences of your end users. Each end user can select from three data-privacy levels:

  • off: Android Agent won't capture any monitoring data.
  • performance: Android Agent will only capture anonymous performance data. Monitoring data that can be used to identify individual users, such as user tags and custom values, won't be captured.
  • user behavior: Android Agent will capture both performance and user data. In this mode, Android Agent recognizes and reports on users who re-visit in future sessions.

By default, crash reporting is deactivated and data-collection level is set to off. Your end users can change the 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 the privacy settings in the future.

With the Dynatrace.applyUserPrivacyOptions method, you can adjust privacy settings based on the user's decision. The Android Agent 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 the Agent starts.

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

Hybrid apps that use the JavaScript agent inside a WebView

For hybrid applications that use the JavaScript agent, cookies must be set for each instrumented domain or server that the application communicates with. When the hybrid app monitoring feature is enabled, Android Agent 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 AppMonConfigurationBuilder("<YourApplicationID>", "<YourAgentPathUrl>")
    .withHybridMonitoring(true)
    .withMonitoredDomains("<domain1>", "<domain2>")
    .buildConfiguration();

Preserve cookies

Android Agent SDK version 8.191

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

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