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.

When Android Agent starts for the first time, Android Agent deactivates crash reporting and sets the data-collection level to off. You should change the privacy settings, based on each user's individual preference when your app starts for the first time. Android Agent doesn't provide a privacy settings dialog or any similar UI component. You must integrate a privacy dialog into your app. We recommend that you display the privacy dialog before your app is displayed and then apply the user's privacy preference. You should also allow your users to change their privacy settings in the future.

Android Agent persists the privacy setting and automatically applies it when the app is restarted. Each time the user changes the data-collection level, a new session with the new privacy settings will be generated by Android Agent. Don't wrap this API method with a user action. Otherwise, Android Agent won't be able to attach the user action to the correct session.

With the Dynatrace.setCrashReportingOptedIn(boolean) method, you can activate or deactivate crash reporting. The data collection level can be changed with the Dynatrace.setDataCollectionLevel(DataCollectionLevel) method.

You can retrieve the privacy settings with the Dynatrace.getDataCollectionLevel() and Dynatrace.isCrashReportingOptedIn() methods. But you can't use the privacy API methods before Android Agent is started. Otherwise, Android Agent will throw an exception.

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();