React Native SDK

集成说明

QuickTracking React Native SDK是基于QuickTracking 原生客户端埋点SDK上的扩展,封装了QT埋点常用的api,如全局属性、页面属性、自定义事件等。

React Native SDK集成

npm线上地址:react-native-quicktracking-analytics-module

下载npm包到项目中

# npm
npm install react-native-quicktracking-analytics-module

# yarn
yarn add react-native-quicktracking-analytics-module

# pnpm
pnpm add react-native-quicktracking-analytics-module

引入SDK环境变量

import { QT } from "react-native-quicktracking-analytics-module";

Android 基座集成

进入控制台

进入QT后台,点击“管理控制台”

image

集成应用

找到需要集成埋点的应用:进入“应用列表”,选择所需组织,点击操作中的“详情或者去集成”

image

Maven地址配置

在工程 build.gradle 配置脚本中 buildscript 和 allprojects 段中添加 sdk maven 仓库地址

buildscript {
    repositories {
    google()
    jcenter()
    maven { url 'https://repo1.maven.org/maven2/' }
  }
  dependencies {
      classpath 'com.android.tools.build:gradle:3.4.0'}
      // NOTE: Do not place your application dependencies here; they belong
      // in the individual module build.gradle files
  }
}
allprojects {
    repositories {
    google()
    jcenter()
    maven { url 'https://repo1.maven.org/maven2/' }
  }
}

组件引用

在工程app对应build.gradle配置脚本dependencies段中,添加集成所需要的依赖

dependencies {
    implementation fileTree(include:['*.jar'], dir:'libs')

    //QuickTracking统计分析SDK
    implementation 'com.lydaas.qtsdk:qt-px-common:1.6.3.PX'
}

请注意:如果已经通过package.json 添加了 QuickTracking ReactNative SDK 的依赖,则不需要再单独集成QuickTracking Android原生SDK

埋点验证配置

在Android.manifest中,找到MainActivity对应的activity标签,并将以下代码粘贴进去,appkey换成自己的

//1.唤起码默认为"atm.该app对应的appkey",不可改变
//2.请使用单独intent-filter,和其他intent-filter并列
//不要将以下代码填入其他intent-filter里;

<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="atm.appkey" />
</intent-filter>

配置权限

统计SDK需要宿主APP授予如下权限:

权限

用途

ACCESS_NETWORK_STATE

检测联网方式,在网络异常状态下避免数据发送,节省流量和电量。

READ_PHONE_STATE(可选)

获取用户设备的IMEI,通过IMEI对用户进行唯一标识,以便提供统计分析服务。

ACCESS_WIFI_STATE

获取WIFI mac地址,在平板设备或电视盒子上,无法通过IMEI标识设备,我们会将WIFI mac地址作为用户的唯一标识,以便正常提供统计分析服务。

INTERNET

允许应用程序联网和发送统计数据的权限,以便提供统计分析服务。

下面给出AndroidManifest.xml清单文件示例:

<manifest ……> 
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <application ……>
</manifest>

混淆配置

如果您的应用使用了代码混淆,请添加如下配置,以避免Quick Tracking SDK被错误混淆导致SDK不可用。

-keep class com.umeng.** {*;}
-keep class org.repackage.** {*;}

-keep class com.quick.qt.** {*;}
-keep class rpk.quick.qt.** {*;}

-keepclassmembers class * {
   public <init> (org.json.JSONObject);
}
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

SDK需要引用导入工程的资源文件,通过了反射机制得到资源引用文件R.java,但是在开发者通过proguard等混淆优化工具处理apk时,proguard可能会将R.java删除,如果遇到这个问题,请添加如下配置:

