Flutter 无埋点SDK实现

Flutter之全埋点思考与实现 唯一性』非常好理解,因为对于任意元素而言,其 ID 应该是与其他所有元素都不同的,这样我们才能根据 ID 唯一标识出那个我们想要的元素,采集上来的数据才是准确的,不重复的。所以转换的顺序应该是修改普通构造在最前面,其次是处理常量声明表达式,最后是Aspectd自己的转换。根据"唯一性"与"稳定性",将页面所在类的类型作为ID,它是相对唯一的,除了页面复用,基本不存在其他类名相同的页面(不同的package例外),其次它是相对稳定的,除了修改类名情况下才会改变,除了一些页面重大的改版之外不会轻易修改类名。 阅读详情

前言

先看下各个平台自动化埋点支持

平台

特点

自动化埋点方案

Android

Java支持编译器静态代理/运行期动态代理

有成熟方案和产品,例如GrowingIO、神策、友盟

iOS

OC提供了强大的运行时和动态性

有成熟方案和产品,例如GrowingIO、神策、友盟

Flutter

Dart反射支持很弱,Flutter禁用了反射机制,考虑从编译期代码插桩实现

无成熟方案和产品

从编译期进行代码插桩,则需要修改编译期的中间件文件。

Dart文件编译会先编译成Dill文件,然后再编译成二进制代码。

如果能在编译器拿到Dill文件,然后进行修改插桩,再进行编译成Binary Code就可以达到AOP埋点的效果

flutter_tool是flutter的编译工具,其并没有提供接口供开发者hook,以及修改编译流程,那么要实现这个步骤,我们就需要修改flutter_tool这个工具。

闲鱼的AspectD就使用了这个思想GitHub - XianyuTech/aspectd: AOP for Flutter(Dart)

基于闲鱼的ApectD来开展后续的工作,这里的 Flutter SDK 完全依赖于原生 SDK,不具有单独运行的能力。

AspectD的使用

首先配置好flutter环境(flutter sdk、dart sdk、fvm、环境变量等),这里使用版本信息如下:

    • Flutter version 2.2.2 at /Users/sheng/GrowIO/flutter
    • Framework revision d79295af24 (4 months ago), 2021-06-11 08:56:01 -0700
    • Engine revision 91c9fc8fe0
    • Dart version 2.13.3
    • Pub download mirror https://pub.flutter-io.cn
    • Flutter download mirror https://storage.flutter-io.cn

1. 下拉aspectd仓库

这里我们对aspectd进行了部分修改,以满足我们的无埋点要求。

git clone https://github.com/growingio/aspectd.git

2. 修改build_tool,通过git patch方式

  • git patch

cd path-for-flutter-git-repo
git apply --3way path-for-aspectd-package/0001-aspectd.patch
rm bin/cache/flutter_tools.stamp

path-for-flutter-git-repo表示flutter的路径

path-for-aspectd-package表示aspectd的路径

这里可能会存在git apply错误的情况,可以打开0001-aspectd.patch文件,根据变动自行添加修改。

AspectD通过改写Flutter中的flutter_tools进行修改Dill文件,变动了两个文件:

  • flutter/packages/flutter_tools/lib/src/aspectd.dart 添加

  • flutter/packages/flutter_tools/lib/src/build_system/targets/common.dart 修改

AspectD通过git patch方式,给flutter的分支添加了这些变动。

  • 环境设置

在 ~/.bash_profile 文件中添加

export PUB_HOSTED_URL=https://pub.flutter-io.cn //国内用户需要设置
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn //国内用户需要设置

注:git@github.com: Permission denied 问题,需要你设置ssl证书

然后在aspectd根目录执行 flutter pub get

sheng@chengpengdeMacBook-Pro aspectd % flutter pub get
[KWLM]:pub get
Warning: You are using these overridden dependencies:
! kernel 0.0.0 from git git@github.com:XianyuTech/sdk.git at c9f1a5 in pkg/kernel
! meta 1.3.0 from git git@github.com:XianyuTech/sdk.git at c9f1a5 in pkg/meta
Running "flutter pub get" in aspectd...                            744ms
Running "flutter pub get" in example...                             7.8s

显示我们修改了kernel依赖。

3. 运行example

