大同上报
大同上报
Kuikly
- Kuikly端绑定大同上报方法
// DtExt.kt
// 大同字段,用作数据上报
/**
* 绑定大同上报方法
* (如果元素想要绑定页面id,则需要在pager上绑定pageVR)
* */
fun Attr.elementVR(elementId: String, elementParams: JSONObject? = null) {
vr(null, elementId = elementId, elementParams = elementParams)
}
fun Attr.pageVR(pageId: String, pageParams: JSONObject? = null) {
vr(pageId = pageId, pageParams = pageParams)
}
/**
* [identifier]-列表中使用时设置,解决复用的问题
*/
fun Attr.vr(
pageId: String? = null,
elementId: String? = null,
elementParams: JSONObject? = null,
pageParams: JSONObject? = null,
identifier:String? = null,
) {
val vr = JSONObject()
pageId?.let {
vr.put("pageId", pageId)
}
elementId?.let {
vr.put("elementId", elementId)
}
elementParams?.let {
vr.put("params", elementParams)
}
pageParams?.let {
vr.put("pageParams", pageParams)
}
identifier?.let {
vr.put("identifier", identifier)
}
EcommerceExtConst.VR with vr.toString()
}
object EcommerceExtConst {
const val VR = "vr"
}
- 在元素页面设置相应的上报信息
在attr内调用绑定的大同上报方法
override fun body(): ViewBuilder {
val ctx = this
return {
attr {
pageVR("PageID")
}
...
Text {
attr {
elementVR("elementID")
}
}
...
}
}
安卓
新版大同SDK
- 宿主端集成大同SDK
参考:
https://git.woa.com/UniversalReport/DTKMM/tree/master
大同SDK版本需要使用:
com.tencent.dt:core:1.0.16-SNAPSHOT
- 宿主端实现自定义属性Handler用于设置页面和元素信息
在执行Kuikly业务侧上报操作,即调用elementVR(), pageVR()后,相关属性设置会传到宿主端,业务需要在宿主端响应,并调用大同SDK进行设置。
class ViewPropExternalHandler : IKuiklyRenderViewPropExternalHandler {
override fun setViewExternalProp(
renderViewExport: IKuiklyRenderViewExport,
propKey: String,
propValue: Any
): Boolean {
return when (propKey) {
PROP_VR -> {
DtKuiklyAndriod.setProp(renderViewExport, propValue)
true
}
else -> false
}
}
override fun resetViewExternalProp(
renderViewExport: IKuiklyRenderViewExport,
propKey: String
): Boolean {
return when (propKey) {
PROP_VR -> {
DtKuiklyAndriod.resetProp(renderViewExport)
true
}
else -> false
}
}
companion object {
const val PROP_VR = "vr"
}
}
这里简单包装了大同SDK,推荐业务方在使用过程中自行实行相关SDK的调用封装
// 参考代码
// DtKuiklyAndriod.kt
object DtKuiklyAndriod {
private val handlerMap = mutableMapOf<String, (View, Any) -> Unit>()
init {
handlerMap.apply {
put("pageId") { view, pageId ->
DT.setPageId(view, pageId as String)
}
put("pageParams") { view, pageParams ->
DT.resetPageParams(view)
DT.setPageParams(view, pageParams as MutableMap<String, Any>)
}
put("elementId") { view, elementId ->
DT.setElementId(view, elementId as String)
}
put("params") { view, elementParams ->
DT.resetElementParams(view)
DT.setElementParams(view, elementParams as MutableMap<String, Any>)
}
put("identifier") { view, identifier ->
DT.setElementReuseId(view, identifier as String)
}
}
}
fun setProp(renderViewExport: IKuiklyRenderViewExport, propValue: Any) {
val view = renderViewExport.view()
// 点击上报逻辑 如果需要
view.putViewData(KRCssConst.PRE_CLICK, object : KuiklyRenderCallback {
override fun invoke(result: Any?) {
val propValueJson = JSONObject(propValue as String)
val propMap = propValueJson.toMap()
// 此处会调用大同SDK的report出口,可在出口处处理上报信息。
DTCore.reportEvent("dt_clck", propMap)
}
})
setDtReportInfo(renderViewExport, propValue)
}
fun resetProp(renderViewExport: IKuiklyRenderViewExport) {
renderViewExport.view().removeViewData<KuiklyRenderCallback>(KRCssConst.PRE_CLICK)
resetDtReportInfo(renderViewExport.view())
}
private fun setDtReportInfo(renderViewExport: IKuiklyRenderViewExport, propValue: Any) {
val propValueJson = JSONObject(propValue as String)
val propMap = propValueJson.toMap()
propMap.forEach { (propKey, propValue) ->
handlerMap[propKey]?.invoke(renderViewExport.view(), propValue)
}
}
private fun resetDtReportInfo(target: Any) {
DT.reset(target)
}
}
- 宿主端注册实现的自定义属性Handler
// KuiklyRenderActivity.kt
override fun registerViewExternalPropHandler(kuiklyRenderExport: IKuiklyRenderExport) {
super.registerViewExternalPropHandler(kuiklyRenderExport)
with(kuiklyRenderExport) {
viewPropExternalHandlerExport(ViewPropExternalHandler())
}
}
- 验证是否接入成功
参考:https://iwiki.woa.com/p/546285336
事件出口,在report中,输出日志调试
旧版大同SDK
旧版和新版SDK,在Kuikly页面使用的主要区别为:
第二步中自定义属性Handler用于设置页面和元素信息中的 大同SDK 、setProp 和 resetProp 设置不同
- 宿主端集成大同SDK
参考:https://iwiki.woa.com/p/546285336
- 宿主端实现自定义属性Handler用于设置页面和元素信息
在执行Kuikly业务侧上报操作,即调用elementVR(), pageVR()后,相关属性设置会传到宿主端,业务需要在宿主端响应,并调用大同SDK进行设置。
class ViewPropExternalHandler : IKuiklyRenderViewPropExternalHandler {
override fun setViewExternalProp(
renderViewExport: IKuiklyRenderViewExport,
propKey: String,
propValue: Any
): Boolean {
return when (propKey) {
PROP_VR -> {
DtKuiklyAndriod.setProp(renderViewExport, propValue)
true
}
else -> false
}
}
override fun resetViewExternalProp(
renderViewExport: IKuiklyRenderViewExport,
propKey: String
): Boolean {
return when (propKey) {
PROP_VR -> {
DtKuiklyAndriod.resetProp(renderViewExport)
true
}
else -> false
}
}
companion object {
const val PROP_VR = "vr"
}
}
这里简单包装了大同SDK,推荐业务方在使用过程中自行实行相关SDK的调用封装
// 参考代码
// DtKuiklyAndriod.kt
object DtKuiklyAndriod {
private val handlerMap = mutableMapOf<String, (View, Any) -> Unit>()
init {
handlerMap.apply {
put("pageId") { view, pageId -> VideoReport.setPageId(view, pageId as String) }
put("pageParams") { view, pageParams ->
VideoReport.resetPageParams(view)
VideoReport.setPageParams(view, PageParams(pageParams as MutableMap<String, Any>))
}
put("elementId") { view, elementId ->
VideoReport.setElementId(view, elementId as String)
}
put("params") { view, elementParams ->
VideoReport.resetElementParams(view)
VideoReport.setElementParams(view, elementParams as MutableMap<String, Any>)
}
put("identifier") { view, identifier ->
VideoReport.setElementReuseIdentifier(view, identifier as String)
}
}
}
fun setProp(renderViewExport: IKuiklyRenderViewExport, propValue: Any) {
val view = renderViewExport.view()
val layChangedHadCall = view.getViewData<Boolean>(VR_LAYOUT_CHANGE_HAD_CALL) ?: false
if (layChangedHadCall) {
setDtReportInfo(renderViewExport, propValue)
} else {
view.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
override fun onLayoutChange(
p0: View?,
p1: Int,
p2: Int,
p3: Int,
p4: Int,
p5: Int,
p6: Int,
p7: Int,
p8: Int
) { // 等布局出来以后再设置,不然对于动态添加的view,layout还没出来,大同的曝光上报会无效
view.putViewData(VR_LAYOUT_CHANGE_HAD_CALL, true)
view.removeOnLayoutChangeListener(this)
// 内部的点击事件时通过GestureDetector来实现的,因此大同hook不到setOnClickListener
// 这里在点击回调前,手动上报
view.putViewData(KRCssConst.PRE_CLICK, object : KuiklyRenderCallback {
override fun invoke(result: Any?) {
// 点击上报逻辑
VideoReport.reportEvent("dt_clck", renderViewExport.view(), null)
}
})
setDtReportInfo(renderViewExport, propValue)
}
})
}
}
fun resetProp(renderViewExport: IKuiklyRenderViewExport) {
renderViewExport.view().removeViewData<KuiklyRenderCallback>(KRCssConst.PRE_CLICK)
renderViewExport.view().removeViewData<Boolean>(VR_LAYOUT_CHANGE_HAD_CALL)
resetDtReportInfo(renderViewExport.view())
}
private fun setDtReportInfo(renderViewExport: IKuiklyRenderViewExport, propValue: Any) {
val propValueJson = JSONObject(propValue as String)
val propMap = propValueJson.toMap()
propMap.forEach { (propKey, propValue) ->
handlerMap[propKey]?.invoke(renderViewExport.view(), propValue)
}
}
private fun resetDtReportInfo(target: Any) {
VideoReport.resetElementParams(target)
VideoReport.setElementId(target, "")
}
private const val VR_LAYOUT_CHANGE_HAD_CALL = "vr_layout_change_had_call"
}
- 宿主端注册实现的自定义属性Handler
// KuiklyRenderActivity.kt
override fun registerViewExternalPropHandler(kuiklyRenderExport: IKuiklyRenderExport) {
super.registerViewExternalPropHandler(kuiklyRenderExport)
with(kuiklyRenderExport) {
viewPropExternalHandlerExport(ViewPropExternalHandler())
}
}
- 验证是否接入成功
参考:https://iwiki.woa.com/p/546285336
所有大同SDK产生的上报数据,都会通过IReporter接口回调出来,业务方在初始化SDK时需要实现这个接口,并在这实现中调用灯塔SDK相关的方法即可。
可以通过在大同SDK回调接口IDTReport中,输出日志调试
dt_pgin
dt_appin_callfrom=callFromIcon, dt_starttype=0, dt_wxopenid=wxopenid-1, dt_appin_type=0, dt_seqtime=1725881210372, app_bld=1, dt_fchlid=FChml1, dt_cre_pgid=vr_page_none, os_vrsn=Android 11, dt_mchlid=MChnl1, dt_tid=Tid-1, dt_guid=guid-1, dt_usstmp=1726211238952, dt_ref_pgid=vr_page_none, dt_pgstp=1, dt_callfrom=callFromIcon, dt_qq=qq-1, dt_usid=1726211238952880, dt_ussn=1725881210368079, dt_qqopenid=qqopenId-1, realtime=public, dt_omgbzid=omgBizId, dt_appin_callschema=CallScheme, dt_seqid=71, os=1, dt_mainlogin=main-login, udf_kv={"pg_stp":1,"pagein":"pagein","ref_pg":{"pg_stp":0,"pgid":"vr_page_none"},"cre_pg":{"pg_stp":0,"pgid":"vr_page_none"},"pgid":"PageID"}, dt_protoversion=1, dt_wbopenid=wbopenid-1, dt_callschema=CallScheme, nonRealtime=public, dt_simtype=China Mobile, dt_appin_iscold=1, dt_pgid=PageID, dt_oaid=oaid-1, dt_adcode=Adcode-Beijing, dt_wxunionid=wxunionid-1, dt_pg_isreturn=0, dt_ts=1726211248168, dt_accountid=account-id, ui_vrsn=RSR1.210722.003, dt_sdkversion=2437, app_vr=1.0, dt_coldstart=1}
dt_imp
{dt_appin_callfrom=callFromIcon, dt_ele_is_first_imp=1, dt_starttype=0, dt_wxopenid=wxopenid-1, dt_appin_type=0, dt_seqtime=1725881210372, app_bld=1, dt_fchlid=FChml1, dt_cre_pgid=vr_page_none, os_vrsn=Android 11, dt_mchlid=MChnl1, dt_tid=Tid-1, dt_guid=guid-1, dt_usstmp=1726211238952, dt_ref_pgid=vr_page_none, dt_pgstp=1, dt_callfrom=callFromIcon, dt_qq=qq-1, dt_element_params=[{"eid":"ElementID"}], dt_usid=1726211238952880, dt_ussn=1725881210368079, dt_qqopenid=qqopenId-1, realtime=public, dt_omgbzid=omgBizId, dt_eid=ElementID, dt_ele_scroll_flag=0, dt_appin_callschema=CallScheme, dt_seqid=209, os=1, dt_mainlogin=main-login, udf_kv={"eid":"ElementID","cur_pg":{"pg_stp":1,"ref_pg":{"pg_stp":0,"pgid":"vr_page_none"},"cre_pg":{"pg_stp":0,"pgid":"vr_page_none"},"pgid":"PageID"},"imp":"imp"}, dt_protoversion=1, dt_wbopenid=wbopenid-1, dt_ele_is_first_scroll_imp=0, dt_callschema=CallScheme, nonRealtime=public, dt_simtype=China Mobile, dt_appin_iscold=1, dt_pgid=PageID, dt_oaid=oaid-1, dt_adcode=Adcode-Beijing, dt_wxunionid=wxunionid-1, dt_ts=1726211248173, dt_accountid=account-id, ui_vrsn=RSR1.210722.003, dt_sdkversion=2437, app_vr=1.0, dt_coldstart=1}
iOS
- 宿主端集成大同SDK
参考:https://iwiki.woa.com/p/546284990
- 宿主端调用大同SDK进行自定义属性设置
在执行Kuikly业务侧上报操作,即调用elementVR(), pageVR()后,相关属性设置会传到宿主端,业务需要在宿主端响应,并调用大同SDK进行设置。
因为在kotlin里面设置端是vr属性,所以到了宿主侧,kuikly会通过反射寻找css_vr的属性,进行设置调用,因此声明一个UIView的分类,并参考如下实现:
UIView+ECVideoReport.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (ECVideoReport)
@property (nonatomic, strong, nullable) NSString *css_vr;
@end
NS_ASSUME_NONNULL_END
UIView+ECVideoReport.m
#import "UIView+ECVideoReport.h"
#import "UIView+EC.h"
#import "NSObject+RIJCategory.h"
#import <objc/runtime.h>
#import <VideoReport/QLVideoReport.h>
@implementation UIView (ECVideoReport)
- (NSString *)css_vr {
return objc_getAssociatedObject(self, @selector(css_vr));
}
- (void)setCss_vr:(NSString *)css_vr {
if (self.css_vr != css_vr) {
objc_setAssociatedObject(self, @selector(css_vr), css_vr, OBJC_ASSOCIATION_RETAIN);
NSDictionary *params = [css_vr rij_stringToDictionary];
if ([params isKindOfClass:[NSDictionary class]] && (params[@"pageId"] || params[@"elementId"])) {
NSDictionary *pageParams = params[@"pageParams"];
NSDictionary *elementParams = params[@"params"];
if ([[pageParams allKeys] containsObject:VR_COM_ARG_APP_KEY] ||
[[elementParams allKeys] containsObject:VR_COM_ARG_APP_KEY]) {
self.vr_pageId = params[@"pageId"];
self.vr_elementId = params[@"elementId"];
if (pageParams && [pageParams isKindOfClass:[NSDictionary class]]) {
self.vr_setPageParams(pageParams);
}
if (elementParams && [elementParams isKindOfClass:[NSDictionary class]]) {
self.vr_setElementParams(elementParams);
}
// 默认为QLVRViewEndExposurePolicy_None导致元素没有反曝光上报
self.vr_endExposureReportPolicy = QLVRViewEndExposurePolicy_All;
// 默认为QLVRViewExposurePolicy_First会导致元素只曝光一次
self.vr_exposureReportPolicy = QLVRViewExposurePolicy_All;
} else {
self.videoReport.ec_appType = ECReportAppTypeQQLive;
self.videoReport.ec_pageId = params[@"pageId"];
self.videoReport.ec_elementId = params[@"elementId"];
[self.videoReport ec_setElementParams:params[@"params"]];
[self.videoReport ec_setPageParams:params[@"pageParams"]];
}
} else if (!css_vr) {
if (self.videoReport.ec_appType) {
self.videoReport.ec_appType = 0;
if (self.videoReport.ec_pageId) {
self.videoReport.ec_pageId = nil;
}
if (self.videoReport.ec_elementId) {
self.videoReport.ec_elementId = nil;
}
[self.videoReport ec_setElementParams:nil];
[self.videoReport ec_setPageParams:nil];
}
if (self.vr_pageId) {
self.vr_pageId = nil;
self.vr_elementId = nil;
self.vr_setPageParams(nil);
self.vr_setElementParams(nil);
self.vr_endExposureReportPolicy = QLVRViewEndExposurePolicy_None;
self.vr_exposureReportPolicy = QLVRViewExposurePolicy_None;
}
}
}
}
@end
- 验证是否接入成功
参考"10.上报核验"章节https://iwiki.woa.com/p/546284990#10上报核验 验证是否成功
方式一.控制台日志过滤(日志有打印,说明大同SDK采集到了,不代表上报成功,适合开发调试)
1.接入大同sdk的日志
在大同sdk初始化时,将大同sdk的日志接出并打印,见1.3初始化SDK
2.过滤采集到事件日志
(1)针对2.2.5.3之前的版本,关键字 [VR_EVENT]
(2)针对2.2.5.3及之后的版本,关键字 [DT][report]方式二.使用大同平台的可视化联调进行测试(适合开发/测试/产品)。
1.确保app已经接入了可视化联调,见章节一的3.2。
2.进入http://datong.oa.com/, 切换到自己所在业务tab,在可视化联调页面扫码跳转到app进行测试,上报的数据会在网页进行显示
鸿蒙
- 宿主端集成大同SDK
参考:https://git.woa.com/UniversalReport/DTKMM/tree/master
// entry/oh-pachage.json5
{
...
"dependencies": {
...
"dt_data_collector": "1.0.15"
...
}
...
}
# entry/main/cppCmakeLists.tst
...
find_package(dt_data_collector)
target_link_libraries(XXX PUBLIC dt_data_collector::dt_data_collector)
终端执行:ohpm install
// 大同初始化
class DTKuiklyReporter implements DTJSEventReporter {
report(eventKey: string, appKey: string, params: Map<string, string>): void {
// 上报信息
}
const params1 = new Map<string, string>()
const coreConfig = new DTJsCoreConfig.Builder().enableDebug(true)// 启用debug模式,默认为false
.publicParams(params1)// 添加所有事件公参
.enableAppHeartbeatReport(false)// 是否开启App心跳上报
.reporter(new DTKuiklyReporter()) // 添加事件出口
.build()
const config = new DTJsConfig.Builder()
.coreConfig(coreConfig)
.build()
initializeDTCore(this.context, config)
// 初始化大同SDK
initializeDTCamera(this.getUIContext(), CoreType.ETS)
- 宿主端实现自定义属性Handler用于设置页面和元素信息
在执行Kuikly业务侧上报操作,即调用elementVR(), pageVR()后,相关属性设置会传到宿主端,业务需要在宿主端响应,并调用大同SDK进行设置。
#include <string>
#include "nlohmann/json.hpp"
#include "libharmony_render/api/include/kuikly/Kuikly.h"
#include "dt_ark_c_api.h"
bool DTKuiklyPropHandler(void* arkui_handle, const char* propKey, KRAnyData propValue) {
if (strcmp(propKey, "vr") == 0) {
if (KRAnyDataIsString(propValue)) {
// propValueStr为Kuikly传递传递过来的字符串,KRAnyData暂时支持String和Int
/**
* eg:{"elementId": "xxx","params": {"xxx": "xxx","xxx": "xxx"}}
*/
std::string propValueStr(KRAnyDataGetString(propValue));
/**
* 解析字符串获取设置的字段key,value,进行相应的大同API设置
*/
nlohmann::json propValueJson = nlohmann::json::parse(propValueStr);
if (propValueJson.contains("pageId")) {
com::tencent::dt::camera::api::arkc::DT().SetPageId((ArkUI_Node*)arkui_handle, pageId);
}
if (propValueJson.contains("elementId")) {
std::string elementId = propValueJson["elementId"];
com::tencent::dt::camera::api::arkc::DT().SetElementId((ArkUI_Node*)arkui_handle, elementId);
}
/**
* 其他字段的处理...
*/
return true;
}
}
return true;
}
bool DTKuiklyResetPropHandler(void* arkui_handle, const char* propKey) {
if (strcmp(propKey, "vr") == 0) {
com::tencent::dt::camera::api::arkc::DT().Rest((ArkUI_Node*)arkui_handle);
return true;
}
return false;
}
- 宿主端注册实现的自定义属性Handler
static napi_value InitKuikly(napi_env env, napi_callback_info info) {
...
KRRenderViewSetExternalPropHandler(*DTKuiklySetPropHandler, *DTKuiklyResetPropHandler);
// 位于api->kotlin.root.initKuikly()之前;
...
}
- 验证是否接入成功
参考大同输出日志
- 自定义View上报
自定义View实现参照:Kuikly鸿蒙版接入指引
自定义View需要单独对Kuikly传来的参数进行设置
@Observed
export class KRMyView extends KuiklyRenderBaseView {
...
vrMap: Map<string, string> = new Map()
setProp(propKey: string, propValue: KRAny | KuiklyRenderCallback): boolean {
switch(propKey) {
...
case "vr":
let keyValue = JSON.parse(propValue as string) as Record<string,string>;
this.vrMap = new Map(Object.entries(keyValue))
}
...
}
...
}
@Component
export struct KRMy {
@ObjectLink renderView: KRMyView
...
build() {
Element({
dt_eid: this.renderView.vrMap.get("elementId"),
dt_reuse_id: this.renderView.vrMap.get("reuse_id"),
dt_content_id: this.renderView.vrMap.get("content_id")
}) {
...
}
}
...
}