-keep public class [您的应用包名].R$*{
    public stac final int 

初始化SDK

  1. 域名设置

务必在初始化之前设置私有化环境收数域名。

/**
 * 设置上传统计日志的主域名和备用域名。SDK会优先将统计数据上报到主域名,失败的情况下会再尝试将数据上报到备用域名。
 * 主域名primaryDomain或不能传入null或者空串,否则会抛出SdkDomainUndefined运行时异常。
 */
QtConfigure.setCustomDomain("收数域名", null); 
  1. 预初始化

请在宿主App的Application.onCreate函数中调用基础组件库初始化函数。

//SDK预初始化函数不会采集设备信息,也不会向QT后台上报数据。
//preInit预初始化函数耗时极少,不会影响App首次冷启动用户体验
QtConfigure.preInit(this,"appkey","Channel");

3.正式初始化

//正式初始化SDK,务必调用
public static void init(Context context,String appkey,String channel,int deviceType,String pushSecret);

参数

含义

备注

appkey

QT为当前应用平台颁发的唯一标识。

  1. 入参的appkey一定要与QT后台保持一致;

  2. 该appkey会跟随每一条事件日志进行上报,用于QT的应用平台数据区分标识

channel

app投放的应用市场

QT分析平台的“系统属性”中“升级渠道”的数据来源

deviceType

QtConfigure.DEVICE_TYPE_PHONE

默认填写QtConfigure.DEVICE_TYPE_PHONE即可

pushSecret

废弃字段,填写空即可

废弃字段,填写空即可

示例:

public class MainApplication extends Application implements ReactApplication {
    @Override
    public void onCreate() {
    super.onCreate();
    
    //SDK预初始化函数不会采集设备信息,也不会向QT后台上报数据。
    //preInit预初始化函数耗时极少,不会影响App首次冷启动用户体验
    QtConfigure.preInit(this,"您的appkey","aliyun");
    //正式初始化SDK,务必调用
    QtConfigure.init(this,"您的appkey","aliyun",QtConfigure.DEVICE_TYPE_PHONE, "");
  }

Android统计分析SDK集成方式请参考文档:Android SDK

iOS 基座集成

使用CocoaPods集成

进入iOS工程目录

cd ios && pod install && cd .. 

SDK初始化并集成appKey

为保证您的App在集成统计SDK之后,能够满足工信部相关合规要求,您应确保App首次冷启动时,在用户阅读您的《隐私政策》并取得用户授权之后,才调用正式初始化函数初始化统计SDK,此时SDK才会真正采集设备信息并上报数据。反之,如果用户不同意《隐私政策》授权,则不能调用初始化函数

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  
    /** 初始化所有组件产品
      * @param appKey 开发者在QT申请的appkey.
      * @param channel 渠道标识,可设置nil表示"App Store".
      */
    [QTConfigure initWithAppkey:@"应用的appKey" channel:@"安装渠道"];
 
    return YES;
}

一旦App获取到《隐私政策》的用户授权,后续的App冷启动,开发者应该保证调用到初始化函数。

配置收数域名

#import <QTCommon/UMConfigure.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  
    /** 设置上报统计日志的主域名和备用域名。此函数必须在SDK初始化函数调用之前调用。
      * @param primaryDomain 传日志的主域名收数地址,参数不能为null或者空串。
      * @param standbyDomain 上传日志备用域名收数地址,参数可以为null或者空串,若此参数为空,SDK内部会自动将主域名设置为备用域名。
      */
    [QTConfigure setCustomDomain:@"主收数域名" standbyDomain:@"备用收数域名"];

    return YES;
}

按需引入日志组件

引入统计所需组件库(在更新SDK时,您可以直接使用 pod update 命令进行直接更新)

pod 'UMCCommonLog'

Podfile示例

require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

platform :ios, '12.0'

target 'qt_reactnative_demo' do
  config = use_native_modules!
  
  //QuickTracking 统计分析 SDK
  pod 'QTCommon', '~> 1.5.8.PX'//需要指定版本号
   
  //可在项目中加入 “基础库-日志库” 中的 UMCCommonLog 进行开发调试。
  pod 'UMCCommonLog'
  
  use_react_native!(
    :path => config[:reactNativePath],
    # to enable hermes on iOS, change `false` to `true` and then install pods
    :hermes_enabled => flags[:hermes_enabled],
  )

  use_flipper!()

  post_install do |installer|
    react_native_post_install(installer)
    __apply_Xcode_12_5_M1_post_install_workaround(installer)
  end
end

请注意:如果已经通过package.json 添加了 QuickTracking ReactNative SDK 的依赖,则不需要再单独集成QuickTracking iOS原生SDK

开启日志打印

#import <QTCommon/UMConfigure.h>
#import <UMCommonLog/UMCommonLogHeaders.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  
    [UMCommonLogManager setUpUMCommonLogManager];
    [QTConfigure setLogEnabled:YES];
  
    return YES;
}

埋点验证配置

添加您的 URL Scheme 到项目中,URL Scheme 位于项目设置 target -> 选项卡 Info - > URL Types。

填入的scheme:atm.yourappkey。

image.png

在AppDelegate中调用函数[QTMobClick handleUrl:url]来接收 URL

- (BOOL)application:(UIApplication *)application openURL:(nonnull NSURL *)url options:(nonnull NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
    if ([QTMobClick handleUrl:url]) {
        return YES;
    }
    return YES;
}

