本文内容根据神策大数据技术直播系列第一季第二讲《数据采集与埋点》整理而成,可在公众号后台回复【629技术直播】,获取 PPT。

▼▼▼

主持人(刘鑫):来到神策以后参与的一个项目,就是灼洲主导的可视化全埋点,而我来神策之前其实有很长时间,都是类似神策 SDK 这种产品的用户,这个领域从多方面来说,对我都是一个很亲切的话题,我这边看不到大家的入场的情况,不过七点钟就正式开始了,接下来我那把话筒交给灼洲老师。

讲师(王灼洲):大家下午好,我叫王灼洲,是神策 SDK 研发负责人。今天非常高兴能有机会跟大家一起探讨埋点。今天主要跟大家分享埋点的整体概况,涉及到的内容比较多,因此不会深入探讨具体细节。

神策数据采集 SDK 属于开源项目,任何个人和组织都可以在 GitHub 上看到我们的源码(https://github.com/sensorsdata/)。

我们的 SDK 全端覆盖,包括 Android、iOS、Web,以及主流的后端开发语言如 Java、C++、C、PHP、.NET、Node、Go、Python、Ruby 等,还有小程序如微信、百度、字节跳动、支付宝、QQ、快应用等,同时也覆盖了主流的跨平台开发框架如 React Native、APICloud、mPaaS、Flutter 等。

数据分析的常见应用场景

  • 用户画像

  • 个性化推荐

  • 反作弊

  • 精准广告

  • 在线分析

  • 搜索优化

  • 文本挖掘

数据采集的数据类型可以通过两个维度划分。

按照数据类型:用户行为数据、用户相关数据、业务相关数据、文本类型数据

按照数据的所有者:第一方数据、第三方数据

▼▼▼

数据采集到底重不重要?神策成立 5 年来,我们已服务 1000+ 家企业客户,我们发现一个现象:很多客户的产品迭代速度特别快,基本上是两周一个版本,甚至一周一个版本。在这么短的周期内,既要做业务开发,又要进行埋点,着实不容易。若两者产生冲突,更多的客户会选择放弃埋点。

数据分析的流程大概是:数据采集 → 数据传输(实时 / 批量) → 数据建模(存储) → 数据统计 / 分析 / 挖掘 → 数据可视化(反馈),从这个流程中我们可以发现,数据采集是数据分析的基本,更是源头,数据采集与业务开发同等重要。

一个完整的埋点流程应该是什么样的?整体上与产品研发流程是类似的。

在神策分析中,我们使用事件模型(Event 模型)来描述用户在产品上的各种行为,这也是神策分析所有接口和功能设计的核心依据。简单来说,事件模型包括事件(Event)和用户(User)两个核心实体。

事件 Event,就是谁在什么时间、什么地点,以什么样的方式干了什么。对于用户 User 则是这个事件发生的主题。

▼▼▼

在进行埋点的时候,我们应该遵循「大」、「全」、「细」、「时」四个原则。

「大」,主要是考虑到随着我们业务的发展,用户规模和数据规模会指数增长,不仅体量大,更是我们积累数据资产的重要前提。

「全」,主要是指多端采集。比如神策的 SDK 种类比较多,会覆盖主流的平台和开发语言。只有这样,我们再进行数据分析时,就可以做到全量用户行为分析,而不是抽样分析。同时,我们采集到的用户行为数据,也能做到贯穿用户的整个生命周期。

「细」,主要是指我们可以采集更全面的属性和更细的粒度,让采集的数据更全面、更优质,同时更能满足我们更精细化的分析需求,更方便我们积累数据资产。

「时」,主要是指实时性。比如,用户搜索了“牙疼”,但我们的数据分析结果一周后才出来,而此时用户牙疼可能“康复了”。神策非常注重实时性,考虑到技术和成本,神策会最大限度的提高实时性,从而确保数据分析应用的时效性。目前,从数据采集到数据分析并展现出来,神策已达到分钟级别。

数据采集 SDK 的功能,概括起来就三点:

  • 通过埋点来采集数据
  • 将采集到的数据传输到指定的服务器端

  • 最大限度的保证数据的准确性和完整性

常见的埋点方式:

  • 代码埋点
  • 全埋点(无埋点)

  • 可视化全埋点(圈选)

代码埋点』

代码埋点即在我们的产品内,当用户的某个行为发生的时候,调用 SDK 的接口触发事件。

代码埋点优点:

  • 精准控制埋点(方便、灵活的自定义事件和属性)
  • 采集数据丰富

  • 可以满足更精细化的分析需求

代码埋点缺点:

  • 埋点代价比较大
  • 需要伴随着 APP 发版

全埋点』

全埋点也叫无埋点、无码埋点、自动埋点、无痕埋点,即无需研发工程师写代码或者写少量代码,即可达到自动(预先)埋点的目的。全埋点的宗旨,既要满足客户实际业务分析的需求,又要减少客户埋点的代价。

全埋点可以采集的事件:

  • 激活
  • APP 启动

  • APP 退出
  • APP 页面浏览
  • 控件点击
  • 曝光

激活即用户安装 APP 后的首次启动。对激活的判断,Android 和 iOS 面临的最大挑战,就是如何解决 APP 卸载重装的问题。通常情况下,都是通过去唯一标识一台设备,比如 Android 可能会采用 AndroidID,iOS 可能会采用 IDFA 等。激活事件一般会携带渠道信息,比如当前用户是从哪个渠道来的。

APP 启动,即点击应用图标,或者其他方式,让 APP 运行并在前台展示。

启动事件需要采集的属性:

  • 是否首次启动(安装后的首次启动)
  • 冷启动、热启动(从后台恢复)

  • 启动来源

启动来源包括:

  • 用户点击图标启动?
  • 从 Web 跳转?
  • 从小程序跳转?
  • 从其他 APP 跳转?
  • 用户点击推送唤醒?
  • 通过 Deeplink 唤醒?

对于 APP 退出的常规定义是 APP 不在前端显示。这个定义,可以覆盖绝大部分的业务场景,但对于播放器类型的 APP,这个定义就不再适用了:我们让播放器进入后台,但仍在继续使用 APP,此时到底算不算退出呢?因此,对于不同的业务类型场景,其定义也略有差异。APP 退出事件采集的属性:

  • APP 使用时长

  • 退出方式

其中退出方式包括:按 HOME 键让 APP 进入后台、把 APP 强杀、APP 发生崩溃、跳转到其他 APP。

不同的业务,对曝光的定义也不同。比如有一个列表页面,有的认为只要列表从服务端拉取到 APP 端就算曝光了,而有的认为,只有每一项在 APP 端显示出来了才算曝光。

适合采集曝光的场景:

  • 广告位

  • 列表

  • Toast(吐司)

  • Dialog(对话框)

对于 Android,页面是指 Activity、Fragment;对于 iOS,页面是指 UIViewController。

页面浏览事件采集的属性:

  • 页面标题($title)

  • 页面名称($screen_name)

  • 前向地址($referrer)

  • 自定义属性

对于 APP 而言,用户频率最高的行为就属点击控件。因此,全埋点最重要的工作就是采集控件点击。

控件点击事件采集的属性:

  • 哪个控件被点击了

  • 控件是什么类型

  • 控件所属哪个页面

  • 控件上显示的文本信息

  • 扩展自定义属性

全埋点,默认情况下是全量采集,即默认采集所有页面上的所有控件的点击,但有一些点击确实是业务分析不需要的,这样不仅会占用带宽也会占用大量存储空间。因此,全埋点采集的点击事件,还需要提供丰富的 API 来对点击事件进行灵活配置,比如:

  • 不采集哪些页面上的 $AppClick 事件

  • 不采集哪些类型控件的 $AppClick 事件

  • 不采集某个特定控件的 $AppClick 事件

  • 给控件的 $AppClick 事件添加自定义属性

全埋点优点:

  • 埋点代价比较小

  • 无需更新 APP

  • 解决数据「回溯」的问题

  • 其他高级功能的强依赖(可视化全埋点、点击图)

全埋点缺点:

  • 覆盖的功能有限

  • 无法自动采集业务相关的数据

  • 无法满足更精细化的分析需求

  • 兼容性问题

  • 传输的数据量大、浪费资源

对于 Android 和 iOS,其全埋点原理略有差异。由于实现细节比较多,在此仅仅做一个简单的介绍,更多细节可参考我们的 SDK 源码(后台回复【SDK源码】即可)。

关于 iOS 全埋点更多的技术细节,可参考《iOS 全埋点解决方案》一书。

▼▼▼

关于 Android 的启动和退出,也需要考虑多进程的问题,神策目前对 Android APP 启动和退出的定义如下:

启动:若一个页面显示出来了,与上一个页面退出的时间间隔超过了 30s,则算一次启动。

退出:若一个页面进到后台,30s 之内没有新的页面显示,则算一次退出。

对于 Android 采集控件点击事件的技术也比较多,比如 AspectJ、ASM、 Javassist、AST 等 8 种方案,神策目前是采用 ASM。关于其他方案的技术原理,可参考《Android 全埋点解决方案》一书。

可视化全埋点』

可视化全埋点,也叫圈选,即通过可视化的方式对全埋点进行配置,比如:

  • 设置 / 修改控件 ID

  • 设置 / 修改控件“内容”

  • 设置 / 修改控件所属页面的页面标题

  • 控制不采集哪些控件的点击事件

  • 添加静态属性

  • 重命名点击事件名

可视化全埋点的演示,可参考下图,更详细的体验,可申请神策的 Demo。

可视化全埋点的技术原理,主要依赖三个概念:

  • 截图

  • ViewTree

  • ViewPath

截图:即 APP 当前页面的截图。

ViewTree:Android 和 iOS 的页面跟网页一样,其结构都可以用一棵树来表示,这棵树我们叫 ViewTree。在 ViewTree 里,会包含控件的层级(父子)关系和控件可视化信息,比如控件类型、坐标、长宽、显示文本等。

ViewPath:在 ViewTree 里,任何一个控件,从根节点到当前节点的路径都是唯一的,这个路径称为 ViewPath。

当 APP 连接神策分析后,SDK 会定时(每隔一秒)将 APP 当前的截图和对应的 ViewTree 信息发给服务端,前端以截图为背景,根据用户鼠标点击的位置在 ViewTree 里找到对应控件的 ViewPath,再根据 ViewPath 里的元素信息绘制元素矩形框,同时通过 ViewPath 在系统里唯一标识当前控件,最后与控件点击事件采集的 ViewPath 进行匹配。

点击图与可视化全埋点类似,均是以上技术不同的应用,他以一种更直观的方式展示用户在当前页面上的点击情况。

▼▼▼

面对这么多埋点方式,我们到底选用哪一种?

神策这 5 年来,已服务 1000+ 家企业客户,通过深度服务客户,我们发现其实并没有一种非常完美的埋点方案,它们各有优缺点。

面对这么多的埋点方案,不能一味追求省事,更不能追求埋点方式的「酷炫」,最主要的还是要根据实际的分析需求和业务场景,选择最能满足我们需求的埋点方式。若有多种埋点方案都能满足,我们可以再追求「省事」和「酷炫」的方案。

在这 5 年来,我们也积累了一些经验,借此机会跟大家分享一下。

设备 ID』

用户在产品上登录之前,我们需要唯一标识当前设备,也即设备 ID。对于 Android 和 iOS,设备 ID 也略有差异。

1iOS 设备 ID

UDID

即 iOS 设备的唯一识别码,是 Unique Device Identifier 的缩写。它由 40 位十六进制组成的序列。从 iOS 5 开始,苹果为了保护用户的隐私,就禁止应用程序通过代码获取 UDID了。而且还对外宣称,一旦发现应用程序继续获取 UDID,将禁止上架 APP Store。所以不能保证唯一识别该设备。

UUID

即通用唯一标识符,是 Universally Unique Identifier 的缩写。由 32 位十六进制组成的序列,使用小横线来连接,格式为:8-4-4-4-12。如果用户删除该应用再次安装时,又会生成新的字符串,所以不能保证唯一识别该设备。

Mac Address

用来表示互联网上每一个站点的标识符,采用十六进制数表示,共 6 个字节(48位)。在 iOS 7 之后,Mac Address 也属于用户隐私。如果请求 Mac Address都会返回一个固定值:02:00:00:00:00:00。所以不能保证唯一识别该设备。

IDFA

即广告标示符,是 Identifier For Advertising 的缩写。适用于对外:例如广告推广、换量等跨应用的用户追踪等。由 32 位十六进制组成的序列,格式与 UUID 一致。在同一个设备上的所有 APP 都会取到相同的值。在 iOS 10.0 以后,当用户开启限制广告跟踪,获取的值为:00000000-0000-0000-0000-000000000000。比较适合作为设备 ID。

IDFV

即 Vendor 标示符,是 Identifier For Vendor 的缩写,适用于分析用户在应用内的行为等。也是一个由 32 位十六进制组成的序列,格式与 UUID 一致。每个设备在所属同一个 Vendor 的应用里,都有相同的值。如果用户将属于此 Vendor 的所有 APP 卸载,则 IDFV 的值会被重置。

最佳实践

采用 IDFA/IDFV + Keychain 的方式,数据并不是存放在 APP 的 SandBox 中,即使删除了 APP,数据依然保存在 Keychain 中。如果重新安装了 APP,还可以从 Keychain 获取数据。可以有效的解决卸载重新安装新增的问题。

2Android 设备 ID

IMEI

需要 android.permission.READ_PHONE_STATE 权限,这个方法只对有电话功能的设备有效。Android Q 开始,非系统应用或者不在白名单内,无法获取 IMEI。不适合作为设备 ID。

Serial Number

可以通过 String SerialNumber = android.os.Build.SERIAL 方式获取。不需要权限,部分机型可能会返回 0123456789ABCDEF,最新的 Android OS 可能会限制读取。不适合作为设备 ID。

Mac Address

需要权限,Android 6.0 后会返回固定值: 02:00:00:00:00:00。不适合作为设备 ID。

最佳实践

使用 AndroidID 作为设备 ID。在设备首次启动时,系统会随机生成一个 64 位的数字,并把这个数字以 16 进制字符串的形式保存下来。不需要权限,平板设备通用,缺点是设备恢复出厂设置会重置,但已最大限度的满足实际分析需求。也要考虑 Android Q,同一个设备,不同的 APP 获取到的 AndroidID 不同。

『时间』

在数据采集时,和时间相关的有两个:

  • 记录事件发生的时间戳

  • 统计某个行为持续的时长

如果我们以客户端时间为准,可能会有如下两个问题:

  • 统计事件时长 ($event_duration) 不准,为负、超大

  • 事件发生时间 (time) 不合理,超前、超后

可能会有同学说,为什么不用服务端时间呢?为了提高效率和减少数据丢失,客户端采集的事件一般是先缓存在本地,待符合一定的策略之后再同步。

又会有同学说,为什么不通过服务端校准客户端时间呢?既然是校准,就需要网络请求,如果没有网络怎么办?离线的 APP 怎么办?在校准之前的事件或者时长统计怎么办?

因此,以上两种方案均无法兼容各种场景。下面,我们介绍神策的解决方案。

1统计时长

Android 和 iOS,都有一个 boot time 的概念,即手机启动了多长时间。可以理解为系统里有一个定时器,每次手机启动后,这个定时器就从零开始计时,与客户端时间无关,如果我们使用这个 time 来计算间隔,就是 100% 准确的。

// Android
SystemClock.elapsedRealtime()

// iOS
NSProcessInfo.processInfo.systemUptime

2事件发生的时间戳

我们采用的是“时间纠正”这个策略。

用户在手机时间戳 T1 时触发了一个事件(比如登录事件),然后事件存入本地缓存。数据采集 SDK 在用户手机时间戳 T2 时开始同步事件数据(包含登录事件),然后服务端通过 HTTP(S) 的 Request Header Date 可以获取到数据采集 SDK 发起 Request 时用户手机时间戳 T2 。

如果 T2 与当前服务端的时间戳 T3 的误差在一定的可接受范围之内(比如 30s,这是因为网络请求也需要一定的时间),就可以说明当前用户手机的时间戳是准确的(我们假设服务端时间戳是准确的,绝大部分情况下也都是准确的),同时说明登录事件发生时的时间戳 T1 也是准确的,服务端在接收到登录事件时无需做任何的特殊处理。

如果 T2 与 T3 相差比较大(比如上图中的 T2 为 15:00,T3 为 16:00),我们可以确定当前用户手机的时间戳 T2 是不准确的,即比服务端的时间戳晚了一个小时(16:00 - 15:00 = 一个小时),从而就可以假设登录事件发生时的时间戳 T1 也比服务端晚了一个小时,即 14:00 加上一个小时应该为 15:00。

因此,服务端在接收到事件时会把登录事件的时间戳 T1 再加上一个小时,变成 15:00,从而就达到了“时间纠正”的效果。

当然,“时间纠正”策略,也无法确保可以 100% 解决所有和时间戳相关的问题。比如,还是以上图为例,如果在时间戳 T1 和 T2 之间,用户手机的时间戳再次发生了人为变更,那么,该方案就无法正确的进行时间纠正了,这是因为时间戳 T2 和 T3 之间的误差额,与时间戳 T1 和当时服务端对应时间戳的差额就不相等了。

但从实际情况来看,由于数据采集 SDK 一般都会以较短的固定时间间隔同步数据(比如 15s),所以时间戳 T1 和 T2 之间的时间间隔会比较短,本身发生时间戳变更的可能性就比较小,即使真正发生变更了,影响到的事件数量也比较少(15s 内可能只会触发几条事件)。因此,“时间纠正”策略可以解决 99% 以上的和事件时间戳相关的问题。

另外,由于“时间纠正”策略的实现逻辑都是在服务端处理的,而且实现起来也比较简单,在此我们不再详细描述实现细节。

救火队员』

对于 SDK 来说,最核心的目的就是采集数据,但有一个前提,即不能够影响客户的正常业务。不管研发如何努力,QA 如何测试,总是会出现无法预料的异常。此时,我们就必须要有救火队员,即通过远程下发配置控制 SDK 的行为,比如关闭 SDK。

同步策略』

为了最大限度的保证数据的完整性和准确性,SDK 采集的数据,一般会先缓存到本地,待符合一定条件之后再同步。

常见的数据同步策略:

  • 事件先缓存到本地 sqlite3

  • 本地缓存一定条数量的事件会同步(默认 100 条)

  • 固定时间间隔同步(默认 15 秒)

  • 核心事件发生同步(trackInstallation、login 等)

  • APP 退出尝试同步

  • 本地缓存太多事件时的处理

在 APP 退出时尝试同步数据,是为了防止用户之后长时间不启动 APP、不再启动 APP 或者直接卸载 APP。

网络策略』

同步数据需要网络请求,不同的业务、不同的场景对网络策略要求也不同。比如,对用户体验要求较高的业务,有可能要求只有在 WiFi 网络下才去同步数据。因此,SDK 就需要支持可以灵活的配置网络策略,以满足不同的需求。默认情况下,在 3G、4G、5G、WiFi 网络下 SDK 会同步数据。

SDK 支持的网络配置如下:

  • NetworkType.TYPE_2G

  • NetworkType.TYPE_3G

  • NetworkType.TYPE_4G

  • NetworkType.TYPE_5G

  • NetworkType.TYPE_WIF

  • NetworkType.TYPE_ALL

  • NetworkType.TYPE_NONE

H5 与 APP 打通』

近年来,iOS 混合开发越来越流行,APP 与 H5 的打通需求也越来越迫切。那什么是 APP 与 H5 打通呢?所谓“打通”,是指 H5 集成 JavaScript 数据采集 SDK 后,H5 触发的事件不直接同步给服务端,而是先发给 APP 端的数据采集 SDK,经 APP 端数据采集 SDK 二次加工处理后入本地缓存再进行同步。

APP 为什么要与 H5 打通呢?

主要是从以下几个角度考虑。

1数据丢失率

在业界,APP 端采集数据的丢失率一般在 1% 左右,而 H5 采集数据的丢失率一般在 5% 左右(主要是因为缓存、网络或切换页面等原因)。因此,如果 APP 与 H5 打通,H5 触发的所有事件都可以先发给 APP 端数据采集 SDK,经过 APP 端二次加工处理后并入本地缓存,在符合特定策略之后再进行同步数据,即可把数据丢失率由 5% 降到 1% 左右。

2数据准确率

众所周知,H5 无法直接获取设备相关的信息,只能通过解析 UserAgent 值获取到有限的信息,而解析 UserAgent 值,至少会面临如下两个问题:

1)有些信息通过解析 UserAgent 值根本获取不到,比如应用程序的版本号等。

