反射埋点方案: 全局点击埋点代理OnClickListener SDK 编写(2)

简介: 简介: 你在开发中是否遇到过这样的场景,当点击同一个dialog或者button的时候,如果暴击多次,该dialog或button的被点击行为会被瞬间执行多次,这时候有小伙伴可能要想了,我可以做一个view时间戳呀,让它延迟生效。

五. SDK拓展采集能力

5.0.1 怎样获取TextView的显示文本?

((TextView) view).getText().toString();

5.0.2 怎样获取ImageView的显示文本信息?

view.getContentDescription().toString();

5.0.3 怎样采集CheckBox的点击事件?

5.0.3.1 自定义WrapperOnCheckedChangeListener并且 织入埋点代码
public class WrapperOnCheckedChangeListener implements CompoundButton.OnCheckedChangeListener {
    private CompoundButton.OnCheckedChangeListener source;
    public WrapperOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener source) {
        this.source = source;
    }
    @Override
    public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
        //调用原有的 OnClickListener
        try {
            if (source != null) {
                source.onCheckedChanged(compoundButton, b);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //插入埋点代码
        SensorsDataPrivate.trackViewOnClick(compoundButton);
    }
}
5.0.3.2 获取 CheckBox 设置的 OnCheckedChangeListener
    /**
     * CheckBox  代理事件
     *
     * @param view compoundButton代理ui
     */
    public void compoundButtonItemClick(View view) {
        if (view instanceof CheckBox){
            final CompoundButton.OnCheckedChangeListener onCheckedChangeListener =
                    SensorsDataHelper.getOnCheckedChangeListener(view);
            if (onCheckedChangeListener != null &&
                    !(onCheckedChangeListener instanceof MkOnCheckedChangeListener)) {
                ((CompoundButton) view).setOnCheckedChangeListener(
                        new MkOnCheckedChangeListener(onCheckedChangeListener));
            }
        }
    }

5.0.4 怎样采集RadioGroup的点击事件?

  • 5.0.4.1 确定 RadioGroup 设置的Listener类型OnGroupClickListener,然后自定义MkOnGroupClickListener
public class MkOnGroupClickListener implements ExpandableListView.OnGroupClickListener {
    private ExpandableListView.OnGroupClickListener source;
    public MkOnGroupClickListener(ExpandableListView.OnGroupClickListener source) {
        this.source = source;
    }
    @Override
    public boolean onGroupClick(ExpandableListView expandableListView, View view, int groupPosition, long id) {
        SensorsDataManager.trackAdapterView(expandableListView, view, groupPosition, -1);
        if (source != null) {
            source.onGroupClick(expandableListView, view, groupPosition, id);
        }
        return false;
    }
}
  • 5.0.4.2 判断当前ViewRadioGroup
 if (view instanceof RadioGroup) {
            try {
                RadioGroup radioGroup = (RadioGroup) view;
                Activity activity = SensorsDataHelper.getActivityFromView(view);
                if (activity != null) {
                    int checkedRadioButtonId = radioGroup.getCheckedRadioButtonId();
                    RadioButton radioButton = activity.findViewById(checkedRadioButtonId);
                    if (radioButton != null) {
                        text = radioButton.getText().toString();
                    }
                }
            } catch (Exception e) {
                Log.getStackTraceString(e);
            }
        }
  • 5.0.4.3如果反射已经设置了mOnClickListener,且mOnClickListener不为空,比去不是我们自定义的MkOnGroupClickListener
  • 通过MkOnGroupClickListener代理
     if (onGroupClickListener != null && !(onGroupClickListener instanceof MkOnGroupClickListener)) {
                   ((ExpandableListView) view).setOnGroupClickListener(new MkOnGroupClickListener(onGroupClickListener));
               }

5.0.5 怎样采集RattingBar的点击事件?

5.0.6 怎样采集 SeekBar 的点击事件?

    public void seekBarItemClick(View view) {
        final SeekBar.OnSeekBarChangeListener onSeekBarChangeListener =
                SensorsDataHelper.getOnSeekBarChangeListener(view);
        if (onSeekBarChangeListener != null &&
                !(onSeekBarChangeListener instanceof MkOnSeekBarChangeListener)) {
            ((SeekBar) view).setOnSeekBarChangeListener(
                    new MkOnSeekBarChangeListener(onSeekBarChangeListener));
        }
    }

5.0.7 怎样采集 Spinner 的点击事件?

5.0.8 怎样采集 ListView ,GridView 的点击事件?

  ListView ,GridViewAdapterView子类,设置代理逻辑判断都大同小异

  • 5.0.8.1 设置 ListView/ GridView的代理MkAdapterViewOnItemClick
    /**
     * 列表代理事件
     *
     * @param view gridView代理ui
     */
    public void gridViewItemClick(View view) {
        if (view instanceof ListView || view instanceof GridView){
            AdapterView.OnItemClickListener onItemClickListener = ((AdapterView) view).getOnItemClickListener();
            if (onItemClickListener != null && !(onItemClickListener instanceof MkAdapterViewOnItemClick)) {
                ((AdapterView) view).setOnItemClickListener(new MkAdapterViewOnItemClick(onItemClickListener));
            }
        }
    }
  • 5.0.8.1 自定义 MkAdapterViewOnItemClick源码如下:
public class MkAdapterViewOnItemClick implements AdapterView.OnItemClickListener {
    private AdapterView.OnItemClickListener source;
    public MkAdapterViewOnItemClick(AdapterView.OnItemClickListener source) {
        this.source = source;
    }
    @Override
    public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
        if (source != null) {
            source.onItemClick(adapterView, view, position, id);
        }
        SensorsDataManager.trackAdapterView(adapterView, view, position);
    }
}

5.0.9 怎样采集 ExpandableListView的点击事件?

   ExpandableListViewAdapterView的子类,同时也是ListView的子类,ListView的点击事件分为GroupClickChildClick,它设置的监听器也有两种,这里我们就需要增加额外的方式去做处理啦,我们来看看 ExpandableListView的点击事件是如何处理的吧

    public static void trackAdapterView(AdapterView<?> adapterView, View view, int groupPosition, int childPosition) {
        try {
            final JSONObject jsonObject = new JSONObject();
            jsonObject.put(ITrackClickEvent.CANONICAL_NAME, adapterView.getClass().getCanonicalName());
            jsonObject.put(ITrackClickEvent.ELEMENT_ID, SensorsDataHelper.getViewId(adapterView));
            if (childPosition > -1) {
                jsonObject.put(ITrackClickEvent.ELEMENT_POSITION, String.format(Locale.CHINA, "%d:%d", groupPosition, childPosition));
            } else {
                jsonObject.put(ITrackClickEvent.ELEMENT_POSITION, String.format(Locale.CHINA, "%d", groupPosition));
            }
            StringBuilder stringBuilder = new StringBuilder();
            String viewText = SensorsDataHelper.traverseViewContent(stringBuilder, view);
            if (!TextUtils.isEmpty(viewText)) {
                jsonObject.put(ITrackClickEvent.ELEMENT_ELEMENT, viewText);
            }
            final Activity activity = SensorsDataHelper.getActivityFromView(adapterView);
            if (activity != null) {
                jsonObject.put(ITrackClickEvent.ACTIVITY_NAME, activity.getClass().getCanonicalName());
            }
            SensorsReporter.getSensorsDataApiInstance().track(ITrackClickEvent.APP_CLICK, jsonObject);
        } catch (Exception e) {
            Log.getStackTraceString(e);
        }
    }

那么我们是如何获取ExpandableListView的呢?这里主要体现在 $element_position这个参数上,我们需要根据是group或者child做不同的逻辑处理

        /**
     * ExpandableListView代理事件
     *
     * @param view ExpandableListView代理view
     */
    public void expandableItemClick(View view) {
       if (view instanceof  ExpandableListView){
           try {
               final Class viewClazz = Class.forName("android.widget.ExpandableListView");
               // ---------------------------------------Child---------------------------------------
               Field mOnChildClickListenerField = viewClazz.getDeclaredField("mOnChildClickListener");
               if (!mOnChildClickListenerField.isAccessible()) {
                   mOnChildClickListenerField.setAccessible(true);
               }
               ExpandableListView.OnChildClickListener onChildClickListener =
                       (ExpandableListView.OnChildClickListener) mOnChildClickListenerField.get(view);
               if (onChildClickListener != null && !(onChildClickListener instanceof MkOnChildClickListener)) {
                   ((ExpandableListView) view).setOnChildClickListener(new MkOnChildClickListener(onChildClickListener));
               }
               // ---------------------------------------Group---------------------------------------
               Field mOnGroupClickListenerField = viewClazz.getDeclaredField("mOnGroupClickListener");
               if (!mOnGroupClickListenerField.isAccessible()) {
                   mOnGroupClickListenerField.setAccessible(true);
               }
               ExpandableListView.OnGroupClickListener onGroupClickListener =
                       (ExpandableListView.OnGroupClickListener) mOnGroupClickListenerField.get(view);
               if (onGroupClickListener != null && !(onGroupClickListener instanceof MkOnGroupClickListener)) {
                   ((ExpandableListView) view).setOnGroupClickListener(new MkOnGroupClickListener(onGroupClickListener));
               }
           } catch (Exception e) {
               Log.getStackTraceString(e);
           }
       }
    }

先通过反射获取mOnGroupClickListener对象和mOnChildClickListener,如果listener不为空,并且不是我们需要的Listener类型,那么分别通过自定义的的MkOnGroupClickListenerMkOnChildClickListener区代理完成。

5.0.9.1 OnGroupClickListene源码如下:
/**
 * @author 杨正友(小木箱)于 2020/10/4 15 22 创建
 * @Email: yzy569015640@gmail.com
 * @Tel: 18390833563
 * @function description:
 */
public class MkOnGroupClickListener implements ExpandableListView.OnGroupClickListener {
    private ExpandableListView.OnGroupClickListener source;
    public MkOnGroupClickListener(ExpandableListView.OnGroupClickListener source) {
        this.source = source;
    }
    @Override
    public boolean onGroupClick(ExpandableListView expandableListView, View view, int groupPosition, long id) {
        SensorsDataManager.trackAdapterView(expandableListView, view, groupPosition, -1);
        if (source != null) {
            source.onGroupClick(expandableListView, view, groupPosition, id);
        }
        return false;
    }
}