iOS统计分析SDK集成方式请参考文档:iOS SDK

埋点验证操作请参考文档:埋点验证详细指南

埋点API

SDK初始化

正式初始化

务必调用,请务必在用户同意隐私政策后,再初始化SDK。

function init(appKey: string, channel: string): void

参数

含义

appKey

QT后台提供的唯一应用key值

channel

下载渠道

示例

QT.init('appkey', 'quicktracking');

用户账号上报

用户登录

该值的上传对应产品中“登录用户”:计算“登录用户”数,就是计算下述API上传值的去重数

function profileSignIn(ID: string, provider?: string): void

参数

含义

ID

用户账号,长度小于64字节。 该值的上传对应产品中“登录用户”:计算“登录用户”数,就是计算该用户账号的去重数

provider

无效字段,传空即可。

用户登出

账号登出时需调用此接口,调用之后不再发送账号相关内容。

function profileSignOff(): void

示例

QT.profileSignIn('用户ID');
QT.profileSignOff();

用户属性上传

使用事件编码固定为"$$_user_profile"的自定义事件上传,该事件所携带的事件属性会被作为用户属性放在用户表中。

function sendEvent(eventId: string, params: any): void

参数

含义

eventId

当前统计的事件ID

params

对当前事件的参数描述,定义为“参数名:参数值”的“<键-值>”对

请注意:用户属性上传一定要在用户账号上报后。

示例:

QT.onProfileSignIn("张三");

const user = {
  gender: "male",
  age: "8"
}
QT.sendEvent("$$_user_profile", user);

上述上传的业务含义为:张三 男 8岁

全局属性

全局属性为每一个事件都会携带的属性

注册全局属性

function registerGlobalProperty(globalProperty: any): void

参数

含义

globalProperty

要注册的全局属性,定义为“属性名:属性值”的“<键-值>”对。为单层对象结构,不支持多层嵌套。

注意:

  1. 属性名、string类型的属性值,只支持大小写字母、数字及下划线。

  2. 在Android中,属性值不支持JavaScript的boolean类型,需要手动在JS中转为0、1。

  3. 在Android中,对于全局属性值为null或undefined的场景,底层Android sdk会过滤这个全局属性字段,如需要空值分析场景,需自定义默认空值

  4. 在iOS中,全局属性值不支持null和undefined,需要手动过滤。

示例:

QT.registerGlobalProperty({
    name: 'MyApp',
    description: 'this is a app',
    aBoolean: 1, //boolean类型需传为0或1,
    aNull: '', //null或undefined类型需传空字符串
    //默认为number类型,对于返回值为null或undefined场景,需业务自定义数值型默认空值
    aNumber: 66, 
});

删除特定的全局属性

function unregisterGlobalProperty(propertyName: string): void

参数

含义

propertyName

要删除的全局属性名

示例:

 QT.unregisterGlobalProperty('name'); // 删除全局属性name

获取特定的全局属性

async function getGlobalProperty(propertyName: string): Promise<any>

参数

含义

propertyName

要获取的全局属性名

示例:

await QT.getGlobalProperty('name'); // 获取全局属性name,返回 {name: "MyApp"}

获取所有全局属性

async function getGlobalProperties(propertyName: string): Promise<any>

示例:

await QT.getGlobalProperties(); // 获取所有全局属性

清除所有全局属性

function clearGlobalProperties(): void

示例:

QT.clearGlobalProperties(); // 所有全局属性都被清除(慎用)

页面浏览事件埋点

开发者如果希望对页面路径和页面停留时长进行采集和统计。可以通过调用该接口手动埋点

function onPageStart(pageName: string): void
function onPageEnd(pageName: string): void

参数

含义

pageName

页面编码

示例:

QT.onPageStart('MainPage');
QT.onPageEnd('MainPage');

请注意:

onPageStart 是SDK记录页面进入的信息,onPageStart不会上报事件,只有调用onPageEnd的时候才会上报页面浏览事件。

onPageStart和onPageEnd必须成对调用,且传值的pageName需要保持一致,如果没有onPageEnd或者onPageEnd与onPageStart传值的pageName不一致,则onPageStart记录的信息不会生效。

页面属性上传

支持给当前页面附加自定义属性

function uploadPageProperties(pageName: string, params: EventParams): void

参数:

参数

含义

pageName

目标页面名(页面编码),必须和当前页面名一致。如不一致,函数执行无效。

params

对当前事件的参数描述,定义为"参数名:参数值" 的 "<键-值>"对。为单层对象结构,不支持多层嵌套。