2)有些信息通过解析 UserAgent 值可以获取到,但内容可能不正确。

如果 APP 与 H5 打通,由 App 端数据采集 SDK 补充这些信息,即可确保事件信息的准确性和完整性。

3用户标识

如果用户在 APP 端注册或登录之前使用我们的产品,我们一般都是使用匿名 ID 来标识用户。而 APP 与 H5 标识匿名用户的规则不一样(iOS 一般使用 IDFA 或 IDFV,H5 一般使用 Cookie),进而就会导致一个用户使用了我们的产品,结果产生了两个匿名用户的情况。如果 APP 与 H5 打通,就可以将两个匿名 ID 做归一化处理(以 APP 端匿名 ID 为准)。

打通的场景比较多,也比较复杂,需要重点考虑以下几个常见:

  • 自家的 H5

  • 第三方 H5(非神策客户)

  • 第三方 H5(神策客户)

今天由于时间的关系,大概就跟大家分享这么多的内容。如果大家要对这方面有兴趣,或者说对于神策的产品有兴趣,我们可以在线下进行更进一步的交流。

交流合作✎✎✎更多内容

  • Lambda 设计参考
  • 神策数据微信小程序 SDK 功能介绍

  • 神策分析 iOS SDK 全埋点解析之启动与退出

android 退出app代码_PPT 下载 | APP 埋点那些事相关推荐

  1. android今日资讯代码,今日资讯app下载_今日资讯新闻弹窗最新消息软件下载安装 安卓版 V1.4 - 罐头安卓网...

    今日资讯app是一款拥有海量新闻实时资讯的手机软件,帮助用户实时了解海内外热门新闻以及重要政治要闻,聚集了全国各地的资讯新闻信息内容,祝你轻松搞定一切热点新闻讯息.在这里所有报道全部都是权威性资讯文章 ...

  2. android 服务器是什么系统版本,android系统版本代码的下载、编译及发布

    不积跬步无以至千里 一.代码的下载 1.创建文件夹 在服务器的个人账户下创建一个此系统版本的专用文件夹(F9)(事先声明这是Q9的项目) mkdir F9 2.设置git信息 git config - ...

  3. android指南针校准 代码_android 指南针app源码(亲测可用)

    [实例简介] [实例截图] [核心代码] package cn.icast.zhinanzhen; import android.app.Activity; import android.conten ...

  4. android指南针校准 代码_Android指南针app的实现原理总结

    要想实现指南针功能,其实主要就是获取手机的方位,通过对比前一刻方位和现在手机方位算出手机旋转的角度,然后根据手机实际旋转的角度去旋转指南针的imageview.关键在于如何获取手机实际方位. 那么如何 ...

  5. Dynamics 365Online 如何从APP Source中下载APP

    进入CRM,点击设置,在自定义下会看到Microsoft AppSource 点开搜索你要安装的app,比如我这里的Data Export Service,点击Get it now,会跳转到一个页面让 ...

  6. 火鸟门户底部提示下载APP修改方法_火鸟修改底部提示APP的教程

    ///火鸟门户修改底部APP提示的方法 ///零起步创业工作室旗下海码站提供撰写修改 ///如有转发的请备注,来源海码站 谢谢 修改方法一 ****找到目录 /static/js/core/touch ...

  7. 不下载APP就不能看全文?工信部出手了...

    相信大家都遇到过这种情况,当你在手机或电脑上看资讯信息的时候,不料才看了三分之一内容,这时候页面上就有这样的提示:更多内容,请在某某App中查看",理由是这样做"体验更佳" ...

  8. 在微信中分享下载APP或H5页面如何做好域名防封

    很多情况下H5作为微信推广裂变的一种方式,特别是在QP.BC.CP方面的效力,得到特别多的关注和应用,且有些商家通过H5页面搭建虚假红包链接.跳转APP,甚至出现恶意传播来获取关注,造成了特别的大的影 ...

  9. 不下载APP就不给看?工信部:这类APP要整改

    想要浏览链接,却遭遇"不下载APP(移动应用)就不给看":愿意下载还不行,APP又要求获取定位.摄像和录音等权限--近日,针对部分网站在用户浏览页面信息时强制要求下载APP等问题, ...

