Skip to content

Android Application Integration


By collecting metrics data from Android applications, analyze application performance in a visualized manner.

Prerequisites

Note

If the RUM Headless service has already been activated, the prerequisites have been automatically configured, and you can proceed directly with the application integration.

Application Integration

  1. Navigate to User Analysis > Create > Android;
  2. Input the application name;
  3. Input the application ID;
  4. Select the application integration method:

    • Public DataWay: Directly receives RUM data without installing the DataKit collector.
    • Local Environment Deployment: Receives RUM data after meeting the prerequisites.

Installation

Source Code Address: https://github.com/GuanceCloud/datakit-android

Demo: https://github.com/GuanceDemo/guance-app-demo

Gradle Configuration

  • Add the remote repository address for SDK in the build.gradle file at the root directory of your project.
buildscript {
    //...
    repositories {
        //...
        //Add the remote repository address for SDK
        maven {
            url 'https://mvnrepo.jiagouyun.com/repository/maven-releases'
        }
    }
    dependencies {
        //...
        //Add the plugin dependency, AGP 7.4.2 or above, Gradle 7.2.0 or above
        classpath 'com.cloudcare.ft.mobile.sdk.tracker.plugin:ft-plugin:[latest_version]'
        // For AGP 7.4.2 versions below, use ft-plugin-legacy 
        //classpath 'com.cloudcare.ft.mobile.sdk.tracker.plugin:ft-plugin-legacy:[latest_version]'

    }
}
allprojects {
    repositories {
        //...
        //Add the remote repository address for SDK
        maven {
            url 'https://mvnrepo.jiagouyun.com/repository/maven-releases'
        }
    }
}
//setting.gradle
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        //Add the remote repository address for SDK
        maven {
            url('https://mvnrepo.jiagouyun.com/repository/maven-releases')
        }
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        //Add the remote repository address for SDK
        maven {
            url('https://mvnrepo.jiagouyun.com/repository/maven-releases')
        }
    }
}

//build.gradle
plugins{
    //Add the plugin dependency, AGP 7.4.2 or above, Gradle 7.2.0 or above
    id 'com.cloudcare.ft.mobile.sdk.tracker.plugin' version '[lastest_version]' apply false
    // For AGP 7.4.2 versions below, use ft-plugin-legacy 
    //id 'com.cloudcare.ft.mobile.sdk.tracker.plugin.legacy' version '[lastest_version]' apply false

    // Plugin configuration role: 
    // 1. Automatic collection: App startup, OkHttp requests, WebView activities, Activity transitions 
    // 2. Automatically collects View click events, Console Logcat 
    // 3. Automatically collects Console Logcat 
}
  • Add the SDK dependency and Plugin usage in the build.gradle file of the main module app of the project.
dependencies {
    //Add SDK dependency
    implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-sdk:[latest_version]'
    //Dependency to capture native layer crash information, must be used with ft-sdk, cannot be used alone
    implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-native:[latest_version]'
    //JSON serialization
    implementation 'com.google.code.gson:gson:2.8.+'
    //Optional, if network request automatic collection and automatic chain initiation are required, add this
    implementation 'com.squareup.okhttp3:okhttp:4.+'
}
//Apply plugin
apply plugin: 'ft-plugin'   //If using old version compatibility ft-plugin-legacy, no changes needed

//Configure plugin usage parameters (optional)
FTExt {
    //Whether to display Plugin logs, default is false
    //showLog = true

    //Set ASM version, supports asm7 - asm9, default is asm9
    //asmVersion='asm7'

    //ASM ignore path configuration, paths with . and / are equivalent
    //ignorePackages=['com.ft','com/ft']
}
android{
    //...Omitted part of code
    defaultConfig {
        //...Omitted part of code
        ndk {
            //When using ft-native to capture native layer crashes, choose supported abi architectures based on the different platforms adapted by the application
            //Currently ft-native includes abi architectures 'arm64-v8a',
            // 'armeabi-v7a', 'x86', 'x86_64'
            abiFilters 'armeabi-v7a'
        }
    }
    compileOptions {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }
}

The latest versions can be found above under ft-sdk, ft-plugin, and ft-native version names.

Application Configuration

The best place for SDK initialization is in the onCreate method of Application. If your application has not yet created an Application, you need to create one and declare it in AndroidManifest.xml. Example reference here.

<application 
       android:name="YourApplication"> 
</application> 

R8 / Proguard Obfuscation Configuration

If android.buildTypes needs minifyEnabled = true, enable the following configuration:

-dontwarn com.ft.sdk.**

### ft-sdk library
-keep class com.ft.sdk.**{*;}

### ft-native library
-keep class ftnative.*{*;}

### Prevent Action getting action_name from being obfuscated###
-keepnames class * extends android.view.View
-keepnames class * extends android.view.MenuItem

SDK Initialization

Basic Configuration

public class DemoApplication extends Application {

    @Override
    public void onCreate() {
        //Local environment deployment, Datakit deployment
        FTSDKConfig config = FTSDKConfig.builder(datakitUrl);
        //Use public DataWay
        FTSDKConfig config = FTSDKConfig.builder(datawayUrl, clientToken);
        //...
        //config.setDebug(true);                //debug mode
        config.setCompressIntakeRequests(true); //Enable data compression
        FTSdk.install(config);
    }
}
class DemoApplication : Application() {
    override fun onCreate() {
        //Local environment deployment, Datakit deployment
        val config = FTSDKConfig.builder(datakitUrl)
        //Use public DataWay
        val config = FTSDKConfig.builder(datawayUrl, clientToken)
        //...
        //config.setDebug(true);                //debug mode
        config.setCompressIntakeRequests(true)  //Enable data compression
        FTSdk.install(config)
    }
}
Method Name Type Required Meaning
datakitUrl String Yes Local environment deployment (Datakit) reporting URL address, example: http://10.0.0.1:9529, default port 9529, the device where the SDK is installed must be able to access this address. Note: Choose either datakitUrl or datawayUrl
datawayUrl String Yes Public Dataway reporting URL address, obtained from the [User Analysis] application, example: https://open.dataway.url, the device where the SDK is installed must be able to access this address. Note: Choose either datakitUrl or datawayUrl
clientToken String Yes Authentication token, must be configured simultaneously with datawayUrl
setDebug Boolean No Whether to enable debug mode. Default is false, enabling this allows printing SDK operation logs
setEnv EnvType No Set the collection environment, default is EnvType.PROD
setEnv String No Set the collection environment, default is prod. Note: Only configure one of String or EnvType types
setOnlySupportMainProcess Boolean No Whether to only support running in the main process, default is true, if execution is needed in other processes, set this field to false
setEnableAccessAndroidID Boolean No Enable obtaining Android ID, default is true, setting to false means that the device_uuid field data will not be collected, market privacy review related view here
addGlobalContext Dictionary No Add SDK global attributes, refer to rules here
setServiceName String No Set the service name, affects the service field data in Logs and RUM, default is df_rum_android
setAutoSync Boolean No Whether to automatically synchronize data to the server after collection, default is true. When set to false, use FTSdk.flushSyncData() to manage data synchronization yourself
setSyncPageSize Int No Set the number of entries per sync request, SyncPageSize.MINI 5 entries, SyncPageSize.MEDIUM 10 entries, SyncPageSize.LARGE 50 entries, default SyncPageSize.MEDIUM
setCustomSyncPageSize Enum No Set the number of entries per sync request, range [5,), note that the larger the number of entries, the more computing resources are consumed for data synchronization, default is 10 Note: Only configure one of setSyncPageSize and setCustomSyncPageSize
setSyncSleepTime Int No Set the intermittent time for synchronization, range [0,5000], default not set
enableDataIntegerCompatible Void No It is recommended to enable when coexisting with web data. This configuration handles web data type storage compatibility issues. Default enabled starting from version 1.6.9
setNeedTransformOldCache Boolean No Whether to maintain compatibility with old cache data from versions of ft-sdk below 1.6.0, default is false
setCompressIntakeRequests Boolean No Compress uploaded sync data using deflate, default disabled, supported by ft-sdk versions 1.6.3 and above
enableLimitWithDbSize Void No Enable limiting data size using db, default is 100MB, unit Byte, the larger the database, the greater the disk pressure, default not enabled.
Note: After enabling, FTLoggerConfig.setLogCacheLimitCount and FTRUMConfig.setRumCacheLimitCount will become ineffective. Supported by ft-sdk versions 1.6.6 and above
setEnableOkhttpRequestTag bool No Automatically add a unique ResourceID to Okhttp Requests, used for high-concurrency scenarios with identical requests. Supported by ft-sdk versions 1.6.10 and above, ft-plugin versions 1.3.5 and above
setProxy java.net.Proxy No Set Proxy for data network sync requests, only supports okhttp3, supported by ft-sdk versions 1.6.10 and above
setProxyAuthenticator okhttp3.Authenticator No Set Proxy for data sync network requests, only supports okhttp3, supported by ft-sdk versions 1.6.10 and above
setDns okhttp3.Dns No Support custom DNS resolution for data sync network requests, only supports okhttp3, supported by ft-sdk versions 1.6.10 and above
setDataModifier DataModifier No Modify individual fields. Supported by ft-sdk versions 1.6.11 and above, see example here
setLineDataModifier LineDataModifier No Modify individual data entries. Supported by ft-sdk versions 1.6.11 and above, see example here