示例:

QT.uploadPageProperties('detail_page', { test: 1 })

请注意:

  1. 该接口必须在onPageStart和onPageEnd之间调用。

  2. 属性名、string类型的属性值,只支持大小写字母、数字及下划线。

  3. 在Android中,属性值不支持JavaScript的boolean类型,需要手动在JS中转为0、1。

  4. 在Android中,对于全局属性值为null或undefined的场景,底层Android sdk会过滤这个全局属性字段,如需要空值分析场景,需自定义默认空值

  5. 在iOS中,全局属性值不支持null和undefined,需要手动过滤。

自定义事件埋点

自定义事件可以用于追踪用户行为,记录行为发生的具体细节。

使用 sendEvent 接口进行事件的统计,接口如下:

/**
  * 自定义事件埋点
  * @param eventId 当前统计的事件编码
  * @param params 对当前事件的参数描述,定义为“参数名:参数值”的“<键-值>”对
  * @param pageName 事件编码
  */
function sendEvent(eventId: string, params?: any, pageName?: string): void

参数

含义

eventId

为当前统计的事件编码。

params

对当前事件的参数描述,定义为“参数名:参数值”的“<键-值>对”。为单层对象结构,不支持多层嵌套。

pageName

当前统计事件的页面编码

示例:

// 携带事件参数的自定义事件
QT.sendEvent(
  'event1',
  {
    name: 'quick tracking',
    method: 'func',
  },
);

// 携带事件参数和页面编码的自定义事件
QT.sendEvent(
  'event2',
  {
    name: 'quick tracking',
    method: 'func',
  },
  'main-page'
);

备注:

  • 多参数类型事件能满足原来计算事件/计数事件的分析场景;

  • 对于计算型事件不同的参数类型对应不同的计算方式,总共可以分为两大类,数值型和字符型

  • 数字型:支持累加值、最大值、最小值、平均值和去重数计算

  • 字符型:支持去重数计算

请注意:

同全局属性,事件属性在Android和iOS不同平台上,也有着类型处理的差异:

  1. 在Android中,不支持JavaScript的boolean类型,需要手动在JS中转为0、1。

  2. 在Android中,对于全局属性值为null或undefined的场景,底层Android sdk会过滤这个全局属性字段,如需要空值分析场景,需自定义默认空值

  3. 在iOS中,全局属性值不支持null和undefined,需要手动过滤。

桥接事件埋点

桥接事件用于h5桥接RN的场景,使用此接口将H5日志发送至App中。

/**
  * 桥接事件埋点
  * @param data H5转发事件的日志体
  */
function sendEventForH5(data: string): void

参数

含义

data

H5转发事件的日志体

示例:

const content = data.nativeEvent.data;
QT.sendEventForH5(content);

RN App中嵌入h5页面(RN桥接模式)

H5集成 QuickTracking Web SDK

该步骤请参考:Web SDK

转发H5端发送的日志给React Native WebView

<script charset="UTF-8">
  ...
  // sdk接入及配置部分
  ...
  
  //转发页面自定义事件(点击、元素曝光、其他)
  aplus_queue.push({
    action: 'aplus.aplus_pubsub.subscribe',
    arguments: ['mw_change_hjlj', function (content) {
      var eventData = content && content.what_to_send && content.what_to_send.hjljdataToUmNative;
      if (/*iOS环境*/) {
        window.ReactNativeWebView.postMessage(JSON.stringify(eventData), '*'); 
      } else {
        window.ReactNativeWebView.postMessage(JSON.stringify(eventData));
      }
    }]
  })
  
  aplus_queue.push({
    action: 'aplus.aplus_pubsub.subscribe',
    arguments: ['mw_change_pv', function (content) {
      var pvData = content && content.what_to_send && content.what_to_send.pvdataToUmNative;
      if (/*iOS环境*/) {
        window.ReactNativeWebView.postMessage(JSON.stringify(pvData), '*');    
      } else {
        window.ReactNativeWebView.postMessage(JSON.stringify(pvData));
      }
    }]
  })
</script>

React Native WebView接收消息并调用QT SDK上报日志

import * as React from 'react'
import { WebView } from 'react-native-webview';
import { QT } from 'react-native-quicktracking-analytics-module';
import { Platform, SafeAreaView } from 'react-native';

export default function WebPage() {
  const onMessage = (data) => {
    try {
      const content = data.nativeEvent.data;
      QT.sendEventForH5(content);
    } catch (error) {
      console.log('webview message error:', error);
    }
  };
  
  return (
    <SafeAreaView style={{ flex: 1 }}>
    	<WebView
      	...
        onMessage={onMessage}
      	...
      />
    </SafeAreaView>
  );
}