最新文章

  1. 呼叫中心团队管理浅谈
  2. iOS开发 -------- AFNetworking使用中遇到的小问题
  3. JAVA 两个简单的抽奖算法
  4. PLSQL的UTL_FILE使用例子
  5. puppet应用原理及安装部署
  6. 【python 3】 面向对象
  7. Servlet的学习笔记
  8. Angularjs 动态添加指令并绑定事件
  9. 猎豹MFC--CFile类家族介绍ADO连接数据库 打开数据库 关闭数据库 连接字符串
  10. php手册中点击下载的功能,PHP实现文件下载功能
  11. 断路器监控(Hystrix Dashboard)
  12. php安装sphinx扩展,安装php的sphinx扩展模块
  13. 令人眼前一亮的下拉式终端 Tilda Guake
  14. 算法练习-珠心算测验
  15. 好看又实用的压缩文件管理器——Bandizip
  16. 一款开源免费的办公套件系统:DzzOffice详细部署
  17. 工会优秀工作者先进事迹材料【加精推荐】 - 蒋炳楠的博客
  18. Web网站HTML打开QQ聊天窗口示例
  19. 输入正确的账号密码和验证码后 验证码一直报错误的解决办法
  20. Android Paint的使用详解

热门文章

  1. 程序人生 - Python 攻克移动开发失败!
  2. js调用身份证读卡器-兼容Chrome、Edge、360、Firefox、IE、钉钉、企业微信等
  3. python执行CMD指令,并获取返回
  4. ct值在哪里看_来,带你见识一下CT三维重建
  5. asp.net 动态修改css样式,ASP.NET中直接用C# 动态修改CSS样式
  6. 2019传统微商为什么要转型快手电商?
  7. 第一篇 厚黑学 代序四 狂狷嘲世一教主
  8. 普元 AppServer在window2019中无法启动server,也没有报错信息
  9. 财务期初开账-科目余额/期初导入/期初数据/期初余额
  10. 手游虚拟机中连接不到服务器,自由幻想手游模拟器进不去游戏 登录失败解决办法...