RUM Configuration

FTSdk.initRUMWithConfig(
        new FTRUMConfig()
            .setRumAppId(RUM_APP_ID)
            .setEnableTraceUserView(true)
            .setDeviceMetricsMonitorType(DeviceMetricsMonitorType.ALL.getValue())
            .setEnableTraceUserAction(true)
            .setEnableTraceUserResource(true)
            .setEnableTrackAppUIBlock(true)
            .setEnableTrackAppCrash(true)
            .setEnableTrackAppANR(true)
            .setExtraMonitorTypeWithError(ErrorMonitorType.ALL.getValue())
);
FTSdk.initRUMWithConfig(
            FTRUMConfig()
                .setRumAppId(RUM_APP_ID)
                .setEnableTraceUserView(true)
                .setDeviceMetricsMonitorType(DeviceMetricsMonitorType.ALL.getValue())
                .setEnableTraceUserAction(true)
                .setEnableTraceUserResource(true)
                .setEnableTrackAppUIBlock(true)
                .setEnableTrackAppCrash(true)
                .setEnableTrackAppANR(true)
                .setExtraMonitorTypeWithError(ErrorMonitorType.ALL.getValue())
        )
Method Name Type Required Meaning
setRumAppId String Yes Set Rum AppId. Corresponds to setting RUM appid, which enables RUM collection functionality, method to obtain appid
setSamplingRate Float No Set the collection rate, range [0,1], 0 means no collection, 1 means full collection, default value is 1. Scope covers all View, Action, LongTask, Error data under the same session_id
setSessionErrorSampleRate Float No Set error collection rate, when the session is not sampled by setSamplingRate, if an error occurs during the session, data within 1 minute before the error can be collected, range [0,1], 0 means no collection, 1 means full collection, default value is 0. Scope covers all View, Action, LongTask, Error data under the same session_id. Supported by ft-sdk versions 1.6.11 and above
setEnableTrackAppCrash Boolean No Whether to report App crash logs, default is false, enabling this will display error stack data in error analysis.
Regarding deobfuscating content in crash logs.

ft-sdk versions 1.5.1 and above, through extraLogCatWithJavaCrash and extraLogCatWithNativeCrash settings whether logcat should be displayed for Java Crash and Native Crash
setExtraMonitorTypeWithError Array No Set auxiliary monitoring information, add additional monitoring data to Rum crash data, ErrorMonitorType.BATTERY for battery level, ErrorMonitorType.MEMORY for memory usage, ErrorMonitorType.CPU for CPU occupancy
setDeviceMetricsMonitorType Array No Set View monitoring information, add monitoring data during the View cycle, DeviceMetricsMonitorType.BATTERY monitors the highest current output for the current page, DeviceMetricsMonitorType.MEMORY monitors the application's memory usage, DeviceMetricsMonitorType.CPU monitors CPU spikes, DeviceMetricsMonitorType.FPS monitors screen frame rate. Monitoring frequency, DetectFrequency.DEFAULT 500 milliseconds, DetectFrequency.FREQUENT 100 milliseconds, DetectFrequency.RARE 1 second
setEnableTrackAppANR Boolean No Whether to enable ANR detection, default is false.

ft-sdk versions 1.5.1 and above, through extraLogCatWithANR settings whether logcat should be displayed for ANR
setEnableTrackAppUIBlock Boolean, long No Whether to enable UI freeze detection, default is false, ft-sdk versions 1.6.4 and above control detection time range [100,) in milliseconds, default is 1 second
setEnableTraceUserAction Boolean No Whether to automatically track user operations, currently only supports user start and click operations, default is false
setEnableTraceUserView Boolean No Whether to automatically track user page operations, default is false
setEnableTraceUserViewInFragment Boolean No Whether to automatically track Fragment type page data, default is false, ft-sdk versions 1.6.11 and above support
setEnableTraceUserResource Boolean No Whether to automatically trace user network requests, only supports Okhttp, default is false
setEnableResourceHostIP Boolean No Whether to collect the IP address of the requested target domain name. Scope: only affects the default collection when EnableTraceUserResource is true. For custom Resource collection, use FTResourceEventListener.FTFactory(true) to enable this function. Additionally, a single Okhttp caches IP for the same domain name, so the same OkhttpClient generates only once unless the IP connecting to the server changes
setResourceUrlHandler Callback No Set filtering conditions for Resources, default does not filter
setOkHttpEventListenerHandler Callback No ASM sets a global Okhttp EventListener, default is not set
setOkHttpResourceContentHandler Callback No ASM sets a global FTResourceInterceptor.ContentHandlerHelper, default is not set, supported by ft-sdk versions 1.6.7 and above, custom Resource
addGlobalContext Dictionary No Add custom tags for distinguishing data sources in user monitoring, if tracking functionality is needed, parameter key is track_id, value is any numerical value, refer to notes on adding rules here
setRumCacheLimitCount int No Local cache RUM limit count [10_000,), default is 100_000. Supported by ft-sdk versions 1.6.6 and above
setRumCacheDiscardStrategy RUMCacheDiscard No Set the discard rule for data when RUM reaches its limit, default is RUMCacheDiscard.DISCARD, DISCARD discards appended data, DISCARD_OLDEST discards old data, supported by ft-sdk versions 1.6.6 and above

