大同上报

大约 9 分钟

大同上报

Kuikly

  1. 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"
}
  1. 在元素页面设置相应的上报信息

在attr内调用绑定的大同上报方法

override fun body(): ViewBuilder {
    val ctx = this
    return {
        attr {
            pageVR("PageID")
        }
        ...
        
        Text {
            attr {
                elementVR("elementID")
            }
        }
        ...
    }
}

安卓

新版大同SDK

  1. 宿主端集成大同SDK

参考:

https://git.woa.com/UniversalReport/DTKMM/tree/masteropen in new window

大同SDK版本需要使用:

com.tencent.dt:core:1.0.16-SNAPSHOT
  1. 宿主端实现自定义属性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)
    }
}
  1. 宿主端注册实现的自定义属性Handler
// KuiklyRenderActivity.kt
    override fun registerViewExternalPropHandler(kuiklyRenderExport: IKuiklyRenderExport) {
        super.registerViewExternalPropHandler(kuiklyRenderExport)
        with(kuiklyRenderExport) {
            viewPropExternalHandlerExport(ViewPropExternalHandler())
        }
    }
  1. 验证是否接入成功

参考:https://iwiki.woa.com/p/546285336open in new window

事件出口,在report中,输出日志调试

旧版大同SDK

旧版和新版SDK,在Kuikly页面使用的主要区别为:

第二步中自定义属性Handler用于设置页面和元素信息中的 大同SDK 、setProp 和 resetProp 设置不同

  1. 宿主端集成大同SDK

参考:https://iwiki.woa.com/p/546285336open in new window

  1. 宿主端实现自定义属性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"
}
  1. 宿主端注册实现的自定义属性Handler
// KuiklyRenderActivity.kt
    override fun registerViewExternalPropHandler(kuiklyRenderExport: IKuiklyRenderExport) {
        super.registerViewExternalPropHandler(kuiklyRenderExport)
        with(kuiklyRenderExport) {
            viewPropExternalHandlerExport(ViewPropExternalHandler())
        }
    }
  1. 验证是否接入成功

参考:https://iwiki.woa.com/p/546285336open in new window

所有大同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

  1. 宿主端集成大同SDK

参考:https://iwiki.woa.com/p/546284990open in new window

  1. 宿主端调用大同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
  1. 验证是否接入成功

参考"10.上报核验"章节https://iwiki.woa.com/p/546284990#10上报核验open in new window 验证是否成功

方式一.控制台日志过滤(日志有打印,说明大同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进行测试,上报的数据会在网页进行显示

鸿蒙

  1. 宿主端集成大同SDK

参考:https://git.woa.com/UniversalReport/DTKMM/tree/masteropen in new window

// 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)

  1. 宿主端实现自定义属性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;
}

  1. 宿主端注册实现的自定义属性Handler
static napi_value InitKuikly(napi_env env, napi_callback_info info) {
...

    KRRenderViewSetExternalPropHandler(*DTKuiklySetPropHandler, *DTKuiklyResetPropHandler);

    // 位于api->kotlin.root.initKuikly()之前;
 
    ...
}

  1. 验证是否接入成功

参考大同输出日志

  1. 自定义View上报

自定义View实现参照:Kuikly鸿蒙版接入指引open in new window

自定义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")
        }) {
            
            ...
            
        }
      
    }
        
    ...  
}

上次编辑于:
Kuikly AI助手