Android manual setup and instrumentation

Getting Started

Do the following to setup Android apps for instrumentation:

  1. Configure your project
  2. Update your permissions
  3. Update your build script
  4. Start the agent

Configure your project

  1. Download the latest mobile agent. The file name is dynatrace-mobile-agents-<version>.zip and unpack it to a directory of your choice.
  2. Add the library Dynatrace.jar from the folder Android/agent to your project. In Android Studio, place the Dynatrace.jar file in the default library folder <project_name>/<module_name>/libs. Your module's build.gradle configuration imports it with the dependency compile fileTree(dir: 'libs', include: ['*.jar']).
  3. If you have a hybrid application that targets API 17 or higher, then you also have to include the com.dynatrace.android.ext.jsi.jar library from the Android/extensions folder.

Update your permissions

The mobile app must have the INTERNET and ACCESS_NETWORK_STATE permissions to use the Android mobile agent, as shown below.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.testapp" >
	<uses-permission android:name="android.permission.INTERNET"/>
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
...

The following permission is optional. See details in Lifecycle Instrumentation and Limitations below.

<uses-permission android:name="android.permission.GET_TASKS"/>

Note

If permissions change for an app such as for instrumentation, users must run a manual update on the Google Play store once and can then accept the new permission level.

Update your build script

If the compileSdkVersion setting in your build.gradle file is 23 (Android 6.0) or later, then you must add the compile-time dependency for the Apache HTTP client library as shown below.

android {
    useLibrary 'org.apache.http.legacy'
}

Start the agent

Call the Dynatrace.startup method to initialize and start the mobile agent as shown below. You must provide a unique application id and the address of your instrumented web or application server as parameter values. Invoke the Dynatrace.startup method as early as possible. Using the onCreate method from your application class is recommended.

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        Dynatrace.startup(this, "MyAppId", "http://myhost.mydomain.com", false, null);
    }
}

If specifying an HTTPS address in the agentPath parameter, the agent verifies the server certificate and the host name. The agent communication with the instrumented web or application server fails, when the verification steps can't be successfully completed. If you don't have a root certificate for you instrumented web or application server, you must provide a KeyStore object. This object mast hold the certificate chain of the web server you want to connect to. You can also deactivate the certificate validation with the useAnyCert parameter. Host name verification can't be deactivated.

KeyStore trusted = KeyStore.getInstance("BKS");
InputStream in = getResources().openRawResource(R.raw.mykeystore);
try {
    trusted.load(in, "myverysecretpassword".toCharArray());
} finally {
    in.close();
}
Dynatrace.startup(this, "MyAppId", "https://myhost.mydomain.com:443", false, trusted);

Note

If using BouncyCastle as your keystore instance, ensure that the keystore is generated with BouncyCastle 1.45. The BouncyCastle 1.46 file format is not compatible with older versions of Android, see this Android AOSP issue for more details.

Crash reporting

Crash reporting is disabled by default. To activate or deactivate crash reporting, call the Dynatrace.enableCrashReporting method. If you deactivate crash reporting, the agent ignores the corresponding value from your user experience configuration.

When crash reporting is activated, the mobile 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. When the crash occurs, while a lifecycle action is active, the mobile agent attaches the crash report to the lifecycle action.

Lifecycle instrumentation

By default, lifecycle data collection is on. You can turn it off in the user experience configuration file.

Complete the following tasks to enable lifecycle data capturing:

You can also use Auto-Instrumentation for Android to automatically perform this instrumentation.

Update the AndroidManifest.xml file

  1. If the application name element does not specify an application class, add android:name="com.dynatrace.android.app.Application" and skip the next step Update the application class.
  2. Optionally add the GET_TASKS permission. See the section Update your permissions for more information.

The GET_TASKS permission detects application lifecycle transitions from foreground to background. This method provides the most reliable detection of application state changes, if lifecycle monitoring is enabled. It does not collect, store, or transmit which applications are running on a user's device. The mobile agent can only reliably determine the foreground/background transition, if all activities in an application extend Dynatrace activities. The GET_TASKS permission is only needed when your application supports Android versions prior to Android 5.0.

Update the application class

If your AndroidManifest.xml specifies an application class (the android:name attribute of the application element), you must derive it from the Dynatrace com.dynatrace.android.app.Application class. For example, change the import statement from import android.app.Application to import com.dynatrace.android.app.Application.

Note

Your lifecycle methods such as onCreate from the application and activity classes must call their super methods.

Update the activity classes

Similar to the application class, your activity classes must derive from the corresponding Dynatrace activity class. Therefore, change the import statement by prefixing com.dynatrace. to the package name. For example, change import android.app.Activity to import com.dynatrace.android.app.Activity.

The Dynatrace.jar library supports the following activity classes:

  • android.app.Activity
  • android.app.AliasActivity
  • android.app.ListActivity
  • android.app.ExpandableListActivity
  • android.app.LauncherActivity