Log Configuration

FTSdk.initLogWithConfig(new FTLoggerConfig()
    //.setEnableConsoleLog(true,"log prefix")
    .setEnableLinkRumData(true)
    .setEnableCustomLog(true)
    //.setLogLevelFilters(new Status[]{Status.CRITICAL, Status.ERROR})
    .setSamplingRate(0.8f));
   FTSdk.initLogWithConfig(
            FTLoggerConfig()
              //.setEnableConsoleLog(true,"log prefix")
                .setEnableLinkRumData(true)
                .setEnableCustomLog(true)
              //.setLogLevelFilters(arrayOf(Status.CRITICAL,Status.ERROR))
                .setSamplingRate(0.8f)
        )
Method Name Type Required Meaning
setSamplingRate Float No Set the collection rate, range [0,1], 0 means no collection, 1 means full collection, default value is 1.
setEnableConsoleLog Boolean No Whether to report console logs, log level correspondence
Log.v -> ok;
Log.i -> info;
Log.d -> debug;
Log.e -> error;
Log.w -> warning,
prefix is the control prefix filter parameter, default does not set filters. Note: Android console volume is large, to avoid affecting application performance and reducing unnecessary resource waste, it is recommended to use prefix to filter out valuable logs. ft-plugin versions 1.3.5 and above support capturing logs printed by Log.println
setEnableLinkRUMData Boolean No Whether to associate with RUM data, default is false
setEnableCustomLog Boolean No Whether to upload custom logs, default is false
setLogLevelFilters Array No Set log level filters, default not set
addGlobalContext Dictionary No Add log global attributes, refer to rules here
setLogCacheLimitCount Int No Local cache maximum log entry limit [1000,), larger logs mean greater disk cache pressure, default is 5000
setLogCacheDiscardStrategy LogCacheDiscard No Set log discard rule when log reaches limit, default is LogCacheDiscard.DISCARD, DISCARD discards appended data, DISCARD_OLDEST discards old data

Trace Configuration

FTSdk.initTraceWithConfig(new FTTraceConfig()
    .setSamplingRate(0.8f)
    .setEnableAutoTrace(true)
    .setEnableLinkRUMData(true));
   FTSdk.initTraceWithConfig(
            FTTraceConfig()
                .setSamplingRate(0.8f)
                .setEnableAutoTrace(true)
                .setEnableLinkRUMData(true)
        )
Method Name Type Required Meaning
setSamplingRate Float No Set the collection rate, range [0,1], 0 means no collection, 1 means full collection, default value is 1.
setTraceType TraceType No Set the trace type, default is DDTrace, currently supports Zipkin , Jaeger, DDTrace, Skywalking (8.0+), TraceParent (W3C), if choosing corresponding trace type when integrating OpenTelemetry, please check supported types and agent-related configurations
setEnableLinkRUMData Boolean No Whether to associate with RUM data, default is false
setEnableAutoTrace Boolean No Set whether to enable automatic http trace, currently only supports automatic tracing for OKhttp, default is false
setOkHttpTraceHeaderHandler Callback No ASM sets a global FTTraceInterceptor.HeaderHandler, default is not set, supported by ft-sdk versions 1.6.8 and above, reference example custom Trace

RUM User Data Tracking

In FTRUMConfig, configure enableTraceUserAction, enableTraceUserView, enableTraceUserResource,setEnableTrackAppUIBlock,setEnableTrackAppCrash and setEnableTrackAppANR to achieve automatic collection of data for Action, View, Resource, LongTask, Error. If you want to perform custom collection, you can use FTRUMGlobalManager to report data, as shown below:

Action

Usage Method

    /**
     * Add Action
     *
     * @param actionName action name
     * @param actionType action type
     * @param property   Additional attribute parameters (optional)
     */
    public void startAction(String actionName, String actionType, HashMap<String, Object> property)

     /**
     * Add Action, this kind of data cannot be associated with Error, Resource, LongTask data
     *
     * @param actionName action name
     * @param actionType action type
     * @param duration   Nanoseconds, duration (optional)
     * @param property Extended attributes (optional)
     */
    public void addAction(String actionName, String actionType, long duration, HashMap<String, Object> property) 
    /**
     * Add action
     *
     * @param actionName action name
     * @param actionType action type
     * @param property   Additional attribute parameters (optional)
     */
    fun startAction(actionName: String, actionType: String, property: HashMap<String, Any>)

    /**
     * Add Action
     *
     * @param actionName action name
     * @param actionType action type
     * @param duration   Nanoseconds, duration (optional)
     * @param property Extended attributes (optional)
     */
    fun addAction(actionName: String, actionType: String, duration: Long, property: HashMap<String, Any>)

startAction internally calculates timing, during which it tries to associate nearby occurring Resource, LongTask, Error data. There is a 100 ms frequent trigger protection, recommended for use with user operation type data. If there is a need for frequent calls, use addAction, this data will not conflict with startAction and will not associate with current Resource, LongTask, Error data

Code Example

// Scene 1
FTRUMGlobalManager.get().startAction("login", "action_type");

// Scene 2: Dynamic parameters
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().startAction("login", "action_type", map);


// Scene 1
FTRUMGlobalManager.get().addAction("login", "action_type");

// Scene 2: Dynamic parameters
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().addAction("login", "action_type", map);
// Scene 1
FTRUMGlobalManager.get().startAction("login", "action_type")

// Scene 2: Dynamic parameters
val map = HashMap<String,Any>()
map["ft_key"]="ft_value"
FTRUMGlobalManager.get().startAction("login","action_type",map)


// Scene 1
FTRUMGlobalManager.get().startAction("login", "action_type")

// Scene 2: Dynamic parameters
val map = HashMap<String,Any>()
map["ft_key"]="ft_value"
FTRUMGlobalManager.get().startAction("login","action_type",map)

View

Usage Method

    /**
     * View start
     *
     * @param viewName Current page name
     * @param property Additional attribute parameters (optional)
     */
    public void startView(String viewName, HashMap<String, Object> property)

    /**
     * View end
     *
     * @param property Additional attribute parameters (optional)
     */
    public void stopView(HashMap<String, Object> property)
     /**
     * View start
     *
     * @param viewName Current page name
     * @param property Additional attribute parameters (optional)
     */

    fun startView(viewName: String, property: HashMap<String, Any>)

     /**
     * View end
     *
     * @param property Additional attribute parameters (optional)
     */
    fun stopView(property: HashMap<String, Any>)

Code Example

@Override
protected void onResume() {
    super.onResume();

    // Scene 1
    FTRUMGlobalManager.get().startView("Current Page Name");

    // Scene 2: Dynamic parameters
    HashMap<String, Object> map = new HashMap<>();
    map.put("ft_key", "ft_value");
    map.put("ft_key_will_change", "ft_value");
    FTRUMGlobalManager.get().startView("Current Page Name", map);
}