aspectd/ 、aspectd/aspectd_impl/ 、aspectd/example/ 这3个目录我们都需要进行 flutter pub get

然后进入到aspectd源码目录的example中执行:flutter run --debug --verbose ,也可以直接Android Studio中打开,运行Main

如果/aspectd/lib/src/flutter_frontend_server/下生成了frontend_server.dart.snapshot则表示此次编译 aspectd_impl 成功了。

你也可以不使用flutter run来生成frontend_server.dart.snapshot,使用下面的命令

dart --deterministic --packages=/Users/sheng/GrowIO/aspectd-ex/lib/src/flutter_frontend_server/package_config.json --snapshot=/Users/sheng/GrowIO/aspectd-ex/lib/src/flutter_frontend_server/frontend_server.dart.snapshot --snapshot-kind=kernel /Users/sheng/GrowIO/aspectd-ex/lib/src/flutter_frontend_server/starter.dart

如果frontend_server.dart.snapshot没有生成,运行会显示build完成,但是无法运行。

Launching lib/main.dart on iPhone SE (2nd generation) in debug mode...
lib/main.dart:1
Xcode build done.                                           23.1s
Failed to build iOS app
Error output from Xcode build:
↳
    ** BUILD FAILED **
Xcode's output:
↳
    /Users/sheng/GrowIO/aspectd/aspectd_impl/.packages does not exist.
    Did you run "flutter pub get" in this directory?
    Command PhaseScriptExecution failed with a nonzero exit code
    note: Using new build system
    note: Building targets in parallel
    note: Planning build
    note: Constructing build description
    warning: The iOS Simulator deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 9.0 to 14.0.99. (in target 'Runner' from project 'Runner')
Could not build the application for the simulator.
Error launching application on iPhone SE (2nd generation).
Exited (sigterm)

值得注意的是,

  • aspectd/ 对应调试aspectd的transform代码部分工程,即修改/aspectd/lib/src/ 下的代码是需要该工程的

  • aspectd/aspectd_impl/ 是添加的 Hook 相关的代码部分

  • aspectd/example/ 则是工程demo

每次修改 aspectd hook 相关的代码需要先执行 flutter clean ,再进行编译。

4. hook相关

  • hook写在哪里,怎么hook?

参考aspectd的 README,至此apsectd的集成就告一段落。

自动化埋点

由于Flutter可以依赖于原生SDK,原生SDK包含事件发送逻辑,网络传输逻辑,并且发送 App打开关闭事件、 App访问事件 、自定义事件等,那么Flutter部分只需要传递如下事件到原生SDK:

  1. 点击元素事件

  2. 元素内容改变事件

  3. 页面曝光事件

点击事件

对于点击事件,则需要hook点击触发方法,暂时分成两步,在以下时机进行切面

  /// click event aop step 1
  /// hittest
@Call("package:flutter/src/gestures/hit_test.dart", "HitTestTarget",
      "-handleEvent")
  /// click event aop step 2
  /// callback
@Call("package:flutter/src/gestures/recognizer.dart", "GestureRecognizer",
      "-invokeCallback")

关于路径Path的获取,则是通过 Element 中向上遍历父级元素 visitAncestorElements 方法,将一整条元素链存入数组。

相关代码可以去仓库 ​aspectd/growing_aop_impl.dart at master · growingio/aspectd 查看。

路径过滤

从上述方法 中最终获取到的路径 Path 包含多余的系统元素,例如:

MyHomePage/Semantics/Builder/RepaintBoundary/IgnorePointer/AnimatedBuilder/Stack/DecoratedBox/DecoratedBoxTransition/FractionalTranslation/SlideTransition/FractionalTranslation/SlideTransition/CupertinoPageTransition/AnimatedBuilder/RepaintBoundary/Semantics/FocusScope/Actions/PageStorage/Offstage/Semantics/TickerMode/Overlay/Semantics/FocusScope/AbsorbPointer/Listener/HeroControllerScope/Navigator/IconTheme/IconTheme/CupertinoTheme/Theme/AnimatedTheme/Builder/DefaultTextStyle/CustomPaint/Banner/CheckedModeBanner/Title/Directionality/Semantics/Localizations/MediaQuery/Focus/FocusTraversalGroup/Actions/Semantics/Focus/Shortcuts/WidgetsApp/HeroControllerScope/ScrollConfiguration/MaterialApp/MyApp/[root]