Your app may contain activity classes, that are not part of this list. Such activities are android.support.v7.app.AppCompatActivity, com.google.android.maps.MapActivity or other activity classes from third party libraries. In this case you must generate a wrapper class for these activity classes. Therefore you have to execute the following steps:

  1. Generate a class with the name of activity class in the package com.dynatrace.android.app.
  2. Copy the code snippet below into your new class file.
  3. Replace the activity name placeholder with the activity name.
  4. Derive your activity classes from the new wrapper class.

Collected lifecycle data

With lifecycle instrumentation, the mobile agent automatically collects the following data and renders it as actions:

  • Application Start Event: Represents the time from Application.onCreate() to Activity.onPostResume() of the first activity displayed. If the first activity does not extend a Dynatrace activity, the application-start event time is not recorded correctly.
  • Activity Display: Represents the time from Activity.onCreate() to Activity.onPostResume().
  • Activity Redisplay: Represents the time from Activity.onStart() to Activity.onPostResume().

These activities' display/redisplay actions can also contain web requests or crash events.

User action monitoring

Create user actions with the Dynatrace.enterAction method and close them with the leaveAction method. The resulting action is visible as a mobile user action PurePath in the AppMon client. For example, you can use the enterAction and leaveAction methods to determine how long it takes to pull content from a remote server. You get visibility to the corresponding backend transactions within the action scope. Other event types (reportEvent, reportValue and reportError) can be reported within the context of the action object, thus creating a series of events within an action scope.

The reportError event is different from the reportValue event in that it is specifically identified as an error type of event. The integer value most likely contains an error code. You can also use the Dynatrace.reportError method to create a stand-alone error action.

Note

When the parent action is closed, the mobile agent automatically closes all nested/child actions of the parent action. Keep in mind, that the mobile agent discards the captured action data (including child actions), when the action is not closed.

User action PurePath example

The code below shows a sample instrumentation of the fictional method search, which makes a web request to an instrumented application server and parses the received result.

public boolean search(String query) {
	// [1a] start outer/parent action
	DTXAction searchAction = Dynatrace.enterAction("search");

	// [2a] start first inner action
	DTXAction searchRequestAction = Dynatrace.enterAction("search request", searchAction);

	try {
		// [3] tag a web request inside the inner action
		URL url = new URL("http://<host_name>/ajax?searchquery=" + query);
		HttpURLConnection connection = (HttpURLConnection) url.openConnection();
		searchRequestAction.tagRequest(connection);

		InputStream stream = connection.getInputStream(); // can throw an exception

		// [4] add a named event
		searchRequestAction.reportEvent("read input stream");
		readStream(stream);

		// [2b] end first inner action
		searchRequestAction.leaveAction();
	} catch (Exception e) {
		// [7] add an error
		searchRequestAction.reportError("connection failed", e);

		// [2b] end first inner action
		searchRequestAction.leaveAction();

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

		return false;
	}

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

	results = parseResult();

	// [6] add a custom value
	parseAction.reportValue("list size", results.size());
	// [5b] end second inner action
	parseAction.leaveAction();

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

	return true;
}

The figures below show the two differnt user actions, that are generated by the provided sample instrumentation.

User action example with a successful web request
User action example with a successful web request
User action example with an unsuccessful web request
User action example with an unsuccessful web request

Web request monitoring

The following list shows the supported HTTP frameworks:

For all other HTTP frameworks, ensure that the Dynatrace mobile tag is placed on the HTTP request.

Tagging

Web request tagging lets the AppMon server identify mobile traffic and correlates web request server-side PurePath data to a corresponding mobile user action. Therefore the mobile agent must add an additional HTTP header to your web request. The web server agent analyzes this HTTP header, and the AppMon server generates a corresponding web request PurePath. For web requests to unmonitored servers such as third party servers, no web request PurePath is generated.

There are two ways to assign a web request to an action.

  • Use the DTXAction.tagRequest methods of a specific action to assign a web request to this action.
  • Use the Dynatrace.tagRequest methods to let the mobile agent assign the web requests to an appropriate user action.

The mobile agent uses the following rule set:

  1. If the current thread has an open action, when the request is tagged, then the mobile agent assigns the web request to the open action.
  2. If the current thread has no open action, the AppMon server assigns the web request to an action that calls its leaveAction() method in the same thread where the tagging is done. The tagging time must fall between the action enter and leave times.
  3. If neither of the preceding cases applies, the web request is not assigned to any action. In this case, the server-side PurePath data is not linked to a mobile user action, but PurePath data can be identified as mobile traffic in the transaction flow.

The tagRequest(java.net.HttpURLConnection) and tagRequest(org.apache.http.HttpRequest) methods generate and add the correct HTTP header to your web request automatically.

DTXAction action = Dynatrace.enterAction("<action_name>");
URL url = new URL("http://myhost.mydomain.com/example");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
action.tagRequest(connection);

// handle web request

action.leaveAction();
DTXAction action = Dynatrace.enterAction("<action_name>");
HttpGet get = new HttpGet("http://myhost.mydomain.com/example");
action.tagRequest(get);

// handle web request

action.leaveAction();