@Override
protected void onPause() {
    super.onPause();

    // Scene 1
    FTRUMGlobalManager.get().stopView();

    // Scene 2 : Dynamic parameters
    HashMap<String, Object> map = new HashMap<>();
    map.put("ft_key_will_change", "ft_value_change"); //ft_key_will_change this value will be changed to ft_value_change when stopping the view
    FTRUMGlobalManager.get().startView("Current Page Name", map);
}
override fun onResume() {
     super.onResume()

     // Scene 1
     FTRUMGlobalManager.get().startView("Current Page Name")

     // Scene 2: Dynamic parameters
     val map = HashMap<String, Any>()
     map["ft_key"] = "ft_value"
     map["ft_key_will_change"] = "ft_value"
     FTRUMGlobalManager.get().startView("Current Page Name", map)

}

override fun onPause() {
     super.onPause()

     // Scene 1
     FTRUMGlobalManager.get().stopView()


     // Scene 2 : Dynamic parameters
     val map = HashMap<String, Any>()
     map["ft_key_will_change"] = "ft_value_change" //ft_key_will_change this value will be changed to ft_value_change when stopping the view
     FTRUMGlobalManager.get().startView("Current Page Name", map)

}

Error

Usage Method

    /**
     * Add error information
     *
     * @param log       Log
     * @param message   Message
     * @param errorType Error type, ErrorType
     * @param state     Program runtime state
     * @param dateline  Occurrence time, nanoseconds (optional)
     * @param property  Additional attributes (optional)
     */
    public void addError(String log, String message, long dateline, ErrorType errorType,
                         AppState state, HashMap<String, Object> property)
    /**
     * Add error information
     *
     * @param log       Log
     * @param message   Message
     * @param errorType Error type, String
     * @param state     Program runtime state
     * @param dateline  Occurrence time, nanoseconds (optional)
     * @param property  Additional attributes (optional)
     */
    public void addError(String log, String message, long dateline, String errorType,
                         AppState state, HashMap<String, Object> property)
     /**
     * Add error information
     *
     * @param log       Log
     * @param message   Message
     * @param errorType Error type, ErrorType
     * @param state     Program runtime state
     * @param dateline  Occurrence time, nanoseconds (optional)
     * @param property  Additional attributes (optional)
     */
    fun addError(log: String, message: String, dateline: Long, errorType: ErrorType,state: AppState, property: HashMap<String, Any>)

     /**
     * Add error information
     *
     * @param log       Log
     * @param message   Message
     * @param errorType Error type, String
     * @param state     Program runtime state
     * @param dateline  Occurrence time, nanoseconds (optional)
     * @param property  Additional attributes (optional)
     */
    fun addError(log: String, message: String, dateline: Long, errorType: String,state: AppState, property: HashMap<String, Any>)

Code Example

// Scene 1:
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN);

// Scene 2: Delay recording errors, this time is generally when the error occurred
FTRUMGlobalManager.get().addError("error log", "error msg", 16789000000000000000L, ErrorType.JAVA, AppState.RUN);

// Scene 3: Dynamic parameters
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN, map);
// Scene 1:
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN)

// Scene 2: Delay recording errors, this time is generally when the error occurred
FTRUMGlobalManager.get().addError("error log", "error msg", 16789000000000000000, ErrorType.JAVA, AppState.RUN)

// Scene 3: Dynamic parameters
val map = HashMap<String, Any>()
map["ft_key"] = "ft_value"
FTRUMGlobalManager.get().addError("error log", "error msg",ErrorType.JAVA,AppState.RUN,map)

LongTask

Usage Method

    /**
     * Add long task
     *
     * @param log      Log content
     * @param duration Duration, nanoseconds
     * @param property   Additional attribute parameters (optional)
     */
    public void addLongTask(String log, long duration, HashMap<String, Object> property)
    /**
     * Add long task
     *
     * @param log      Log content
     * @param duration Duration, nanoseconds
     * @param property   Additional attribute parameters (optional)
     */

    fun addLongTask(log: String, duration: Long, property: HashMap<String, Any>)

Code Example

// Scene 1
FTRUMGlobalManager.get().addLongTask("error log", 1000000L);

// Scene 2: Dynamic parameters
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().addLongTask("", 1000000L, map);
// Scene 1
FTRUMGlobalManager.get().addLongTask("error log",1000000L)

// Scene 2: Dynamic parameters
 val map = HashMap<String, Any>()
 map["ft_key"] = "ft_value"
 FTRUMGlobalManager.get().addLongTask("", 1000000L,map)

Resource

Usage Method

```java

/**
 * Resource start
 *
 * @param resourceId Resource Id
 * @param property   Additional attribute parameters (optional)
 */
public void startResource(String resourceId, HashMap<String, Object> property)

/**
 * Resource termination
 *
 * @param resourceId Resource Id
 * @param property   Additional attribute parameters (optional===

```java */ public void stopResource(final String resourceId, HashMap property)

    /**
     * Set network transmission content
     *
     * @param resourceId
     * @param params
     * @param netStatusBean
     */
    public void addResource(String resourceId, ResourceParams params, NetStatusBean netStatusBean)
=== "Kotlin"