在其onGroupClick方法内部,我们首先调用埋点代码,然后调用原有的listenerOnGroupClickListener方法,这样即可实现插入埋点代码的效果

5.0.9.2 OnChildClickClickListener源码如下:
/**
 * @author 杨正友(小木箱)于 2020/10/4 15 21 创建
 * @Email: yzy569015640@gmail.com
 * @Tel: 18390833563
 * @function description:
 */
public class MkOnChildClickListener implements ExpandableListView.OnChildClickListener {
    private ExpandableListView.OnChildClickListener source;
    public MkOnChildClickListener(ExpandableListView.OnChildClickListener source) {
        this.source = source;
    }
    @Override
    public boolean onChildClick(ExpandableListView expandableListView, View view, int groupPosition, int childPosition, long id) {
        SensorsDataManager.trackAdapterView(expandableListView, view, groupPosition, childPosition);
        if (source != null) {
            return source.onChildClick(expandableListView, view, groupPosition, childPosition, id);
        }
        return false;
    }
}

5.1.0 怎样采集 Dialog 的点击事件?

   目前这种全埋点的方案是无法采集activity上游离的view,如: Dialog ,因为无法遍历到被点击的view。对于这样的dialog,我们可以通过如下方式解决

    /**
     * Track Dialog 的点击
     * @param activity Activity
     * @param dialog Dialog
     */
    public void trackDialog(@NonNull final Activity activity, @NonNull final Dialog dialog) {
        if (dialog.getWindow() != null) {
            dialog.getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    SensorsDataManager.delegateViewsOnClickListener(activity, dialog.getWindow().getDecorView());
                }
            });
        }
    }

然后在Dialogshow之前调用即可

        AlertDialog alertDialog = new AlertDialog.Builder(this)
                .setNegativeButton("取消", (dialog, which) -> {})
                .setPositiveButton("确定", (dialog, which) -> {})
                .setTitle("小木箱").setMessage("一定要加油努力哦~").create();
        SensorsReporter.getSensorsDataApiInstance().trackDialog(this,alertDialog);

六. SDK缺陷

6.1 由于使用反射,效率比较低,对App的整体性能有一定影响,也可能会伴随着一些兼容问题

6.2 无法直接采集游离与activity上的view的点击,比如dialog,popuWindow

七. 参考资料

目录
打赏
0
0
0
0
2
分享
相关文章
鸿蒙HarmonyOS埋点SDK,ClkLog适配鸿蒙埋点分析
ClkLog鸿蒙埋点SDK通过手动埋点的方式实现HarmonyOS 原生应用的前端数据采集。快速接入即可获取埋点数据,同时支持分析功能(基础统计分析、自定义分析、用户画像等)。
183 59
大牛直播SDK在四足机器人和无人机巡检中的创新应用方案
在工业4.0和智能化浪潮下,传统巡检方式正经历深刻变革。四足机器人与无人机凭借灵活机动性和高效巡检能力崭露头角,而大牛直播SDK则赋予其实时直播与智能互动功能。本文介绍大牛直播SDK的核心优势、在四足机器人和无人机巡检中的应用方案,以及技术实现要点和未来展望,展示智能巡检的广阔前景。
112 6
神策SDK不支持Windows客户端全埋点,怎么实现用户统计分析?
本文将介绍,ClkLog针对神策不支持全埋点的客户端实现用户访问基础统计分析 1。
神策SDK不支持Windows客户端全埋点,怎么实现用户统计分析?
云桌面系统镜像文件快速分发方案分享SDK
为了解决云桌面环境下批量升级系统镜像的效率问题,传统的1对多FTP/HTTP方式因服务器带宽限制导致传输慢。一种基于优化的Bittorrent协议的P2P解决方案被提出,利用P2P技术将文件切块并让终端互相分享,提高下载速度,尤其适合大文件如256GB分区镜像的分发。通过自定义IO接口、跳过校验、超大分块、多分块支持及局域网自建Tracker等功能,实现更快的传输和镜像更新,适用于系统镜像、游戏更新等领域。该方案已广泛应用于各行业,可根据不同场景定制优化。
196 1
通用快照方案问题之快照SDK的安装如何解决
通用快照方案问题之快照SDK的安装如何解决
91 0
反射埋点方案: 全局点击埋点代理OnClickListener SDK 编写
反射埋点方案: 全局点击埋点代理OnClickListener SDK 编写
69 0
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
307 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
[Android][Framework]系统jar包,sdk的制作及引用
[Android][Framework]系统jar包,sdk的制作及引用
281 0
|
8月前
|
Android SDK
【10月更文挑战第21天】
214 1
Android|使用阿里云推流 SDK 实现双路推流不同画面
本文记录了一种使用没有原生支持多路推流的阿里云推流 Android SDK,实现同时推送两路不同画面的流的方法。
161 7
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问