For the Apache HttpClient library, you can also use the Dynatrace.registerRequestTaggingInterceptor(org.apache.http.impl.client.DefaultHttpClient) method. This method adds the additional HTTP header to all requests executed with the given DefaultHttpClient. The interceptor overwrites any existing tag placed on the request.

For third party libraries, you must use the getRequestTag() general method to generate the correct tag value. You must manually add the needed HTTP header to your web request. Use the Dynatrace.getRequestTagHeader() method to get the header key, as shown below for the OkHttp 3 library.

DTXAction action = Dynatrace.enterAction("<action_name>");
Request request = new Request.Builder()
		.url("http://myhost.mydomain.com/example")
                .addHeader(Dynatrace.getRequestTagHeader(), action.getRequestTag())
                .build();

// handle web request

action.leaveAction();

Note

Web request tagging does not measure network contribution time. The generated web request PurePath only contains the timing values from the server agent. You must use web request timing for full visibility.

Timing

Web request timing is an extension of web request tagging. It lets you monitor the web request on the mobile side. It also monitors web requests to unmonitored servers.

You must use web request tagging and create manual timings by using the WebRequestTiming class. The Dynatrace.getWebRequestTiming(java.net.HttpURLConnection) and Dynatrace.getWebRequestTiming(org.apache.http.HttpRequest) methods tag your web request (like the Dynatrace.tagRequest methods) and provide the corresponding WebRequestTiming object. For third party libraries, you must use the general method Dynatrace.getWebRequestTiming(String) and generate the parameter value with the Dynatrace.getRequestTag() method. You must manually add the needed HTTP header to your web request.

Call the startWebRequestTiming method before the web request is sent to the server. Call the stopWebRequestTiming method after receiving and reading the HTTP response. The mobile agent generates a web request action with measured timing values and the AppMon server appends the server PurePath data to this web request action. You can see network timings in the Network Contribution column of the User Actions PurePath dashlet and other dashlets.

Examples

Difference between tagging and timing

When you use web request tagging, the AppMon server generates the corresponding mobile web request PurePath. The timing values are based on the server-side timing values. As shown below, the timing values of the mobile web request don't fit the mobile user action search request. This is because the mobile devices' system clocks differ from the server clock. The mobile agent tries to minimize this offset and provides timing values based on the server time.

Example with web request tagging
Example with web request tagging

When you use web request timing, the mobile agent generates a mobile web request event and sends the data to the AppMon server. The AppMon Server links the PurePath data from the mobile and Server agent. This approach lets you measure the network contribution time and shows the correct time values for mobile nodes in the user action PurePath dashlet. Unfortunately the server node may differ from the mobile nodes because of the system clock difference, as shown below.

Example with web request timing
Example with web request timing

Advanced settings

Most mobile agent-specific settings can be set as parameter for the Dynatrace.startup method. Some advanced settings must be specified by adding a Dynatrace.properties file to the assets folder. The following table shows the available advanced configuration settings.

Key Type Default Description
DTXLogLevel String Debug logs can be activated with the value debug.
DTXAllowFileCookies boolean false Set the value to true to use file cookies.

Note

If you use a combination of manual and auto-instrumentation, the auto-instrumentor overrides the Dynatrace.properties file and your configuration is lost. In this case you must move your configuration settings to the auto-instrumentor properties file. The auto-instrumentor ensures that these properties are handled correctly and are part of the generated Dynatrace.properties file.

The Dynatrace.properties file does not support the other auto-instrumentation properties. You can't use these properties for manual instrumentation, because the corresponding instrumentation is missing.

ProGuard settings

ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and removes unused classes, fields, methods, and attributes. It is recommended to exclude all mobile agent classes by adding the following lines to your proguard.pro file:

-keepattributes EnclosingMethod
-keep class com.dynatrace.** { *; }

Note

If you are using a combination of manual and auto-instrumentation, you must exclude all mobile agent classes from obfuscation. This step is necessary, because the auto-instrumentor inserts non-obfuscated code into your application.

If you only use manual instrumentation, you can use ProGuard to obfuscate the Dynatrace.jar library. For hybrid applications, you must exclude the class JsNativeBridgeAPI17 from obfuscation. The class JsNativeBridgeAPI17 is part of the com.dynatrace.android.ext.jsi.jar library .

-keep com.dynatrace.android.ext.jsi.JsNativeBridgeAPI17 { *; }

Limitations

  • For Android 4.4 and earlier, the agent can't detect the correct foreground or background application state unless all activity classes are instrumented. You also have to add the GET_TASKS permission.
  • The agent does not contain a specific web request tagging and timing API for third party web request frameworks. You have to use the general API methods (like Dynatrace.getWebRequestTiming(String)).
  • There is limited support for multi-process applications, as each process starts its own agent and has its own session number.

Overhead

Adding the Android mobile agent (whether manually or through auto-instrumentation) to an application increases the size of the application by about 100 KB to 200 KB. The size increase depends on the implementation of your application and how effectly Android can store the mobile agent and the instrumentation code in your .dex files.

The impacts on app performance and mobile data usage are summarized in the general overhead section.