```kotlin

/**
 * Resource start
 *
 * @param resourceId Resource Id (optional)
 */
fun startResource(resourceId: String, property: HashMap<String, Any>)


/**
 * Resource termination
 *
 * @param resourceId Resource Id
 * @param property   Additional attribute parameters (optional)
 */
fun stopResource(resourceId: String, property: HashMap<String, Any>)

/**
 * Set network transmission content
 *
 * @param resourceId
 * @param params
 * @param netStatusBean
 */
fun addResource(resourceId: String, params: ResourceParams, netStatusBean: NetStatusBean)

Code Example

// Scene 1
// Request starts
FTRUMGlobalManager.get().startResource("resourceId");

//...

// Request ends
FTRUMGlobalManager.get().stopResource("resourceId");

// Finally, after the request ends, send related data metrics for the request
ResourceParams params = new ResourceParams();
params.setUrl("https://guance.com");
params.setResponseContentType(response.header("Content-Type"));
params.setResponseConnection(response.header("Connection"));
params.setResponseContentEncoding(response.header("Content-Encoding"));
params.setResponseHeader(response.headers().toString());
params.setRequestHeader(request.headers().toString());
params.setResourceStatus(response.code());
params.setResourceMethod(request.method());

NetStatusBean bean = new NetStatusBean();
bean.setTcpStartTime(60000000);
//...

FTRUMGlobalManager.get().addResource("resourceId", params, bean);


// Scene 2: Using dynamic parameters
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
map.put("ft_key_will_change", "ft_value");

FTRUMGlobalManager.get().startResource("resourceId",map);

//...
HashMap<String, Object> map = new HashMap<>()
map.put("ft_key_will_change", "ft_value_change"); //ft_key_will_change this value will be changed to ft_value_change when stopping the resource
FTRUMGlobalManager.get().stopResource(uuid,map);
// Scene 1
//Request starts
FTRUMGlobalManager.get().startResource("resourceId")

//Request ends
FTRUMGlobalManager.get().stopResource("resourceId")

//Finally, after the request ends, send related data metrics for the request
val params = ResourceParams()
params.url = "https://guance.com"
params.responseContentType = response.header("Content-Type")
arams.responseConnection = response.header("Connection")
params.responseContentEncoding = response.header("Content-Encoding")
params.responseHeader = response.headers.toString()
params.requestHeader = request.headers.toString()
params.resourceStatus = response.code
params.resourceMethod = request.method

val bean = NetStatusBean()
bean.tcpStartTime = 60000000
//...
FTRUMGlobalManager.get().addResource("resourceId",params,bean)

// Scene 2: Using dynamic parameters
val map = hashMapOf<String, Any>(
        "ft_key" to "ft_value",
        "ft_key_will_change" to "ft_value"
)
FTRUMGlobalManager.get().startResource("resourceId", map)

//...
val map = hashMapOf<String, Any>(
        "ft_key_will_change" to "ft_value_change"
)
// ft_key_will_change this value will be changed to ft_value_change when stopping the resource

FTRUMGlobalManager.get().stopResource(uuid, map)
Method Name Required Meaning Explanation
NetStatusBean.fetchStartTime No Request start time
NetStatusBean.tcpStartTime No TCP connection time
NetStatusBean.tcpEndTime No TCP end time
NetStatusBean.dnsStartTime No DNS start time
NetStatusBean.dnsEndTime No DNS end time
NetStatusBean.responseStartTime No Response start time
NetStatusBean.responseEndTime No Response end time
NetStatusBean.sslStartTime No SSL start time
NetStatusBean.sslEndTime No SSL end time
NetStatusBean.property No Additional attributes
ResourceParams.url Yes URL address
ResourceParams.requestHeader No Request header parameters
ResourceParams.responseHeader No Response header parameters
ResourceParams.responseConnection No Response connection
ResourceParams.responseContentType No Response ContentType
ResourceParams.responseContentEncoding No Response ContentEncoding
ResourceParams.resourceMethod No Request method GET, POST etc.
ResourceParams.responseBody No Return body content
ResourceParams.property No Additional attributes

Logger Log Printing

Use FTLogger for custom log output, need to enable FTLoggerConfig.setEnableCustomLog(true).

Current log content is limited to 30 KB, exceeding characters will be truncated.

Usage Method

    /**
     * Store a single log data locally for synchronization
     *
     * @param content Log content
     * @param status  Log level, enum Status
     * @param property Additional attributes (optional)
     */
    public void logBackground(String content, Status status, HashMap<String, Object> property)

    /**
     * Store a single log data locally for synchronization
     *
     * @param content Log content
     * @param status  Log level, String
     * @param property Additional attributes (optional)
     */
    public void logBackground(String content, String status, HashMap<String, Object> property)

    /**
     * Store multiple log data locally for synchronization
     *
     * @param logDataList {@link LogData} list
     */
    public void logBackground(List<LogData> logDataList)
    /**
     * Store a single log data locally for synchronization
     *
     * @param content Log content
     * @param status  Log level
     * @param property Log attributes (optional)
     */
    fun logBackground(content: String, status: Status, property: HashMap<String, Any>)


    /**
     * Store a single log data locally for synchronization
     *
     * @param content Log content
     * @param status  Log level
     * @param property Log attributes (optional)
     */
    fun logBackground(content: String, status: String, property: HashMap<String, Any>)

    /**
     * Store multiple log data locally for synchronization
     *
     * @param logDataList Log data list
     */
    fun logBackground(logDataList: List<LogData>)

Log Levels

Method Name Meaning
Status.DEBUG Debug
Status.INFO Information
Status.WARNING Warning
Status.ERROR Error
Status.CRITICAL Critical
Status.OK Recovery

Code Example

// Upload a single log
FTLogger.getInstance().logBackground("test", Status.INFO);

// Pass parameters to HashMap
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTLogger.getInstance().logBackground("test", Status.INFO, map);

// Batch upload logs
List<LogData> logList = new ArrayList<>();
logList.add(new LogData("test", Status.INFO));
FTLogger.getInstance().logBackground(logList);
// Upload a single log
FTLogger.getInstance().logBackground("test", Status.INFO)

// Pass parameters to HashMap
val map = HashMap<String,Any>()
map["ft_key"]="ft_value"
FTLogger.getInstance().logBackground("test", Status.INFO,map)

// Batch upload logs
FTLogger.getInstance().logBackground(mutableListOf(LogData("test",Status.INFO)))

Configure FTTraceConfig to enable enableAutoTrace automatically adding trace data, or manually use FTTraceManager in Http requests with Propagation Header, as shown below:

String url = "https://request.url";
String uuid = "uuid";
// Get trace header parameters
Map<String, String> headers = FTTraceManager.get().getTraceHeader(uuid, url);

OkHttpClient client = new OkHttpClient.Builder().addInterceptor(chain -> {
    Request original = chain.request();
    Request.Builder requestBuilder = original.newBuilder();
    // Add trace header parameters in the request
    for (String key : headers.keySet()) {
        requestBuilder.header(key, headers.get(key));
    }
    Request request = requestBuilder.build();

    Response response = chain.proceed(request);

    if (response != null) {
        Map<String, String> requestHeaderMap = new HashMap<>();
        Map<String, String> responseHeaderMap = new HashMap<>();
        for (Pair<String, String> header : response.request().headers()) {
            requestHeaderMap.put(header.first, header.second);
        }
        for (Pair<String, String> header : response.headers()) {
            responseHeaderMap.put(header.first, header.second);
        }
    }

    return response;
}).build();

Request.Builder builder = new Request.Builder().url(url).method(RequestMethod.GET.name(), null);
client.newCall(builder.build()).execute();
val url = "https://request.url"
val uuid ="uuid"
// Get trace header parameters
val headers = FTTraceManager.get().getTraceHeader(uuid, url)

val client: OkHttpClient = OkHttpClient.Builder().addInterceptor { chain ->

                    val original = chain.request()
                    val requestBuilder = original.newBuilder()
                    // Add trace header parameters in the request
                    for (key in headers.keys) {
                        requestBuilder.header(key!!, headers[key]!!)
                    }
                    val request = requestBuilder.build()

                    response = chain.proceed(request)

                    if (response != null) {
                        val requestHeaderMap = HashMap<String, String>()
                        val responseHeaderMap = HashMap<String, String>()
                        request.headers.forEach {
                            requestHeaderMap[it.first] = it.second
                        }
                        response!!.headers.forEach {
                            responseHeaderMap[it.first] = it.second

                        }

                    }

                    response!!
                }.build()

 val builder: Request.Builder = Request.Builder().url(url).method(RequestMethod.GET.name, null)
client.newCall(builder.build()).execute()

Custom Resource and TraceHeader via OKHttp Interceptor

With FTRUMConfig's enableTraceUserResource and FTTraceConfig's enableAutoTrace configurations enabled simultaneously, prioritize loading the custom Interceptor configuration,

For ft-sdk < 1.4.1, need to disable FTRUMConfig's enableTraceUserResource and FTTraceConfig's enableAutoTrace. ft-sdk > 1.6.7 supports associating custom Trace Headers with RUM data

 new OkHttpClient.Builder()
        .addInterceptor(new FTTraceInterceptor(new FTTraceInterceptor.HeaderHandler() {
               @Override
               public HashMap<String, String> getTraceHeader(Request request) {
                   HashMap<String, String> map = new HashMap<>();
                   map.put("custom_header","custom_value");
                   return map;
              }

             // 1.6.7 version and above support
              @Override
              public String getSpanID() {
                return "span_id";
             }
            // 1.6.7 version and above support
             @Override
             public String getTraceID() {
                return "trace_id";
             }
        }))
       .addInterceptor(new FTResourceInterceptor(new FTResourceInterceptor.ContentHandlerHelper() {
           @Override
           public void onRequest(Request request, HashMap<String, Object> extraData) {
               String contentType = request.header("Content-Type");
               extraData.put("df_request_header", request.headers().toString());
               if ("application/json".equals(contentType) ||
                       "application/x-www-form-urlencoded".equals(contentType) ||
                       "application/xml".equals(contentType)) {
                   extraData.put("df_request_body", request.body());


           @Override
           public void onResponse(Response response, HashMap<String, Object> extraData) throws IOException {
               String contentType = response.header("Content-Type");
               extraData.put("df_response_header", response.headers().toString());
               if ("application/json".equals(contentType) ||
                       "application/xml".equals(contentType)) {
                   // Copy part of the body to avoid consuming large data
                   ResponseBody body = response.peekBody(33554432);
                   extraData.put("df_response_body", body.string());
               }

           @Override
           public void onException(Exception e, HashMap<String, Object> extraData)
           }
       }))
       .eventListenerFactory(new FTResourceEventListener.FTFactory())
       .build();
OkHttpClient.Builder()
    .addInterceptor(FTTraceInterceptor(object : FTTraceInterceptor.HeaderHandler {
        override fun getTraceHeader(request: Request): HashMap<String, String> {
            val map = HashMap<String, String>()
            map["custom_header"] = "custom_value"
            return map
        }
    }))
    .addInterceptor(FTResourceInterceptor(object : FTResourceInterceptor.ContentHandlerHelper {
        override fun onRequest(request: Request, extraData: HashMap<String, Any>) {
            val contentType = request.header("Content-Type")
            extraData["df_request_header"] = request.headers().toString()
            if ("application/json" == contentType ||
                "application/x-www-form-urlencoded" == contentType ||
                "application/xml" == contentType) {
                extraData["df_request_body"] = request.body()
            }
        }

        override fun onResponse(response: Response, extraData: HashMap<String, Any>) {
            val contentType = response.header("Content-Type")
            extraData["df_response_header"] = response.headers().toString()
            if ("application/json" == contentType ||
                "application/xml" == contentType) {
                // Copy part of the response body to avoid consuming large data
                val body = response.peekBody(33554432)
                extraData["df_response_body"] = body.string()
            }
        }

        override fun onException(e: Exception, extraData: HashMap<String, Any>) {
            // Handle exception case
        }
    }))
    .eventListenerFactory(FTResourceEventListener.FTFactory())
    .build()

Adding ResourceID to OKhttp Requests

Add UUID to Okhttp Requests. If there are high-frequency concurrent requests, it's recommended to enable this feature. For ft-plugin versions 1.3.5 and above, ft-sdk versions 1.6.10 and above, enabling FTSDKConfig.setEnableOkhttpRequestTag(true) can automatically add ResourceID to Requests.

new Request.Builder()
//...
.tag(ResourceID.class, new ResourceID())//Add unique
Request.Builder()
    // ...
    .tag(ResourceID::class.java, ResourceID()) 

Binding and Unbinding User Data

Use FTSdk to bind and unbind user data

Usage Method

   /**
     * Bind user information
     *
     * @param id
     */
    public static void bindRumUserData(@NonNull String id)

    /**
     * Bind user information
     */
    public static void bindRumUserData(@NonNull UserData data)

    /**
     * Unbind user information
     */
    public static void unbindRumUserData()
/**
     * Bind user information
     *
     * @param id User ID
     */
    fun bindRumUserData(id: String)

    /**
     * Bind user information
     *
     * @param data User information
     */
    fun bindRumUserData(data: UserData)

    /**
     * Unbind user information
     */
    fun unbindRumUserData()

UserData

Method Name Meaning Required Note
setId Set user ID No
setName Set username No
setEmail Set email No
setExts Set user extensions No Refer to rules here

Code Example

// You can call this method after the user successfully logs in to bind user information
FTSdk.bindRumUserData("001");

UserData userData = new UserData();
userData.setName("test.user");
userData.setId("test.id");
userData.setEmail("test@mail.com");
Map<String, String> extMap = new HashMap<>();
extMap.put("ft_key", "ft_value");
userData.setExts(extMap);
FTSdk.bindRumUserData(userData);

// You can call this method after the user logs out to unbind user information
FTSdk.unbindRumUserData();
// You can call this method after the user successfully logs in to bind user information
FTSdk.bindRumUserData("001")

// Bind more user data
val userData = UserData()
userData.name = "test.user"
userData.id = "test.id"
userData.email = "test@mail.com"
val extMap = HashMap<String, String>()
extMap["ft_key"] = "ft_value"
userData.setExts(extMap)
FTSdk.bindRumUserData(userData)

// You can call this method after the user logs out to unbind user information
FTSdk.unbindRumUserData()

Closing SDK

Use FTSdk to close the SDK, if you dynamically change the SDK configuration, you need to close it first to avoid generating incorrect data

FTSdk.shutDown();
FTSdk.shutDown()

Clearing SDK Cache Data

Use FTSdk to clear unsent cache data

FTSdk.clearAllData();
FTSdk.clearAllData()

Actively Synchronizing Data

Use FTSdk to actively synchronize data.

When FTSdk.setAutoSync(false), you need to manually synchronize data

FTSdk.flushSyncData()
FTSdk.flushSyncData()

Dynamically Enable or Disable Access to AndroidID

Use FTSdk to set whether to obtain Android ID within the SDK

// Enable access to Android ID
FTSdk.setEnableAccessAndroidID(true);

// Disable access to Android ID
FTSdk.setEnableAccessAndroidID(false);
// Enable access to Android ID
FTSdk.setEnableAccessAndroidID(true)

// Disable access to Android ID
FTSdk.setEnableAccessAndroidID(false)

Adding Custom Tags

Use FTSdk to dynamically add tags during SDK runtime

Usage Method

/**
 * Dynamically set global tag
 * @param globalContext
 */
public static void appendGlobalContext(HashMap<String,Object> globalContext)

/**
 * Dynamically set RUM global tag
 * @param globalContext
 */
public static void appendRUMGlobalContext(HashMap<String,Object> globalContext)

/**
 * Dynamically set log global tag
 * @param globalContext
 */
public static void appendLogGlobalContext(HashMap<String,Object> globalContext)
/**
 * Dynamically set global tag
 * @param globalContext
 */
fun appendGlobalContext(globalContext: HashMap<String, Any>) 

/**
 * Dynamically set RUM global tag
 * @param globalContext
 */
fun appendRUMGlobalContext(globalContext: HashMap<String, Any>) 

/**
 * Dynamically set log global tag
 * @param globalContext
 */
fun appendLogGlobalContext(globalContext: HashMap<String, Any>)

Code Example

HashMap<String, Object> globalContext = new HashMap<>();
globalContext.put("global_key", "global_value");
FTSdk.appendGlobalContext(globalContext);

HashMap<String, Object> rumGlobalContext = new HashMap<>();
rumGlobalContext.put("rum_key", "rum_value");
FTSdk.appendRUMGlobalContext(rumGlobalContext);

HashMap<String, Object> logGlobalContext = new HashMap<>();
logGlobalContext.put("log_key", "log_value");
FTSdk.appendLogGlobalContext(logGlobalContext);
val globalContext = hashMapOf<String, Any>(
    "global_key" to "global_value"
)
FTSdk.appendGlobalContext(globalContext)

val rumGlobalContext = hashMapOf<String, Any>(
    "rum_key" to "rum_value"
)
FTSdk.appendRUMGlobalContext(rumGlobalContext)

val logGlobalContext = hashMapOf<String, Any>(
    "log_key" to "log_value"
)
FTSdk.appendLogGlobalContext(logGlobalContext)

Uploading Symbol Files

Plugin Upload (Only Supported by datakit [Local Deployment])

ft-plugin version needs to be 1.3.0 or higher to support the latest symbol file upload rules, supporting productFlavor multi-version management. The plugin will execute uploading symbol files after gradle task assembleRelease. Detailed configuration can be referenced from SDK Demo

FTExt {
    //...
    autoUploadMap = true                 // Upload mapping.txt file, default is false
    autoUploadNativeDebugSymbol = true   // Upload c/c++ symbol so file, default is false
    datakitUrl = 'https://datakit.url'  // datakit reporting address, generateSourceMapOnly=true does not require configuration
    datawayToken = 'dataway_token'      // Space token, generateSourceMapOnly=true does not require configuration
    appId = "appid_xxxxx"               // appid, generateSourceMapOnly=true does not require configuration
    env = 'common'                      // Environment, generateSourceMapOnly=true does not require configuration
    // Native so specified path, just specify the upper directory of abi files
    // |-stripped_native_libs
    //      |-release
    //          |-out
    //          |-lib
    //              |-arm64-v8a
    //              |-armeabi-v7a
    //              |-...
    //nativeLibPath='/build/intermediates/merged_native_libs/release/out/lib'
    generateSourceMapOnly = false // Only generate sourcemap, default is false, path example: /app/build/tmp/ft{flavor}SourceMapMerge-release.zip, ft-plugin:1.3.4 and above versions support

    prodFlavors { // prodFlavors configuration overrides outer settings
        prodTest {
            autoUploadMap = false
            autoUploadNativeDebugSymbol = false
            datakitUrl = 'https://datakit.url'
            datawayToken = 'dataway_token'
            appId = "appid_prodTest"
            env = "gray"
        }
        prodPublish {
            autoUploadMap = true
            autoUploadNativeDebugSymbol = true
            datakitUrl = 'https://datakit.url'
            datawayToken = 'dataway_token'
            appId = "appid_prodPublish"
            env = "prod"
        }
    }
}

Manual Upload

Use plugin to enable generateSourceMapOnly = true, execute gradle task assembleRelease to generate, or pack into a zip file manually, then upload it to datakit or through Guance Studio, recommend using zip command-line for packing to avoid including some system hidden files in the zip package. Symbol upload reference sourcemap upload

Unity Native Symbol files please refer to official documentation

Permission Configuration Description

Name Required Usage Reason
READ_PHONE_STATE No To obtain cellular device information

For details on how to request dynamic permissions, refer to Android Developer

Plugin AOP Ignore

Through adding @IngoreAOP in Plugin AOP coverage methods to ignore ASM insertion. If batch ignoring is needed, use ft-plugin FTExt ignorePackages for ignoring.

View.setOnClickListener(new View.OnClickListener() {
            @Override
            @IgnoreAOP
            public void onClick(View v) {

            }
        }
View.setOnClickListener @IngoreAOP{

        }

WebView Data Monitoring

For WebView data monitoring, integrate the Web Monitoring SDK on the accessed page.

Data Masking

If full masking of fields is desired, it is recommended to use setDataModifier for better performance. If detailed rule replacement is needed, it is recommended to use setLineDataModifier

Please do not use complex or highly delayed methods in callback methods, as this would greatly affect SDK data writing performance

FTSdk.install(
    FTSDKConfig.builder("xxx")
    .setDataModifier(new DataModifier() {
    /**
     * Modify a specific field
     * 
     * @param key   Field name
     * @param value Field value (original value)
     * @return New value, return null indicates no modification
     */
    @Override
    public Object modify(String key, Object value) {
        if (key.equals("device_uuid")) {
            return "xxx";
        }
        return null;
    }
    }).setLineDataModifier(new LineDataModifier() {
    /***
     * Modify a specific line of data
     * 
     * @param measurement Data metric type view, action, resource,
     *                    longtask, error, df_rum_android_log
     * @param data Original data key-value pairs
     * @return Modified key-value pairs, (return null or empty map means no modification)
     */
    @Override
    public Map<String, Object> modify(String measurement, HashMap<String, Object> data) {
        if(measurement.equals("view")){
            HashMap<String,Object> changeMap = new HashMap<String,Object>();
            changeMap.put("view_url", "xxx");
        }
        return null;
    }
})      
FTSdk.install(
    FTSDKConfig.builder("xxx")
    .setDataModifier(object : DataModifier {
    /**
     * Modify a specific field
     * 
     * @param key   Field name
     * @param value Field value (original value)
     * @return New value, return null indicates no modification
     */
    override fun modify(key: String, value: Any?): Any? {
        return if (key == "device_uuid") {
            "xxx" // Replace with custom device_uuid
        } else {
            null
        }
    }
})
// Batch modify certain fields in a single data entry
.setLineDataModifier(object : LineDataModifier {
    /**
     * Modify a specific line of data
     * 
     * @param measurement Data metric type view, action, resource,
     *        longtask, error, df_rum_android_log
     * @param data Original data key-value pairs
     * @return Modified key-value pairs, (return null or empty map means no modification)
     */
    override fun modify(
        measurement: String,
        data: HashMap<String, Any>
    ): Map<String, Any>? {
        return if (measurement == "view") {
            hashMapOf("view_url" to "xxx") 
        } else {
            null
        }
    }

Custom Tag Usage Example

Compilation Configuration Method

  1. Create multiple productFlavors in build.gradle to distinguish tags
android{
    //…
    productFlavors {
        prodTest {
            buildConfigField "String", "CUSTOM_VALUE", "\"Custom Test Value\""
            //…
        }
        prodPublish {
            buildConfigField "String", "CUSTOM_VALUE", "\"Custom Publish Value\""
            //…
        }
    }
}
  1. Add corresponding BuildConfig constants in RUM configuration
FTSdk.initRUMWithConfig(
        new FTRUMConfig()
            .addGlobalContext(CUSTOM_STATIC_TAG, BuildConfig.CUSTOM_VALUE)
            //... Add other configurations
);
FTSdk.initRUMWithConfig(
            FTRUMConfig()
                .addGlobalContext(CUSTOM_STATIC_TAG, BuildConfig.CUSTOM_VALUE)
                //… Add other configurations
        )

Runtime Read/Write File Method

  1. Through storing file-type data, such as SharedPreferences, configure and use SDK, add code for obtaining tag data in the configuration section.
SharedPreferences sp = context.getSharedPreferences(SP_STORE_DATA, MODE_PRIVATE);
String customDynamicValue = sp.getString(CUSTOM_DYNAMIC_TAG, "not set");

// Configure RUM
FTSdk.initRUMWithConfig(
     new FTRUMConfig().addGlobalContext(CUSTOM_DYNAMIC_TAG, customDynamicValue)
     //… Add other configurations
);
val sp = context.getSharedPreferences(SP_STORE_DATA, MODE_PRIVATE)
val customDynamicValue = sp.getString(CUSTOM_DYNAMIC_TAG, "not set")

// Configure RUM
FTSdk.initRUMWithConfig(
     FTRUMConfig().addGlobalContext(CUSTOM_DYNAMIC_TAG, customDynamicValue!!)
     //… Add other configurations
)
  1. Add methods to change file data at any location.
public void setDynamicParams(Context context, String value) {
    SharedPreferences sp = context.getSharedPreferences(SP_STORE_DATA, MODE_PRIVATE);
    sp.edit().putString(CUSTOM_DYNAMIC_TAG, value).apply();
}
fun setDynamicParams(context: Context, value: String) {
            val sp = context.getSharedPreferences(SP_STORE_DATA, MODE_PRIVATE)
            sp.edit().putString(CUSTOM_DYNAMIC_TAG, value).apply()

        }
  1. Restart the application finally, see details SDK Demo

Adding Tags During SDK Runtime

After SDK initialization, use FTSdk.appendGlobalContext(globalContext), FTSdk.appendRUMGlobalContext(globalContext), FTSdk.appendLogGlobalContext(globalContext) to dynamically add tags. After setting, it takes effect immediately. Subsequently reported RUM or Log data will automatically include tag data. This usage method suits scenarios where data needs to be obtained later, for instance, tag data requiring network requests.

//SDK Initialization pseudocode, set tags after obtaining parameters from the network

FTSdk.init() 

getInfoFromNet(info){
    HashMap<String, Object> globalContext = new HashMap<>();
    globalContext.put("delay_key", info.value);
    FTSdk.appendGlobalContext(globalContext)
}

Common Issues

Add Local Variables to Avoid Conflicting Fields

To avoid conflicts between custom fields and SDK data, it is recommended to prefix labels with the project abbreviation, such as df_tag_name. The key values used in the project can be queried in the source code. When global variables in SDK appear identical to those in RUM and Log, RUM and Log will override the global variables in the SDK.

SDK Compatibility

Adapting to Market Privacy Reviews

Privacy Statement

Go to view

Method 1: SDK AndroidID Configuration

The SDK uses Android ID to better associate data from the same user. If you need to list your app in an app market, you can handle market privacy reviews as follows.

public class DemoApplication extends Application {
    @Override
    public void onCreate() {
        // Set setEnableAccessAndroidID to false during initialization
        FTSDKConfig config = new FTSDKConfig.Builder(DATAKIT_URL)
                .setEnableAccessAndroidID(false)
                .build();
        FTSdk.install(config);

        // ...
    }
}

// Enable after user agrees to the privacy agreement
FTSdk.setEnableAccessAndroidID(true);
class DemoApplication : Application() {
    override fun onCreate() {

        // Set setEnableAccessAndroidID to false during initialization
        val config = FTSDKConfig
            .builder(DATAKIT_URL)
            . setEnableAccessAndroidID(false)

        FTSdk.install(config)

        //...
    }
}

// Enable after user agrees to the privacy agreement
FTSdk.setEnableAccessAndroidID(true);

Method 2: Delayed Initialization of SDK

If you need to delay loading the SDK in the application, it is recommended to initialize it as follows.

// Application
public class DemoApplication extends Application {
    @Override
    public void onCreate() {
        // If already agreed to the protocol, initialize in Application
        if(agreeProtocol){
            FTSdk.init(); //SDK Initialization pseudocode
        }
    }
}

// Privacy Declaration Activity Page
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Not read privacy declaration
        if ( notReadProtocol ) {
            // Show privacy declaration popup
            showProtocolView();

            // If agreed to privacy declaration
            if( agreeProtocol ){
                FTSdk.init(); //SDK Initialization pseudocode
            }
        }
    }
}
// Application  
class DemoApplication : Application() {
    override fun onCreate() {
        // If already agreed to the protocol, initialize in Application
        if (agreeProtocol) {
            FTSdk.init() //SDK Initialization pseudocode
        }
    }
}

// Privacy Declaration Activity Page
class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // Not read privacy declaration
        if (notReadProtocol) {
            // Show privacy declaration popup
            showProtocolView()

            // If agreed to privacy declaration
            if (agreeProtocol) {
                FTSdk.init() //SDK Initialization pseudocode
            }
        }
    }
}

Third-party Frameworks

flutter, react-native, uni-app, unity can adopt similar native Android delayed initialization methods to handle app market privacy reviews.

Jetpack Compose Support

Currently, automatic collection of compose component-generated pages is temporarily unsupported. However, manual Action and View custom interfaces can track click events and page transition events. Reference here

How to Integrate SDK Without Using ft-plugin

Guance uses Androig Grale Plugin Transformation to achieve code injection, thereby implementing automatic data collection. However, due to compatibility issues, there may be cases where ft-plugin cannot be used. Affected includes RUM Action, Resource, and android.util.Log, Java and Kotlin println automatic capture of console logs, and automatic upload of symbol files.

Currently, for such situations, we have another integration solution. The solution is as follows:

// Application
@Override
public void onCreate() {
    super.onCreate();
    // Needs to be called before SDK initialization
    FTAutoTrack.startApp(null);
    // Set SDK configuration
    setSDK(this);
}
  //Application
    override fun onCreate() {
        super.onCreate()
    // Needs to be called before SDK initialization
        FTAutoTrack.startApp(null)
        // Set SDK configuration
        setSDK(this)

    }
  • Need to add key events manually where triggered, for example, Button onClick event, reference source code example ManualActivity.kt:
view.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        FTRUMGlobalManager.get().startAction("[action button]", "click");
    }
});
    view.setOnClickListener{
        FTRUMGlobalManager.get().startAction("[action button]", "click")
    }
  • OKhttp integrates Resource and Trace through addInterceptor, eventListener, reference example below, source code example ManualActivity.kt:
OkHttpClient.Builder builder = new OkHttpClient.Builder()
    .addInterceptor(new FTTraceInterceptor())
    .addInterceptor(new FTResourceInterceptor())
    .eventListenerFactory(new FTResourceEventListener.FTFactory());
//.eventListenerFactory(new FTResourceEventListener.FTFactory(true));
OkHttpClient client = builder.build();
val builder = OkHttpClient.Builder()
    .addInterceptor(FTTraceInterceptor())
    .addInterceptor(FTResourceInterceptor())
    .eventListenerFactory(FTResourceEventListener.FTFactory())
//.eventListenerFactory(new FTResourceEventListener.FTFactory(true))
val client = builder.build()
  • For other network frameworks, you need to implement using FTRUMGlobalManager with startResource, stopResource, addResource, and FTTraceManager.getTraceHeader. Specific implementation methods can be referenced in the source code example ManualActivity.kt

  • WebView data collection configuration

FTAutoTrack.setUpWebView(webview)
// Configure before performing loadUrl operation
FTAutoTrack.setUpWebView(webview)
// Configure before performing loadUrl operation

Feedback

Is this page helpful? ×