RN 全埋点

需要 QuickTracking ReactNative SDK 2.0.0版本及以上支持

页面浏览事件自动采集

考虑到React Navigation库自身丰富的场景支持以及在社区的影响力,在SDK的页面浏览事件自动采集能力上直接借助了React Navigation开放的接口能力,示例如下:

import {QT} from 'react-native-quicktracking-analytics-module';
import {
  NavigationContainer,
  useNavigationContainerRef,
} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

const App = () => {
  const navigationRef = useNavigationContainerRef();
  const routeNameRef = useRef('');

  return (
    <NavigationContainer
      ref={navigationRef}
      onReady={() => {
        routeNameRef.current = currentRouteName;
        // 设置页面编码
        QT.onPageStart(currentRouteName);
      }}
      onStateChange={() => {
        const previousRouteName = routeNameRef.current;
        const currentRouteName = navigationRef.getCurrentRoute()?.name;
        // 按需设置页面属性(可选)
        QT.uploadPageProperties(previousRouteName, {
          test_page_p_1: 1,
          test_page_p_2: "test"
        });
        // 采集页面浏览事件
        QT.onPageEnd(previousRouteName);
        if (currentRouteName) {
          if (previousRouteName !== currentRouteName) {
            // 更新新的页面编码
            QT.onPageStart(currentRouteName);
            routeNameRef.current = currentRouteName;
          }
        }
      }}
    >
      ...
    </NavigationContainer>
  )
}

控件点击事件自动采集

在待埋点项目路径下执行node命令

node node_modules/react-native-quicktracking-analytics-module/src/hook.js -run

注:如果需要恢复原始文件,可以执行 reset 命令

node node_modules/react-native-quicktracking-analytics-module/src/hook.js -reset

忽略RN控件全埋点采集

由于存在混合开发的场景,支持单独关闭React Native控件点击事件的自动采集,在待埋点项目的package.json中增加QTSDKConfig配置:

{
  "name": "reactnative_demo",
  "QTSDKConfig": {
    "enableAutoCLK": true
  }
}

enableAutoCLK字段取值含义:

  • true 开启RN控件自动采集

  • false 关闭RN控件自动采集

注:开启RN控件点击事件自动采集功能需要配合上述 node 命令使用

开启iOS控件点击事件自动采集

#import <QTCommon/UMConfigure.h>
#import <QTCommon/MobClick.h>
#import <UMCommonLog/UMCommonLogHeaders.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

    // 全埋点原生控件点击自动采集功能开启
    [QTMobClick setAutoEventEnabled:YES];
    
    return YES;
}

开启Android控件点击事件自动采集

import com.quick.qt.analytics.QtTrackAgent;

public class MainApplication extends Application implements ReactApplication {
  ...
    
  @Override
  public void onCreate() {
    super.onCreate();
    ...
    // 全埋点原生控件点击自动采集功能开启
    QtTrackAgent.setAutoEventEnabled(true);
    ...
  }

  ...
}

设置ReactNative控件自定义属性

注:仅支持React Native控件,如TouchableHighlight、TouchableOpacity、Pressable等等

<Pressable
  onPress={()=>{}}
  qtParams={{
    pressable: "press_1",
  }}
>
  {({pressed}) => (
    <Text style={styles.text}>
      {pressed ? '点下了Pressable控件!' : 'Pressable 控件'}
    </Text>
  )}
</Pressable>

<TouchableHighlight 
  onPress={()=>{}} 
  qtParams={{aTouchableHighlight: 1, b: 2}}>
  <Text>TouchableHighlight 控件</Text>
</TouchableHighlight>

<TouchableOpacity 
  onPress={()=>{}} 
  qtParams={{aTouchableOpacity: 1, b: 2}}>
  <Text>TouchableOpacity 控件</Text>
</TouchableOpacity>

<TouchableWithoutFeedback 
  onPress={()=>{}} 
  qtParams={{aTouchableWithoutFeedback: 1, b: 2}}>
  <Text>TouchableWithoutFeedback 控件</Text>
</TouchableWithoutFeedback>

忽略单个控件的自动采集

在事件属性中增加 ignore字段等于true

<TouchableHighlight 
  onPress={()=>{}} 
  qtParams={{
    aTouchableHighlight: 1, 
    b: 2,
    ignore: true
  }}>
  <Text>TouchableHighlight 控件</Text>
</TouchableHighlight>