我们需要过滤掉系统元素,参考 Flutter Inspector 工具的实现以及 /kernel/lib/transformations/track_widget_constructor_locations.dart /flutter/packages/flutter/lib/src/widgets/widget_inspector.dart 文件代码,其实现逻辑为:

/kernel/lib/transformations/track_widget_constructor_locations.dart 文件在编译期通过一个 transformer 使得所有的 widget 实现了抽象类 _HasCreationLocation ,_HasCreationLocation 包含了文件位置信息,如果文件是否是用户自己创建,则会记录进Path,但这个功能只会在debug模式下启用,所以我们需要自己实现,那么参考 track_widget_constructor_locations 实现,我们需要通过AspectD插入一个transformer,来完成所有的 widget 实现了抽象类_HasCreationLocation 的操作。

这里提供了自己实现的代码供参考: ​aspectd/track_widget_custom_location.dart at master · growingio/aspectd

Transformer实现

对track_widget_constructor_locations 实现介绍

_CustomHasCreationLocation 对应 _HasCreationLocation ,因为我们不能和Inspector一致,同理还有 _creationLocationParameterName 以及 _locationFieldName 。

抽象类 _CustomHasCreationLocation 其实是外部实现的,示例中写在 growing_impl.dart 文件中,在 _resolveFlutterClasses 方法中会判断路径,来获取该抽象类。

​aspectd/growing_impl.dart at master · growingio/aspectd

​aspectd/track_widget_custom_location.dart at master · growingio/aspectd

再就是 RootUrl 的判断,Flutter Inspector 中通过 /flutter/packages/flutter/lib/src/widgets/widget_inspector.dart 来获取 RootUrl 的,这里我们暂时在 track_widget_constructor_locations 中保存 main.dart 的路径前段,来判断是否是用户的工程创建,然后将这个 RootUrl 保存在了 _CustomLocation 实体中,具体可以在 _constructLocation 查看

