Android 应用接入¶
通过收集 Android 应用的指标数据,以可视化方式分析应用性能。
前置条件¶
注意
若已开通 RUM Headless 服务,前置条件已自动配置,可直接接入应用。
- 安装 DataKit;
- 配置 RUM 采集器;
- DataKit 配置为公网可访问,并且安装 IP 地理信息库。
应用接入¶
- 进入用户访问监测 > 新建应用 > Android;
- 输入应用名称;
- 输入应用 ID;
-
选择应用接入方式:
- 公网 DataWay:直接接收 RUM 数据,无需安装 DataKit 采集器。
- 本地环境部署:满足前置条件后接收 RUM 数据。
安装¶
源码地址:https://github.com/GuanceCloud/datakit-android
Demo:https://github.com/GuanceDemo/guance-app-demo
Gradle 配置¶
- 在项目的根目录的
build.gradle
文件中添加SDK
的远程仓库地址
buildscript {
//...
repositories {
//...
//添加 SDK 的远程仓库地址
maven {
url 'https://mvnrepo.jiagouyun.com/repository/maven-releases'
}
}
dependencies {
//...
//添加 Plugin 的插件,依赖 AGP 7.4.2 以上,Gradle 7.2.0 以上
classpath 'com.cloudcare.ft.mobile.sdk.tracker.plugin:ft-plugin:[latest_version]'
// AGP 7.4.2 以下版本,请使用 ft-plugin-legacy
//classpath 'com.cloudcare.ft.mobile.sdk.tracker.plugin:ft-plugin-legacy:[latest_version]'
}
}
allprojects {
repositories {
//...
//添加 SDK 的远程仓库地址
maven {
url 'https://mvnrepo.jiagouyun.com/repository/maven-releases'
}
}
}
//setting.gradle
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
//添加 SDK 的远程仓库地址
maven {
url('https://mvnrepo.jiagouyun.com/repository/maven-releases')
}
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
//添加 SDK 的远程仓库地址
maven {
url('https://mvnrepo.jiagouyun.com/repository/maven-releases')
}
}
}
//build.gradle
plugins{
//添加 Plugin 的插件,依赖 AGP 7.4.2 以上,Gradle 7.2.0 以上
id 'com.cloudcare.ft.mobile.sdk.tracker.plugin' version '[lastest_version]' apply false
// AGP 7.4.2 以下版本,请使用 ft-plugin-legacy
//id 'com.cloudcare.ft.mobile.sdk.tracker.plugin.legacy' version '[lastest_version]' apply false
//plugin 配置作用:
// 1. 自动配置采集:App 启动、OkHttp 请求、WebView 活动、Activity 跳转
// 2. 自动采集 View 点击事件,Console Logcat
// 3. 自动采集 Console Logcat
}
- 在项目主模块
app
的build.gradle
文件中添加SDK
的依赖及Plugin
的使用。
dependencies {
//添加 SDK 的依赖
implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-sdk:[latest_version]'
//捕获 native 层崩溃信息的依赖,需要配合 ft-sdk 使用不能单独使用
implementation 'com.cloudcare.ft.mobile.sdk.tracker.agent:ft-native:[latest_version]'
// json 序列化
implementation 'com.google.code.gson:gson:2.8.+'
//可选,如果需要自动采网络请求和自动开启链路必须添加
implementation 'com.squareup.okhttp3:okhttp:4.+'
}
//应用插件
apply plugin: 'ft-plugin' //如果使用旧版本兼容 ft-plugin-legacy,无需更改
//配置插件使用参数 (可选)
FTExt {
//是否显示 Plugin 日志,默认为 false
//showLog = true
//设置 ASM 版本,支持 asm7 - asm9,默认 asm9
//asmVersion='asm7'
//ASM 忽略路径配置,路径中 . 和 / 等效
//ignorePackages=['com.ft','com/ft']
}
android{
//...省略部分代码
defaultConfig {
//...省略部分代码
ndk {
//当使用 ft-native 捕获 native 层的崩溃信息时,应该根据应用适配的不同的平台
//来选择支持的 abi 架构,目前 ft-native 中包含的 abi 架构有 'arm64-v8a',
// 'armeabi-v7a', 'x86', 'x86_64'
abiFilters 'armeabi-v7a'
}
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
}
最新的版本请看上方的 ft-sdk 、ft-plugin 、ft-native 的版本名
Application 配置¶
最佳的 SDK 初始化位置是在 Application
的 onCreate
方法中,如果您的应用尚未创建 Application
,您需要创建一个,并在 AndroidManifest.xml
中声明,示例请参考这里。
R8 / Proguard 混淆配置¶
如果需要 android.buildTypes
设置 minifyEnabled
= true ,需要开启如下配置:
-dontwarn com.ft.sdk.**
### ft-sdk 库
-keep class com.ft.sdk.**{*;}
### ft-native 库
-keep class ftnative.*{*;}
### 防止 Action 获取时 action_name 中类名被混淆###
-keepnames class * extends android.view.View
-keepnames class * extends android.view.MenuItem
SDK 初始化¶
基础配置¶
public class DemoApplication extends Application {
@Override
public void onCreate() {
//本地环境部署、Datakit 部署
FTSDKConfig config = FTSDKConfig.builder(datakitUrl);
//使用公网 DataWay
FTSDKConfig config = FTSDKConfig.builder(datawayUrl, clientToken);
//...
//config.setDebug(true); //debug 模式
config.setCompressIntakeRequests(true); //上报数据压缩
FTSdk.install(config);
}
}
class DemoApplication : Application() {
override fun onCreate() {
//本地环境部署、Datakit 部署
val config = FTSDKConfig.builder(datakitUrl)
//使用公网 DataWay
val config = FTSDKConfig.builder(datawayUrl, clientToken)
//...
//config.setDebug(true); //debug 模式
config.setCompressIntakeRequests(true) //上报数据压缩
FTSdk.install(config)
}
}
方法名 | 类型 | 必须 | 含义 |
---|---|---|---|
datakitUrl | String | 是 | 本地环境部署(Datakit)上报 URL 地址,例子:http://10.0.0.1:9529,端口默认 9529,安装 SDK 设备需能访问该地址。注意:datakitUrl 和 datawayUrl 配置两者二选一 |
datawayUrl | String | 是 | 公网 Dataway 上报 URL 地址,从 [用户访问监测] 应用中获取,例子:https://open.dataway.url,安装 SDK 设备需能访问这地址。注意:datakitUrl 和 datawayUrl 配置两者二选一 |
clientToken | String | 是 | 认证 token,需要与 datawayUrl 同时配置 |
setDebug | Boolean | 否 | 是否开启调试模式 。默认为 false ,开启后方可打印 SDK 运行日志 |
setEnv | EnvType | 否 | 设置采集环境, 默认为 EnvType.PROD , |
setEnv | String | 否 | 设置采集环境,默认为 prod 。注意: String 或 EnvType 类型只需配置一个 |
setOnlySupportMainProcess | Boolean | 否 | 是否只支持在主进程运行,默认为 true ,如果需要在其他进程中执行需要将该字段设置为 false |
setEnableAccessAndroidID | Boolean | 否 | 开启获取 Android ID ,默认为 true ,设置为 false ,则 device_uuid 字段数据将不进行采集,市场隐私审核相关查看这里 |
addGlobalContext | Dictionary | 否 | 添加 SDK 全局属性,添加规则请查阅此处 |
setServiceName | String | 否 | 设置服务名,影响 Log 和 RUM 中 service 字段数据,默认为 df_rum_android |
setAutoSync | Boolean | 否 | 是否在采集数据后自动同步到服务器,默认为 true 。当为 false 时使用 FTSdk.flushSyncData() 自行管理数据同步 |
setSyncPageSize | Int | 否 | 设置同步请求条目数,SyncPageSize.MINI 5 条,SyncPageSize.MEDIUM 10 条,SyncPageSize.LARGE 50 条,默认 SyncPageSize.MEDIUM |
setCustomSyncPageSize | Enum | 否 | 设置同步请求条目数,范围 [5,),注意请求条目数越大,代表数据同步占用更大的计算资源,默认为 10 注意:setSyncPageSize 和 setCustomSyncPageSize 只需要配置一个 |
setSyncSleepTime | Int | 否 | 设置同步间歇时间,范围 [0,5000],默认不设置 |
enableDataIntegerCompatible | Void | 否 | 需要与 web 数据共存情况下,建议开启。此配置用于处理 web 数据类型存储兼容问题。1.6.9 版本默认开启 |
setNeedTransformOldCache | Boolean | 否 | 是否需要兼容同步 ft-sdk 1.6.0 以下的版本的旧缓存数据,默认为 false |
setCompressIntakeRequests | Boolean | 否 | 对上传同步数据进行 deflate 压缩,默认关闭,ft-sdk 1.6.3 以上版本支持这个方法 |
enableLimitWithDbSize | Void | 否 | 开启使用 db 限制数据大小,默认 100MB,单位 Byte,数据库越大,磁盘压力越大,默认不开启。 注意: 开启之后 FTLoggerConfig.setLogCacheLimitCount 及 FTRUMConfig.setRumCacheLimitCount 将失效。ft-sdk 1.6.6 以上版本支持这个方法 |
setEnableOkhttpRequestTag | bool | 否 | 对 Okhttp Request 自动添加唯一 ResourceID ,用于相同请求高并发的场景。ft-sdk 1.6.10 以上支持,ft-plugin 1.3.5 以上支持 |
setProxy | java.net.Proxy | 否 | 对数据网络同步请求进行 Proxy 代理设置, 仅支持 okhttp3, ft-sdk 1.6.10 以上支持 |
setProxyAuthenticator | okhttp3.Authenticator | 否 | 对数据同步网络请求进行 Proxy 代理设置,仅支持 okhttp3, ft-sdk 1.6.10 以上支持 |
setDns | okhttp3.Dns | 否 | 数据同步网络请求支持自定义 Dns 对域名解析进来自定义处理,仅支持 okhttp3, ft-sdk 1.6.10 以上支持 |
setDataModifier | DataModifier | 否 | 对单个字段进行更改。 ft-sdk 1.6.11 以上支持,使用示例请看这里 |
setLineDataModifier | LineDataModifier | 否 | 对单条数据数据进行更改。 ft-sdk 1.6.11 以上支持,使用示例请看这里 |
RUM 配置¶
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())
)
方法名 | 类型 | 必须 | 含义 |
---|---|---|---|
setRumAppId | String | 是 | 设置Rum AppId 。对应设置 RUM appid ,才会开启RUM 的采集功能,获取 appid 方法 |
setSamplingRate | Float | 否 | 设置采集率,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 1。作用域为同一 session_id 下所有 View,Action,LongTask,Error 数据 |
setSessionErrorSampleRate | Float | 否 | 设置错误采集率,当会话未被 setSamplingRate 采样时,若会话期间发生错误,可以采集到错误前 1 分钟范围的数据,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 0。作用域为同一 session_id 下所有 View,Action,LongTask,Error 数据。ft-sdk 1.6.11 以上支持 |
setEnableTrackAppCrash | Boolean | 否 | 是否上报 App 崩溃日志,默认为 false ,开启后会在错误分析中显示错误堆栈数据。关于崩溃日志中混淆内容转换的问题。 ft-sdk 1.5.1 以上版本,可以通过 extraLogCatWithJavaCrash 、extraLogCatWithNativeCrash 设置在 Java Crash 和 Native Crash 是否显示 logcat |
setExtraMonitorTypeWithError | Array | 否 | 设置辅助监控信息,添加附加监控数据到 Rum 崩溃数据中,ErrorMonitorType.BATTERY 为电池余量,ErrorMonitorType.MEMORY 为内存用量,ErrorMonitorType.CPU 为 CPU 占有率 |
setDeviceMetricsMonitorType | Array | 否 | 设置 View 监控信息,在 View 周期中,添加监控数据,DeviceMetricsMonitorType.BATTERY 监控当前页的最高输出电流输出情况,DeviceMetricsMonitorType.MEMORY 监控当前应用使用内存情况,DeviceMetricsMonitorType.CPU 监控 CPU 跳动次数 ,DeviceMetricsMonitorType.FPS 监控屏幕帧率。监控周期,DetectFrequency.DEFAULT 500 毫秒,DetectFrequency.FREQUENT 100毫秒,DetectFrequency.RARE 1 秒 |
setEnableTrackAppANR | Boolean | 否 | 是否开启 ANR 检测,默认为 false 。ft-sdk 1.5.1 以上版本,可以通过 extraLogCatWithANR 设置 ANR 中是否显示 logcat |
setEnableTrackAppUIBlock | Boolean, long | 否 | 是否开启 UI 卡顿检测,默认为 false ,ft-sdk 1.6.4 以上版本可以通过 blockDurationMs 控制检测时间范围 [100,),单位毫秒, 默认是 1 秒 |
setEnableTraceUserAction | Boolean | 否 | 是否自动追踪用户操作,目前只支持用户启动和点击操作,默认为 false |
setEnableTraceUserView | Boolean | 否 | 是否自动追踪用户页面操作,默认为 false |
setEnableTraceUserViewInFragment | Boolean | 否 | 是否自动追踪 Fragment 类型的页面数据,默认为 false , ft-sdk 1.6.11 以上支持 |
setEnableTraceUserResource | Boolean | 否 | 是否自动追动用户网络请求 ,仅支持 Okhttp ,默认为 false |
setEnableResourceHostIP | Boolean | 否 | 是否采集请求目标域名地址的 IP。作用域:只影响 EnableTraceUserResource 为 true 的默认采集。自定义 Resource 采集,需要使用 FTResourceEventListener.FTFactory(true) 来开启这个功能。另外,单个 Okhttp 对相同域名存在 IP 缓存机制,相同 OkhttpClient ,在连接服务端 IP 不发生变化的前提下,只会生成一次 |
setResourceUrlHandler | Callback | 否 | 设置需要过滤的 Resource 条件,默认不过滤 |
setOkHttpEventListenerHandler | Callback | 否 | ASM 设置全局 Okhttp EventListener,默认不设置 |
setOkHttpResourceContentHandler | Callback | 否 | ASM 设置全局 FTResourceInterceptor.ContentHandlerHelper ,默认不设置, ft-sdk 1.6.7 以上支持,自定义 Resource |
addGlobalContext | Dictionary | 否 | 添加自定义标签,用于用户监测数据源区分,如果需要使用追踪功能,则参数 key 为 track_id ,value 为任意数值,添加规则注意事项请查阅此处 |
setRumCacheLimitCount | int | 否 | 本地缓存 RUM 限制数量 [10_000,),默认是 100_000。ft-sdk 1.6.6 以上支持 |
setRumCacheDiscardStrategy | RUMCacheDiscard | 否 | 设置 RUM 达到限制上限以后的数据的丢弃规则,默认为 RUMCacheDiscard.DISCARD ,DISCARD 为丢弃追加数据,DISCARD_OLDEST 丢弃老数据,ft-sdk 1.6.6 以上支持 |
Log 配置¶
方法名 | 类型 | 必须 | 含义 |
---|---|---|---|
setSamplingRate | Float | 否 | 设置采集率,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 1。 |
setEnableConsoleLog | Boolean | 否 | 是否上报控制台日志,日志等级对应关系 Log.v -> ok; Log.i -> info; Log.d -> debug; Log.e -> error; Log.w -> warning, prefix 为控制前缀过滤参数,默认不设置过滤。注意:Android 控制台量是很大的,为了避免影响应用性能,减少不必要的资源浪费,建议使用 prefix 过滤出有价值的日志。ft-plugin 1.3.5 以上版本,支持捕获 Log.println 打印的日志 |
setEnableLinkRUMData | Boolean | 否 | 是否与 RUM 数据关联,默认为 false |
setEnableCustomLog | Boolean | 否 | 是否上传自定义日志,默认为 false |
setLogLevelFilters | Array | 否 | 设置等级日志过滤,默认不设置 |
addGlobalContext | Dictionary | 否 | 添加 log 全局属性,添加规则请查阅此处 |
setLogCacheLimitCount | Int | 否 | 本地缓存最大日志条目数量限制 [1000,),日志越大,代表磁盘缓存压力越大,默认 5000 |
setLogCacheDiscardStrategy | LogCacheDiscard | 否 | 设置日志达到限制上限以后的日志丢弃规则,默认为 LogCacheDiscard.DISCARD ,DISCARD 为丢弃追加数据,DISCARD_OLDEST 丢弃老数据 |
Trace 配置¶
方法名 | 类型 | 必须 | 含义 |
---|---|---|---|
setSamplingRate | Float | 否 | 设置采集率,取值范围 [0,1],0 表示不采集,1 表示全采集,默认值为 1。 |
setTraceType | TraceType | 否 | 设置链路追踪的类型,默认为 DDTrace ,目前支持 Zipkin , Jaeger , DDTrace ,Skywalking (8.0+),TraceParent (W3C),如果接入 OpenTelemetry 选择对应链路类型时,请注意查阅支持类型及 agent 相关配置 |
setEnableLinkRUMData | Boolean | 否 | 是否与 RUM 数据关联,默认为 false |
setEnableAutoTrace | Boolean | 否 | 设置是否开启自动 http trace,目前只支持 OKhttp 的自动追踪,默认为 false |
setOkHttpTraceHeaderHandler | Callback | 否 | ASM 设置全局 FTTraceInterceptor.HeaderHandler ,默认不设置, ft-sdk 1.6.8 以上支持,示例参考自定义 Trace |
RUM 用户数据追踪¶
在FTRUMConfig
配置 enableTraceUserAction
, enableTraceUserView
, enableTraceUserResource
,setEnableTrackAppUIBlock
,setEnableTrackAppCrash
和 setEnableTrackAppANR
来实现 Action
、View
、Resource
、LongTask
、Error
的自动采集追踪获取数据。如果想自定义采集可以通过 FTRUMGlobalManager
来上报数据,示例如下:
Action¶
使用方法¶
/**
* 添加 Action
*
* @param actionName action 名称
* @param actionType action 类型
* @param property 附加属性参数(可选)
*/
public void startAction(String actionName, String actionType, HashMap<String, Object> property)
/**
* 添加 Action, 此类数据无法关联 Error,Resource,LongTask 数据
*
* @param actionName action 名称
* @param actionType action 类型
* @param duration 纳秒,持续时间(可选)
* @param property 扩展属性(可选)
*/
public void addAction(String actionName, String actionType, long duration, HashMap<String, Object> property)
/**
* 添加 action
*
* @param actionName action 名称
* @param actionType action 类型
* @param property 附加属性参数(可选)
*/
fun startAction(actionName: String, actionType: String, property: HashMap<String, Any>)
/**
* 添加 Action
*
* @param actionName action 名称
* @param actionType action 类型
* @param duration 纳秒,持续时间(可选)
* @param property 扩展属性(可选)
*/
fun addAction(actionName: String, actionType: String, duration: Long, property: HashMap<String, Any>)
startAction 内部有计算耗时算法,计算期间会尽量与附近发生的 Resource,LongTask,Error 数据做数据关联,会有 100 ms 频繁触发保护,建议使用于用户操作类型的数据。如果有频繁调用的需求请使用 addAction,这个数据不会于 startAction 发生冲突,并且不与当下 Resource,LongTask,Error 进行数据关联
代码示例¶
// 场景1
FTRUMGlobalManager.get().startAction("login", "action_type");
// 场景2: 动态参数
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().startAction("login", "action_type", map);
// 场景1
FTRUMGlobalManager.get().addAction("login", "action_type");
// 场景2: 动态参数
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().addAction("login", "action_type", map);
// 场景1
FTRUMGlobalManager.get().startAction("login", "action_type")
// 场景2: 动态参数
val map = HashMap<String,Any>()
map["ft_key"]="ft_value"
FTRUMGlobalManager.get().startAction("login","action_type",map)
// 场景1
FTRUMGlobalManager.get().startAction("login", "action_type")
// 场景2: 动态参数
val map = HashMap<String,Any>()
map["ft_key"]="ft_value"
FTRUMGlobalManager.get().startAction("login","action_type",map)
View¶
使用方法¶
代码示例¶
@Override
protected void onResume() {
super.onResume();
// 场景 1
FTRUMGlobalManager.get().startView("Current Page Name");
// 场景 2: 动态参数
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();
// 场景 1
FTRUMGlobalManager.get().stopView();
// 场景 2 : 动态参数
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key_will_change", "ft_value_change"); //ft_key_will_change 这个数值,会在 stopView 时候被修改为 ft_value_change
FTRUMGlobalManager.get().startView("Current Page Name", map);
}
override fun onResume() {
super.onResume()
// 场景 1
FTRUMGlobalManager.get().startView("Current Page Name")
// 场景 2: 动态参数
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()
// 场景 1
FTRUMGlobalManager.get().stopView()
// 场景 2 : 动态参数
val map = HashMap<String, Any>()
map["ft_key_will_change"] = "ft_value_change" //ft_key_will_change 这个数值,会在 stopView 时候被修改为 ft_value_change
FTRUMGlobalManager.get().startView("Current Page Name", map)
}
Error¶
使用方法¶
/**
* 添加错误信息
*
* @param log 日志
* @param message 消息
* @param errorType 错误类型, ErrorType
* @param state 程序运行状态
* @param dateline 发生时间,纳秒(可选)
* @param property 附加属性(可选)
*/
public void addError(String log, String message, long dateline, ErrorType errorType,
AppState state, HashMap<String, Object> property)
/**
* 添加错误信息
*
* @param log 日志
* @param message 消息
* @param errorType 错误类型, String
* @param state 程序运行状态
* @param dateline 发生时间,纳秒(可选)
* @param property 附加属性(可选)
*/
public void addError(String log, String message, long dateline, String errorType,
AppState state, HashMap<String, Object> property)
/**
* 添加错误信息
*
* @param log 日志
* @param message 消息
* @param errorType 错误类型, ErrorType
* @param state 程序运行状态
* @param dateline 发生时间,纳秒(可选)
* @param property 附加属性(可选)
*/
fun addError(log: String, message: String, dateline: Long, errorType: ErrorType,state: AppState, property: HashMap<String, Any>)
/**
* 添加错误信息
*
* @param log 日志
* @param message 消息
* @param errorType 错误类型, String
* @param state 程序运行状态
* @param dateline 发生时间,纳秒(可选)
* @param property 附加属性(可选)
*/
fun addError(log: String, message: String, dateline: Long, errorType: String,state: AppState, property: HashMap<String, Any>)
代码示例¶
// 场景 1:
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN);
// 场景 2:延迟记录发生的错误,这里的时间一般为错误发生的时间
FTRUMGlobalManager.get().addError("error log", "error msg", 16789000000000000000L, ErrorType.JAVA, AppState.RUN);
// 场景 3:动态参数
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN, map);
// 场景 1:
FTRUMGlobalManager.get().addError("error log", "error msg", ErrorType.JAVA, AppState.RUN)
// 场景 2:延迟记录发生的错误,这里的时间一般为错误发生的时间
FTRUMGlobalManager.get().addError("error log", "error msg", 16789000000000000000, ErrorType.JAVA, AppState.RUN)
// 场景 3:动态参数
val map = HashMap<String, Any>()
map["ft_key"] = "ft_value"
FTRUMGlobalManager.get().addError("error log", "error msg",ErrorType.JAVA,AppState.RUN,map)
LongTask¶
使用方法¶
代码示例¶
Resource¶
使用方法¶
/**
* resource 起始
*
* @param resourceId 资源 Id
* @param property 附加属性参数(可选)
*/
public void startResource(String resourceId, HashMap<String, Object> property)
/**
* resource 终止
*
* @param resourceId 资源 Id
* @param property 附加属性参数(可选)
*/
public void stopResource(final String resourceId, HashMap<String, Object> property)
/**
* 设置网络传输内容
*
* @param resourceId
* @param params
* @param netStatusBean
*/
public void addResource(String resourceId, ResourceParams params, NetStatusBean netStatusBean)
/**
* resource 起始
*
* @param resourceId 资源 Id(可选)
*/
fun startResource(resourceId: String, property: HashMap<String, Any>)
/**
* resource 终止
*
* @param resourceId 资源 Id
* @param property 附加属性参数(可选)
*/
fun stopResource(resourceId: String, property: HashMap<String, Any>)
/**
* 设置网络传输内容
*
* @param resourceId
* @param params
* @param netStatusBean
*/
fun addResource(resourceId: String, params: ResourceParams, netStatusBean: NetStatusBean)
代码示例¶
// 场景 1
// 请求开始
FTRUMGlobalManager.get().startResource("resourceId");
//...
// 请求结束
FTRUMGlobalManager.get().stopResource("resourceId");
// 最后,在请求结束之后,发送请求相关的数据指标
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);
// 场景 2 :动态参数使用
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 这个数值,会在 stopResource 时候被修改为 ft_value_change
FTRUMGlobalManager.get().stopResource(uuid,map);
// 场景 1
//请求开始
FTRUMGlobalManager.get().startResource("resourceId")
//请求结束
FTRUMGlobalManager.get().stopResource("resourceId")
//最后,在请求结束之后,发送请求相关的数据指标
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)
// 场景 2 :动态参数使用
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 这个数值,会在 stopResource 时候被修改为 ft_value_change
FTRUMGlobalManager.get().stopResource(uuid, map)
方法名 | 必须 | 含义 | 说明 |
---|---|---|---|
NetStatusBean.fetchStartTime | 否 | 请求开始时间 | |
NetStatusBean.tcpStartTime | 否 | tcp 连接时间 | |
NetStatusBean.tcpEndTime | 否 | tcp 结束时间 | |
NetStatusBean.dnsStartTime | 否 | dns 开始时间 | |
NetStatusBean.dnsEndTime | 否 | dns 结束时间 | |
NetStatusBean.responseStartTime | 否 | 响应开始时间 | |
NetStatusBean.responseEndTime | 否 | 响应结束时间 | |
NetStatusBean.sslStartTime | 否 | ssl 开始时间 | |
NetStatusBean.sslEndTime | 否 | ssl 结束时间 | |
NetStatusBean.property | 否 | 附加属性 | |
ResourceParams.url | 是 | url 地址 | |
ResourceParams.requestHeader | 否 | 请求头参数 | |
ResourceParams.responseHeader | 否 | 响应头参数 | |
ResourceParams.responseConnection | 否 | 响应 connection | |
ResourceParams.responseContentType | 否 | 响应 ContentType | |
ResourceParams.responseContentEncoding | 否 | 响应 ContentEncoding | |
ResourceParams.resourceMethod | 否 | 请求方法 | GET,POST 等 |
ResourceParams.responseBody | 否 | 返回 body 内容 | |
ResourceParams.property | 否 | 附加属性 |
Logger 日志打印¶
通过使用 FTLogger
进行自定义日志输出,需要开启 FTLoggerConfig.setEnableCustomLog(true)
。
目前日志内容限制为 30 KB,字符超出部分会进行截断处理
使用方法¶
/**
* 将单条日志数据存入本地同步
*
* @param content 日志内容
* @param status 日志等级,enum Status
* @param property 附加属性(可选)
*/
public void logBackground(String content, Status status, HashMap<String, Object> property)
/**
* 将单条日志数据存入本地同步
*
* @param content 日志内容
* @param status 日志等级,String
* @param property 附加属性(可选)
*/
public void logBackground(String content, String status, HashMap<String, Object> property)
/**
* 将多条日志数据存入本地同步
*
* @param logDataList {@link LogData} 列表
*/
public void logBackground(List<LogData> logDataList)
/**
* 将单条日志数据存入本地同步
*
* @param content 日志内容
* @param status 日志等级
* @param property 日志属性(可选)
*/
fun logBackground(content: String, status: Status, property: HashMap<String, Any>)
/**
* 将单条日志数据存入本地同步
*
* @param content 日志内容
* @param status 日志等级
* @param property 日志属性(可选)
*/
fun logBackground(content: String, status: String, property: HashMap<String, Any>)
/**
* 将多条日志数据存入本地同步
*
* @param logDataList 日志数据列表
*/
fun logBackground(logDataList: List<LogData>)
日志等级¶
方法名 | 含义 |
---|---|
Status.DEBUG | 调试 |
Status.INFO | 提示 |
Status.WARNING | 警告 |
Status.ERROR | 错误 |
Status.CRITICAL | 严重 |
Status.OK | 恢复 |
代码示例¶
// 上传单个日志
FTLogger.getInstance().logBackground("test", Status.INFO);
// 传递参数到 HashMap
HashMap<String, Object> map = new HashMap<>();
map.put("ft_key", "ft_value");
FTLogger.getInstance().logBackground("test", Status.INFO, map);
// 批量上传日志
List<LogData> logList = new ArrayList<>();
logList.add(new LogData("test", Status.INFO));
FTLogger.getInstance().logBackground(logList);
Tracer 网络链路追踪¶
FTTraceConfig
配置开启enableAutoTrace
自动添加链路数据,或手动使用 FTTraceManager
在 Http 请求中 Propagation Header
,示例如下:
String url = "https://request.url";
String uuid = "uuid";
// 获取链路头参数
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();
// 在请求中,添加链路头参数
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"
//获取链路头参数
val headers = FTTraceManager.get().getTraceHeader(uuid, url)
val client: OkHttpClient = OkHttpClient.Builder().addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
//在请求中,添加链路头参数
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()
通过 OKHttp Interceptor 自定义 Resource 和 TraceHeader¶
FTRUMConfig
的enableTraceUserResource
,FTTraceConfig
的 enableAutoTrace
配置,同时开启,优先加载自定义 Interceptor
配置,
ft-sdk < 1.4.1,需要关闭
FTRUMConfig
的enableTraceUserResource
,FTTraceConfig
的enableAutoTrace
。 ft-sdk > 1.6.7 支持自定 Trace Header 与 RUM 数据做关联
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 以上版本支持
@Override
public String getSpanID() {
return "span_id";
}
// 1.6.7 以上版本支持
@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 读取部分 body,避免大数据消费
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) {
// 复制部分响应体以避免大数据消耗
val body = response.peekBody(33554432)
extraData["df_response_body"] = body.string()
}
}
override fun onException(e: Exception, extraData: HashMap<String, Any>) {
// 处理异常情况
}
}))
.eventListenerFactory(FTResourceEventListener.FTFactory())
.build()
OKhttp 添加 ResourceID¶
对 Okhttp Request 添加 uuid,如果有相同请求高频并发场景建议开启。ft-plugin 1.3.5 以上版本,ft-sdk 1.6.10 以上版本开启FTSDKConfig.setEnableOkhttpRequestTag(true)
可以自动在 Request 添加 ResourceID
用户信息绑定与解绑¶
使用 FTSdk
进行用户的绑定和解绑
使用方法¶
UserData¶
方法名 | 含义 | 必须 | 注意 |
---|---|---|---|
setId | 设置用户 ID | 否 | |
setName | 设置用户名 | 否 | |
setEmail | 设置邮箱 | 否 | |
setExts | 设置用户扩展 | 否 | 添加规则请查阅此处 |
代码示例¶
// 可以在用户登录成功后调用此方法用来绑定用户信息
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);
// 可以在用户退出登录后调用此方法来解绑用户信息
FTSdk.unbindRumUserData();
//可以在用户登录成功后调用此方法用来绑定用户信息
FTSdk.bindRumUserData("001")
//绑定用户更多数据
val userData = UserData()
userData.name = "test.user"
userData.id = "test.id"
userData("test@mail.com")
val extMap = HashMap<String, String>()
extMap["ft_key"] = "ft_value"
userData.setExts(extMap)
FTSdk.bindRumUserData(userData)
//可以在用户退出登录后调用此方法来解绑用户信息
FTSdk.unbindRumUserData()
关闭 SDK¶
使用 FTSdk
关闭 SDK, 如果动态改变 SDK 配置,需要先关闭,以避免错误数据的产生
清理 SDK 缓存数据¶
使用 FTSdk
清理未上报的缓存数据
主动同步数据¶
使用 FTSdk
主动同步数据。
FTSdk.setAutoSync(false) 时, 才需要自行进行数据同步
动态开启和关闭获取 AndroidID¶
使用 FTSdk
设置是否在 SDK中获取 Android ID
添加自定义标签¶
使用 FTSdk
在 SDK运行时,动态添加标签
使用方法¶
/**
* 动态设置全局 tag
* @param globalContext
*/
public static void appendGlobalContext(HashMap<String,Object> globalContext)
/**
* 动态设置 RUM 全局 tag
* @param globalContext
*/
public static void appendRUMGlobalContext(HashMap<String,Object> globalContext)
/**
* 动态设置 log 全局 tag
* @param globalContext
*/
public static void appendLogGlobalContext(HashMap<String,Object> globalContext)
/**
* 动态设置全局 tag
* @param globalContext
*/
fun appendGlobalContext(globalContext: HashMap<String, Any>)
/**
* 动态设置 RUM 全局 tag
* @param globalContext
*/
fun appendRUMGlobalContext(globalContext: HashMap<String, Any>)
/**
* 动态设置 log 全局 tag
* @param globalContext
*/
fun appendLogGlobalContext(globalContext: HashMap<String, Any>)
代码示例¶
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)
符号文件上传¶
plugin 上传 (仅支持 datakit【本地部署】)¶
ft-plugin
版本需要 1.3.0
以上版本支持最新的符号文件上传规则,支持 productFlavor
多版本区分管理,plugin 会在 gradle task assembleRelease
之后执行上传符号文件,详细配置可以参考 SDK Demo
FTExt {
//...
autoUploadMap = true // 上报 mapping.txt 文件,默认为 false
autoUploadNativeDebugSymbol = true // 上报 c/c++ symbol so 文件,默认为 false
datakitUrl = 'https://datakit.url' // datakit 上报地址, generateSourceMapOnly=true 时,无需配置
datawayToken = 'dataway_token' // 空间 token , generateSourceMapOnly=true 时,无需配置
appId = "appid_xxxxx" // appid , generateSourceMapOnly=true 时,无需配置
env = 'common' // 环境, generateSourceMapOnly=true 时,无需配置
// native so 指定路径,只要指定到 abi 文件的上层目录
// |-stripped_native_libs
// |-release
// |-out
// |-lib
// |-arm64-v8a
// |-armeabi-v7a
// |-...
//nativeLibPath='/build/intermediates/merged_native_libs/release/out/lib'
generateSourceMapOnly = false //仅生成 sourcemap,默认为 false,路径示例:/app/build/tmp/ft{flavor}SourceMapMerge-release.zip,ft-plugin:1.3.4 以上版本支持
prodFlavors { //prodFlavors 配置会覆盖外层设置
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"
}
}
}
手动上传¶
使用 plugin
开启 generateSourceMapOnly = true
, 执行 gradle task assembleRelease
生成,或自行打包成 zip
文件,然后自行上传至 datakit
或从观测云 Studio 上传,推荐使用 zip
命令行进行打包,避免将一些系统隐藏文件打入 zip
包中,符号上传请参考 sourcemap 上传
Unity Native Symbol 文件请参考官方文档
权限配置说明¶
名称 | 必须 | 使用原因 |
---|---|---|
READ_PHONE_STATE |
否 | 用于获取手机的蜂窝网设备信息 |
关于如何申请动态权限,具体详情参考 Android Developer
Plugin AOP 忽略¶
通过 Plugin AOP 覆盖方法中添加 @IngoreAOP
来忽略 ASM 插入。如果需要批量忽略,请通过使用 ft-plugin
FTExt
中 ignorePackages
进行忽略。
WebView 数据监测¶
WebView 数据监测,需要在 WebView 访问页面集成Web 监测 SDK
数据脱敏¶
如果是希望对字段做全脱敏,推荐使用 setDataModifier
,表现的性能更好。如果需要细致规则替换推荐 setLineDataModifier
请勿在回调方法中使用复杂或者延迟较高的方法,这会极大影响 SDK 数据写入性能
FTSdk.install(
FTSDKConfig.builder("xxx")
.setDataModifier(new DataModifier() {
/**
* 对某个字段进行更改
*
* @param key 字段名
* @param value 字段值(原始值)
* @return 新的值,返回 null 表示不做更改
*/
@Override
public Object modify(String key, Object value) {
if (key.equals("device_uuid")) {
return "xxx";
}
return null;
}
}).setLineDataModifier(new LineDataModifier() {
/***
* 对某一行数据进行修改
*
* @param measurement 数据指标类型 view,action,resource,
* longtask,error,df_rum_android_log
* @param data 原始数据的 key-value 对
* @return 需要修改的 key-value,(返回 null 或空 map 均为不更改)
*/
@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 {
/**
* 对某个字段进行更改
*
* @param key 字段名
* @param value 字段值(原始值)
* @return 新的值,返回 null 表示不做更改
*/
override fun modify(key: String, value: Any?): Any? {
return if (key == "device_uuid") {
"xxx" // 替换为自定义 device_uuid
} else {
null
}
}
})
// 批量修改单条数据中的某些字段
.setLineDataModifier(object : LineDataModifier {
/**
* 对某一行数据进行修改
*
* @param measurement 数据指标类型 view,action,resource,
* longtask,error,df_rum_android_log
* @param data 原始数据的 key-value 对
* @return 需要修改的 key-value,(返回 null 或空 map 均为不更改)
*/
override fun modify(
measurement: String,
data: HashMap<String, Any>
): Map<String, Any>? {
return if (measurement == "view") {
hashMapOf("view_url" to "xxx")
} else {
null
}
}
})
自定义标签使用示例¶
编译配置方式¶
- 在
build.gradle
中创建多个productFlavors
来做区分区分标签
android{
//…
productFlavors {
prodTest {
buildConfigField "String", "CUSTOM_VALUE", "\"Custom Test Value\""
//…
}
prodPublish {
buildConfigField "String", "CUSTOM_VALUE", "\"Custom Publish Value\""
//…
}
}
}
- 在
RUM
配置中添加对应BuildConfig
常量
运行时读写文件方式¶
- 通过存文件类型数据,例如
SharedPreferences
,配置使用SDK
,在配置处添加获取标签数据的代码。
- 在任意处添加改变文件数据的方法。
3.最后重启应用,详细细节请见 SDK Demo
SDK 运行时添加¶
在 SDK 初始化完毕之后,使用FTSdk.appendGlobalContext(globalContext)
、FTSdk.appendRUMGlobalContext(globalContext)
、FTSdk.appendLogGlobalContext(globalContext)
,可以动态添加标签,设置完毕,会立即生效。随后,RUM 或 Log 后续上报的数据会自动添加标签数据。这种使用方式适合延迟获取数据的场景,例如标签数据需要网络请求获取。
//SDK 初始化伪代码,从网络获取到参数后,再进行标签设置
FTSdk.init()
getInfoFromNet(info){
HashMap<String, Object> globalContext = new HashMap<>();
globalContext.put("delay_key", info.value);
FTSdk.appendGlobalContext(globalContext)
}
常见问题¶
添加局变量避免冲突字段¶
为了避免自定义字段与 SDK 数据冲突,建议标签命名添加 项目缩写 的前缀,例如 df_tag_name
,项目中使用 key
值可查询源码。SDK 全局变量中出现与 RUM、Log 相同变量时,RUM、Log 会覆盖 SDK 中的全局变量。
SDK 兼容性¶
应对市场隐私审核¶
隐私声明¶
方式 1: SDK AndroidID 配置¶
SDK 为更好关联相同用户数据,会使用 Android ID。如果需要在应用市场上架,需要通过如下方式对应市场隐私审核。
public class DemoApplication extends Application {
@Override
public void onCreate() {
// 在初始化设置时将 setEnableAccessAndroidID 设置为 false
FTSDKConfig config = new FTSDKConfig.Builder(DATAKIT_URL)
.setEnableAccessAndroidID(false)
.build();
FTSdk.install(config);
// ...
}
}
// 用户同意隐私协议后再开启
FTSdk.setEnableAccessAndroidID(true);
方式 2:延迟初始化 SDK¶
如果需要在应用中延迟加载 SDK,建议使用如下方式初始化。
// Application
public class DemoApplication extends Application {
@Override
public void onCreate() {
//如果已经同意协议,在 Application 中初始化
if(agreeProtocol){
FTSdk.init(); //SDK 初始化伪代码
}
}
}
// 隐私声明 Activity 页面
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
//未阅读隐私声明
if ( notReadProtocol ) {
//隐私声明弹出弹窗
showProtocolView();
//如果同意隐私声明
if( agreeProtocol ){
FTSdk.init(); //SDK 初始化伪代码
}
}
}
}
// Application
class DemoApplication : Application() {
override fun onCreate() {
// 如果已经同意协议,在 Application 中初始化
if (agreeProtocol) {
FTSdk.init() //SDK 初始化伪代码
}
}
}
// 隐私声明 Activity 页面
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
// 未阅读隐私声明
if (notReadProtocol) {
// 隐私声明弹出弹窗
showProtocolView()
// 如果同意隐私声明
if (agreeProtocol) {
FTSdk.init() //SDK 初始化伪代码
}
}
}
}
第三方框架¶
flutter
、react-native
、uni-app
、unity
可以采用与以上原生 Android 相似延迟初始化方式,来应对应用市场隐私审核。
Jetpack Compose 支持¶
目前暂时不支持自动采集 compose 组件生成的页面,但是可以通过手动 Action
和 View
的自定义接口,点击事件和页面跳转事件进行追踪,可以参考这里
不使用 ft-plugin 情况下如何接入 SDK¶
观测云使用的 Androig Grale Plugin Transformation 实现的代码注入,从而实现数据自动收集。但是由于一些兼容性问题,可能存在无法使用 ft-plugin
的问题。受影响包括 RUM Action
,Resource
,和 android.util.Log
,Java 与 Kotlin println
控制台日志自动抓取,以及符号文件的自动上传。
目前针对这种情况,我们有另外一种集成方案,应对方案如下:
- Application 应用启动事件, 源码示例参考DemoForManualSet.kt
- 按键等事件需要在触发处自行添加,例如,Button onClick 事件为例,源码示例参考 ManualActivity.kt:
OKhttp
通过addInterceptor
,eventListener
方式接入Resource
,Trace
,示例如下,源码示例参考 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();
-
其他网络框架需要自行实现使用
FTRUMGlobalManager
中startResource
,stopResource
,addResource
,FTTraceManager.getTraceHeader
。具体实现方式,请参考源码示例 ManualActivity.kt -
WebView 数据采集配置