ConstructorInvocation _constructLocation(
  Location location, {
  String name,
  ListLiteral parameterLocations,
  bool showFile: true,
}) {
  final List<NamedExpression> arguments = <NamedExpression>[
    new NamedExpression('line', new IntLiteral(location.line)),
    new NamedExpression('column', new IntLiteral(location.column)),
    new NamedExpression('rootUrl', new StringLiteral(_rootUrl)),
  ];

然后以此判断是否是自己创建

bool _isLocalElement(Element element) {
    Widget widget = element.widget;
    if (widget is _CustomHasCreationLocation) {
      _CustomHasCreationLocation creationLocation =
      widget as _CustomHasCreationLocation;
      if (creationLocation._customLocation.isProjectRoot()) {
        return true;
      }
    }
    return false;
  }

最终过滤多余 Element 后,Path 路径如下:

MyApp/MaterialApp/MyHomePage/Scaffold/Center/Column/GestureDetector/Text

元素内容改变事件

对于此类事件,暂时只对常见文本框进行了处理,对文本内容改变的方法进行了切面:

  /// text value changed
  /// EditableTextState
  @Execute("package:flutter/src/widgets/editable_text.dart", "EditableTextState",
      "-updateEditingValue")

代码链接:​aspectd/growing_aop_impl.dart at master · growingio/aspectd

除了改变的 Text 内容,还需要 路径Path 以及当前 页面Page 等关键信息, 路径Path 可以参考元素的点击事件处理,页面Page信息则需要我们自己记录堆栈。

页面曝光事件

在阅读Flutter源码过程中,是有一个类似储存页面堆栈的机制的,叫做 RouteEntry ,我们可以依此展开。

这里为了获取上下文信息,又对 buildPage 方法进行了切面。

  /// 1. Page Push - get only RouteEntry
  @Execute("package:flutter/src/widgets/navigator.dart", "_RouteEntry", "-handlePush")
  /// 2. Page Pop - get only RouteEntry
  @Execute(
      "package:flutter/src/widgets/navigator.dart", "_RouteEntry", "-handlePop")
  /// 3. Page Build
  /// can get context and widget
  @Execute("package:flutter/src/material/page.dart",
      "MaterialRouteTransitionMixin", "-buildPage")
  /// 4. Page Build
  /// can get context and widget
  @Execute("package:flutter/src/cupertino/route.dart",
      "CupertinoRouteTransitionMixin", "-buildPage")

具体代码可以查看:​aspectd/growing_aop_impl.dart at master · growingio/aspectd

然后再在对应的方法中,记录页面信息以及页面堆栈,既可以达到我们预想的效果。具体代码参考 ​aspectd/growing_aop_impl.dart at master · growingio/aspectd 中 handlePush 、handleBuildPage、handlePop 的处理。

可视化埋点(圈选)

可视化埋点需要遍历页面所有元素,并将可以选择的元素上传,依于之前的操作,我们已经做了页面的存储,则可以通过页面子元素的遍历,遍历页面上所有元素信息。此外,也需要监听页面变动,以选择合适的时机来遍历。

Flutter 每次元素变动,或者刷新会触发 DrawFrame 方法

  /// Draw Frame - 每次变动刷新
  /// SchedulerBinding:support window.onBeginFrame/window.onDrawFrame call back
  @Execute("package:flutter/src/scheduler/binding.dart", "SchedulerBinding",
      "-handleDrawFrame")

在此方法中,通过 Element 的 visitChildElements 方法遍历所有子元素,同时过滤系统元素,则可以达到我们想要的效果。

  void webcircleSend() {
    /// 圈选遍历逻辑
    if (GrowingAutotracker.getInstance().webCircleRunning) {
      if (pageList.isEmpty) {
        GIOLogger.debug(
            "handleDrawFrame webcircle error : no found page entry");
        return;
      }
      GrowingPageEntry entry = pageList.last;
      entry.context.visitChildElements((element) {
        traverseElement(element, entry.context as Element, false, 0);
      });

      circleElments.forEach((child) {
        GIOLogger.debug("circleElement : " + child.toString());
      });
      Map<String, dynamic> map = <String, dynamic>{};
      Map<String, dynamic> page = <String, dynamic>{};

      /// translate entry to map
      List<Map> elements = <Map>[];
      circleElments.forEach((element) {
        elements.add(element.toMap());
      });
      map["elements"] = elements;

      var element = entry.context as Element;
      final RenderBox box = element.renderObject as RenderBox;
      final size = box.size;
      final offset = box.localToGlobal(Offset.zero);
      MediaQueryData queryData = MediaQueryData.fromWindow(ui.window);
      if (queryData.devicePixelRatio > 1) {
        page["left"] = offset.dx*queryData.devicePixelRatio;
        page["top"] = offset.dy*queryData.devicePixelRatio;
        page["width"] = size.width*queryData.devicePixelRatio;
        page["height"] = size.height*queryData.devicePixelRatio;
      } else {
        page["left"] = offset.dx;
        page["top"] = offset.dy;
        page["width"] = size.width;
        page["height"] = size.height;
      }
      page["path"] = _getPagePath(entry);
      page["title"] = entry.titile;
      page["isIgnored"] = false;

      /// pages
      map["pages"] = <Map>[page];
      GrowingAutotracker.getInstance().flutterWebCircleEvent(map);
      GIOLogger.debug('handleDrawFrame circle ' + map.toString());
      circleElments.clear();
    }
  }

  void traverseElement(Element element,Element parent, bool isIgnored, int z) {
    // GIOLogger.debug("reversedObjc " + element.widget.runtimeType.toString());
    if (_isLocalElement(element)) {
      String? elementType = null;
      if (element.widget is IgnorePointer) {
        /// ignorePointer will ignore all subtree if is ignoring
        IgnorePointer widget = element.widget as IgnorePointer;
        if (widget.ignoring) {
          element.visitChildElements((child) {
            traverseElement(child,element, true,z++);
          });
          return;
        }
      }else if (element.widget is RawMaterialButton || element.widget is MaterialButton || element.widget is FloatingActionButton || element.widget is AppBar) {
        /// because of is local element, Gesture is create by system
        /// RawMaterialButton is super class of RaisedButton、FlatButton、OutlineButton
        // [RawMaterialButton,MaterialButton,FloatingActionButton].takeWhile((e) => element.widget is e).isNotEmpty;
        elementType = "BUTTON";
      }else if (element.widget is TextFormField || element.widget is TextField) {
        elementType = "INPUT";
      }else if (element.widget is ListView || element.widget is CustomScrollView || element.widget is SingleChildScrollView || element.widget is GridView) {
        elementType = "LIST";
      }else if (parent.widget is GestureDetector) {
        /// gesture click enable
        elementType = "TEXT";
      }

      if (elementType != null) {
        GrowingCircleElement circle = GrowingCircleElement();
        final RenderBox box = element.renderObject as RenderBox;
        final size = box.size;
        final offset = box.localToGlobal(Offset.zero);
        MediaQueryData mediaQuery = MediaQueryData.fromWindow(ui.window);
        if (mediaQuery.devicePixelRatio > 1) {
          circle.rect = Rect.fromLTWH(offset.dx*mediaQuery.devicePixelRatio, offset.dy*mediaQuery.devicePixelRatio, size.width*mediaQuery.devicePixelRatio, size.height*mediaQuery.devicePixelRatio);
        }else {
          circle.rect = Rect.fromLTWH(offset.dx, offset.dy, size.width, size.height);
        }
        var parser = GrowingElementParser(element, currentPage());
        var parentParser = GrowingElementParser(parent, currentPage());
        circle.xpath = parser.xpath;
        circle.parentXPath = parentParser.xpath;
        circle.content = parser.content;
        circle.index = parser.index;
        circle.page = _getPagePath(currentPage());
        circle.zLevel = z;
        circle.isContainer = false;
        circle.isIgnored = isIgnored;
        circle.nodeType = elementType;
        circleElments.add(circle);
      }
      element.visitChildElements((child) {
        traverseElement(child,element, isIgnored,z++);
      });
    }else {
      element.visitChildElements((child) {
        traverseElement(child,parent, isIgnored,z++);
      });
    }


  }

然后将所有信息传输至原生SDK,由原生SDK进行发送。

结尾

此部分代码仍在开发中,可视化埋点部分在iOS上初步顺利,在Android平台上仍有截图黑屏,对原生SDK代码侵入性较强等问题,同时依赖aspectd的方式,也让用户集成会更加困难,也是一个需要考虑的问题。

 

Flutter之全埋点思考与实现,从入门到精通的Android进阶学习笔记整理 目前公司App产品在未引入Flutter之前,一直采用纯原生的埋点功能,原生的实现方案相对来说是比较完善的,采用混合开发后,随着迭代后Flutter模块的增多,仅仅在Flutter侧以代码埋点通过channel调用原生模块埋点接口的方式以逐渐不能满足我们的需求,即使这种方式能精准采集到需要的信息,但是对于App产品来说,耗费的成本逐渐增大,运营、开发、测试,都需要参与,沟通成本也逐渐增大。一旦有了页面的唯一ID的生成规则,我们就可以在页面曝光的时候,去生成这个ID,然后上传即可实现页面的PV、UV指标。 阅读详情

相关推荐

电商平台大数据分析指标

一、五大关键数据分析指标 1.1活跃用户量 活跃用户量是一个基本的指标,有 DAU(日活跃用户)、WAU(周活跃用户)和 MAU(月活跃用户)三个层次; 1.2转化 转化是一个非常重要的指标,电商运营需要关注主路径、次路径甚至精细到每一个品类 / SKU 的转化率; 1.3留存 留存要从不同的时间周期上研究,包括次日留存率、3 日、7 日、30 日留存; 1.4复购 复购则要从 3 个角度去看,复购用户量、复购率和复购金额比; 1.5GMV GMV 是最重要的指标,我们的运营最终是围绕这

默主归沙的博客 4818

Flutter SDK安装

SDK下载安装 Flutter集成了Dart,因此不需要单独安装dart-sdk。 Flutter的SDK可以从官网下载: https://flutter.io/docs/development/tools/sdk/archive?tab=windows#windows 这里列出了4个channel:Stable,Beata,Dev,Master。开发推荐使用dev版本。 当前最新Stable版...

超频化石鱼的博客 2万+

Unity - gamma space下还原linear space效果

还有头发之后的 (头发没有处理完整,因为使用 ASE 连连看练出来的,使用 surface shader,虽然可以生成一下 vert, frag 的方式在修改,但是我懒得去修改了,这样就是 PBR 的 BRDF 里面的部分曲线是不一样的,所以可以看到头发有一些差异)其实上面的代码判断 是否有分量 > 1.0f 的方式来判断是否 HDR 是不太合理的,因为不同的贴图格式的编码方式不同。还有比较隐蔽的一些颜色相关的常量值 (比如PBR中的 绝缘体的 F0 常量值,等等)

Jave.Lin 的学习笔记 2830

Flutter(2) — 安装SDK

Flutter(2)— 安装SDK一、获取Flutter SDK二、安装SDK和环境配置三、SDK初探flutter命令channelversion目录packages目录dev目录bin目录cache目录dart-sdk目录bin目录pkg目录artifactsdownloads目录四、总结 这一篇主要介绍一下Flutter的SDK环境搭建,网上关于环境搭建的文章很多,但是介绍基本都没有介绍SDK里有些什么。本文以Mac上安装为例,因为主要涉及到SDK内部,所以和其他平台应该是一致的。 官方的安装文档:

aha_jasper的博客 4906

FPGA中Verilog HDL/VHDL实现NCO(数字控制振荡器)的查表法优化与性能分析

本文深入探讨了在FPGA中使用Verilog HDL/VHDL实现数字控制振荡器(NCO)的查表法优化与性能分析。文章详细解析了NCO的核心工作原理,包括相位累加器、查找表(ROM)的设计,并重点介绍了利用波形对称性压缩ROM、相位抖动注入等关键优化技巧,以提升无杂散动态范围(SFDR)并平衡速度、精度与资源消耗。

vscode6remote的博客 456

Ubuntu-22.04安装VMware虚拟机并安装Windows10

最近在研究虚拟机,主要集中在KVM、Virtualbox和VMware这三种虚拟机上。前面我已经研究了前两种虚拟机的安装和使用方式,这篇文章介绍3兄弟中最后一个虚拟机安装和使用方式。Virtualbox虚拟机安装和使用KVM虚拟机安装和使用相比于前两种,VMware的安装方式最麻烦,但是综合体验下来我感觉流畅度最好。如果你确实有在Linux上使用VMware虚拟机的需求,那不妨跟着我看下去吧。1、gcc版本和内核版本一定要匹配上,否则会出各种错误。

jiexijihe945的博客 4116

Flutter框架下实现用户行为埋点

用户行为埋点主要是为了采集用户对APP的功能使用情况,包括统计用户的活跃度,用户画像的刻画等等,以明确产品功能的开发、优化方向。我们借助神策数据开源的SDK来实现埋点,这里还需要自己实现服务端数据接收服务;接收到数据后,我们解析后就可以进而加工成符合我们要求的格式,直接存储到数据库或导入其他现有的平台进行后续的用户行为分析、监控等等。

dawnknights的博客 1586

AgentScope实战训练营:从零搭建一个完整的A2A Agent应用

2024年4月,Google在Google Cloud Next大会上正式提出了A2A(Agent-to-Agent)协议,这是一个开放协议,旨在让不同的AI Agent之间能够像微服务一样相互发现、相互调用。通俗地讲:A2A协议就是AI Agent世界的HTTP协议——有了它,一个Agent可以把自己的能力以标准化的方式暴露出去,另一个Agent可以无缝地发现并调用它。本文将基于阿里巴巴开源的(Apache 2.0协议),手把手带你从零搭建一个完整的A2A Agent应用。

program哲学 564

Flutter 在 Windows 下的开发环境搭建(Flutter SDK 3.19.2)【图文详细教程】

Flutter 在 Windows 下的开发环境搭建(Flutter SDK 3.19.2),Flutter 开发环境搭建完成后,创建 Flutter 项目验证开发环境是否搭建成功

emmmm..... 2万+

【从零开始学习 UVM】1.1、UVM 概述 —— Preface

【从零开始学习 UVM】1.1、UVM 概述 —— Preface

努力,加油每一天鸭~ 825

ParaView: 从 2 或 3 个标量创建向量

ParaView: 从 2 或 3 个标量创建向量

希望我的博客,能帮上你解决学习中工作中所遇到的问题 585

flutter可视化_开源Magpie可视化圈选埋点实践

开源项目专题系列(八)1.开源项目名称:magpie_log2.github地址:https://github.com/wuba/magpie_log3.简介:magpie_log是基于flutter的一个跨平台可视化圈选埋点的解决方案。旨在解决手动埋点耦合业务,现有Native自动埋点方案无法带参,以及混合开发中因埋点导致交互频繁影响性能等问题,成为flutter埋点的一站式解决方案。...

weixin_39742392的博客 912

第一节:从Chat_Bot 到 AI_Agent——一个范式转换

本文系统剖析了工业级AI代理系统ClaudeCode的设计原理与工程实践。通过对比ChatBot与Agent的三个本质差异:自主循环决策vs单轮应答、环境交互vs纯文本I/O、持久状态管理vs无状态对话,揭示了构建生产级Agent系统的关键挑战。文章详细解析了ClaudeCode的五层架构设计、1800+文件组成的运行时系统,包括工具调度、权限管理、状态持久化等核心机制,并阐述了从简单API调用到完整Agent系统的演进路径。

2201_75869398的博客 68

ClkLog埋点分析系统-Flutter埋点上报攻略

近期,不少社群里的伙伴有Flutter的集成需求,为了让大家能更快、更顺利地完成集成,我们实现了本次demo给大家作为参考。 目前,我们已为主流的第三方框架提供了相应的集成demo,如果您还有其他SDK的验证需求欢迎联系小秘书,我们会尽量给大家提供实现demo。

ClkLog的博客 510

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Flutter 数据分析接入指南:TalkingData 到底怎么用?

《Flutter数据分析接入指南:TalkingData实践详解》介绍了如何在Flutter项目中接入TalkingData SDK进行数据分析。文章从SDK简介、快速使用流程入手,重点讲解了Flutter侧的插件引入方式、配置常量和延迟初始化方法,并针对Android和iOS平台提供了注意事项。同时给出了埋点扩展示例思路和常见问题排查建议,强调数据分析对业务增长的重要性。全文兼顾官方要点与项目实践,帮助开发者快速完成基础接入并规避常见问题。

码上全栈创享家官方号,小红书、公众号、抖音、b站,youtube等同名,欢迎各位码农一起来学习! 655

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Flutter数据埋点在OpenHarmony的工业级适配方案

数据埋点是移动应用开发中确保产品质量和优化用户体验的关键技术,其核心原理是通过代码插桩记录用户行为数据。在跨平台开发场景下,类型安全的日志采集方案能显著降低维护成本,而OpenHarmony的分布式特性为数据采集带来了新的技术挑战。本文以Flutter生态的analytics_logger_gen库为例,详解如何通过鸿蒙桥接层实现线程安全的数据序列化、兼容分布式文件存储、适配鸿蒙网络栈等关键技术点,最终达到工业级数据采集要求的完整性、一致性和低开销指标。该方案特别适用于需要跨设备同步数据的物联网和智能硬件场

weixin_30899023的博客 215

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Flutter应用迁移OpenHarmony实战:题库App开发与学习报告实现

跨平台移动应用开发中,代码复用与生态兼容是工程落地的关键挑战。Flutter作为成熟的跨平台UI框架,其高复用性与灵活渲染机制为应用迁移提供了技术基础。OpenHarmony作为新兴国产操作系统,其生态建设正逐步完善,如何将既有Flutter代码库高效迁移至鸿蒙平台,成为开发者关注的热点。本文从实际项目出发,围绕一个艺考真题题库App的迁移过程,深入探讨Flutter for OpenHarmony的工程实践,涵盖环境搭建、数据持久化、多线程处理及图表自绘等核心技术。其中学习报告模块的实现路径,展示了如何通

weixin_29015483的博客 158

Flutter AI应用可观测性实战:Dartastic OpenTelemetry原生监控方案

可观测性是现代移动应用,尤其是AI原生Flutter应用的核心能力,它通过统一采集追踪(Tracing)、指标(Metrics)和日志(Logs)数据,实现对跨Isolate、跨Widget、跨平台通道的全链路性能洞察。其原理基于OpenTelemetry标准协议与Dart运行时特性深度协同,利用Zone实现上下文自动传播、Widget生命周期钩子实现零侵入埋点、OTLP协议保障高效可靠传输。技术价值在于将传统‘黑盒式’调试升级为可量化、可关联、可告警的工程化运维体系,支撑百万级DAU场景下的AI推理、向量

weixin_29006701的博客 139

Flutter埋点全攻略

/ 今日科技快讯 /近日,百度大脑开放日举办。IDC中国副总裁兼首席分析师武连峰表示,到2021年底,超过70%具有海外业务的中国企业将不同程度的采用机器翻译,应用场景也将更为丰...

郭霖 4253

Flutter全埋点环境配置脚本:轻松实现应用用户行为追踪

Flutter全埋点环境配置脚本:轻松实现应用用户行为追踪 去发现同类优质开源项目:https://gitcode.com/ 在当今的移动应用开发领域,用户行为分析是提升用户体验、优化产品功能的关键环节。本文将为您推荐一款强大的开源项目——Flutter全埋点环境配置脚本,帮助您快速搭建全埋点追踪功能。 项目介绍 Flutter全埋点环境配置脚本是一款专门为Flutter开发者设计的自动化工具,它...

gitblog_06737的博客 752

Flutter全埋点环境配置脚本

Flutter全埋点环境配置脚本 去发现同类优质开源项目:https://gitcode.com/ 此仓库提供了一份用于配置Flutter全埋点环境的脚本,旨在帮助开发者快速搭建全埋点追踪功能,从而更好地监控和分析用户行为,提升应用性能。 脚本功能 自动配置Flutter项目所需的全埋点依赖库。 一键集成Flutter全埋点SDK。 简化埋点代码编写,提高开发效率。 使用说明 将脚本文件下载...

gitblog_06715的博客 453

Flutter集成友盟的SDK

集成的是友盟最新的SDK:umeng_common_sdk 1.2.3。 SDK的地址: umeng_common_sdk | Flutter Package友盟+组件化基础库:Common Flutter plugin(Android & iOS).https://pub.flutter-io.cn/packages/umeng_common_sdk 官方的集成文档: 开发者中心https://developer.umeng.com/docs/119267/detail/174923 .

gaoyp的专栏 3360

flutter 图解_Flutter SDK使用说明

Flutter SDK 使用文档1. Flutter插件集成1.1. Flutter 插件获取安装根据 dart pub 文档获取安装1.2. Android 集成(Native部分)此 Flutter 插件运行在 Android 手机上时依赖于 Hubble Android SDK,请参考Hubble Android SDK 集成1.3. iOS 集成(Natvie部分)此 Flutter 插件...

weixin_31721961的博客 617

Android最全Android-开发必看---Flutter之全埋点思考与实现(1),2024年最新阿里面试轮数

算法知识点繁多,企业考察的题目千变万化,面对越来越近的“金九银十”,我给大家准备好了一套比较完善的学习方法,希望能帮助大家在有限的时间里尽可能系统快速的恶补算法,通过高效的学习来提高大家面试中算法模块的通过率。这一套学习资料既有文字档也有视频,里面不仅仅有关键知识点的整理,还有案例的算法相关部分的讲解,可以帮助大家更好更全面的进行学习,二者搭配起来学习效果会更好。部分资料展示:有了这套学习资料,坚持刷题一周,你就会发现自己的算法知识体系有明显的完善,离大厂Offer的距离更加近。

2401_84520401的博客 930

Flutter 组件 analytics_gen 的适配 鸿蒙Harmony 深度进阶 - 驾驭动态参数注入、实现鸿蒙端多渠道分流审计与高并发埋点削峰填谷方案

在前文中,我们通过实现了埋点代码的注解驱动自动化生成。但在真正的“国民级 App”、“高频金融交易平台”或“超大型社交矩阵”中。简单的事件生成仅仅是起点。面对需要针对每一个埋点动态注入设备指纹、用户画像标签(Tags);面对需要将同一份数据同时分发给 5 个不同的审计平台且保持吞吐平衡;面对在“双十一”等大促期间瞬间产生的几十万次埋点请求。如果处理不当,不仅会产生数据丢失。更会在鸿蒙(OpenHarmony)端引发严重的网络阻塞与内存溢出。本文将作为适配的进阶篇。

2609_95049439的博客 4099
上一篇: Flink 实时 metrics
下一篇: GrowingIO Design 组件库搭建之 CI/CD
GrowingIO_
博客等级 码龄8年 56粉丝 61原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值