android h5使用缓存_Android SDK 的 H5 打通方案演进 | 数据采集

2ba198fcb51d2c7d3b8d68cbf9671fc8.png

一、前言

近年来,混合开发越来越流行,App 与 H5 的打通需求也越来越迫切。

那什么是 App 与 H5 打通呢?

所谓 “打通”,是指 H5 集成 JavaScript 数据采集 SDK 后,H5 触发的事件不直接同步给服务端,而是先发给 App 端的数据采集 SDK,经 App 端数据采集 SDK 二次加工处理后存入本地缓存再进行同步。

本文的内容,主要是回答以下两个问题:

  • App 与 H5 为什么要打通?
  • App 与 H5 该如何打通?

二、App 与 H5 打通原因

App 为什么要与 H5 打通呢?我们主要是从如下几个角度考虑:

  • 数据丢失率
  • 数据准确性
  • 用户标识
  • 基础功能

下面分别为大家进行介绍。

2.1 数据丢失率

在业界,App 端采集数据的丢失率一般在 1% 左右,而 H5 采集数据的丢失率一般在 5% 左右(主要是因为缓存、网络或切换页面等原因)。

因此,如果 App 与 H5 打通,H5 触发的所有事件都可以先发给 App 端数据采集 SDK,经过 App 端二次加工处理后存入本地缓存。在符合特定策略后再进行数据同步,即可把数据丢失率由 5% 降到 1% 左右。

2.2 数据准确性

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

  1. 有些信息通过解析 UserAgent 值根本获取不到,如应用程序的版本号;
  2. 有些信息通过解析 UserAgent 值可以获取到,但内容可能不正确。

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

2.3 用户标识

对于用户在 App 端注册或登录之前,我们一般都是使用匿名 ID 来标识用户。而 App 与 H5 标识匿名用户的规则不一样(Android 一般使用 Android ID,H5 一般使用 Cookie),进而导致一个用户出现两个匿名 ID 的情况。

如果 App 与 H5 打通,就可以将两个匿名 ID 做归一化处理(以 App 端匿名 ID 为准)。

2.4 基础功能

基于 App 与 H5 打通,可以实现诸如可视化全埋点等更加高级的功能。

介绍完打通的原因之后,我们来看下 App 与 H5 如何进行打通。

三、打通方案演进

在打通方面,神策积累了丰富的经验,同时也踩了许多的坑。目前摸索出了三种打通方案,我们将按照技术演进的顺序为大家一一介绍这几种方式,并分析其背景、原理和不足。

3.1 早期版本(1.0)

3.1.1 背景和原理

上一节介绍了为什么要进行 H5 打通,其中有一个点非常关键,“App 与 H5 打通,就可以将两个匿名 ID 做归一化处理”,简单而言就是使用 App 的用户 ID 去标识 H5 的行为,即将 H5 传到服务端的数据添加上 App 的用户 ID,然后上传到服务端,从而统一移动端的用户行为。

本着让 H5 产生的事件数据使用 App 的用户 ID 的思路,首先想到的是将 App 的用户信息发给 H5,神策的早期打通方案也确实是这么做的。

基本的原理是将 JSBridge 注入到,WebView(读者可查看官方的 Building web apps in WebView[1] 了解 Android 和 H5 页面相互调用的操作),JSBridge 中提供方法给 H5 中的 JS 调用,提供的方法会返回 is_login(标识客户是否在 App 登录)、distinct_id(用户 ID)等信息。如图 3-1 所示:

9cf448173568852477f34f6aa7c0c15b.png
图 3-1 早期版本的打通方案

图 3-1 描述了早期版本的打通方案,就是将 App 的用户信息通过 JSBridge 传给 H5 ,然后由 H5 将用户信息添加到事件中从而实现 App 和 H5 页面的用户标识的统一。此种方式对应的代码片段如下:

注入 JSBridge

webView.addJavascriptInterface(new AppWebViewInterface(mContext, properties), "SensorsData_APP_JS_Bridge");

JSBridge 类

class AppWebViewInterface {private static final String TAG = "SA.AppWebViewInterface";private Context mContext;private JSONObject properties;AppWebViewInterface(Context c, JSONObject p) {this.mContext = c;this.properties = p;}@JavascriptInterfacepublic String sensorsdata_call_app() {try {if (properties == null) {properties = new JSONObject();}properties.put("type", "Android");String loginId = SensorsDataAPI.sharedInstance(mContext).getLoginId();if (!TextUtils.isEmpty(loginId)) {properties.put("distinct_id", loginId);properties.put("is_login", true);} else {properties.put("distinct_id", SensorsDataAPI.sharedInstance(mContext).getAnonymousId());properties.put("is_login", false);}return properties.toString();} catch (JSONException e) {SALog.i(TAG, e.getMessage());}return null;}
}

3.1.2 方案缺陷

在了解了这种打通方案后,你可能会注意到这种方式是 H5 直接将数据发送到服务端,跟我们在第一章中介绍的 “进行 H5 打通可以降低数据丢失率” 正好相反;同时你也可能注意到一旦将 WebView 注入了 JSBridge 对象后,那么这个 WebView 加载的所有 H5(这里特指集成了神策 Web JS SDK 的页面) 都会使用 App 提供的 is_login 和 distinct_id 字段。

假如 WebView 加载了另外一家集成了神策 Web JS SDK 的 H5 页面就会出现很大的问题,因为 App 提供的信息添加在了另外一家客户的 H5 里,这样就会给另外一家客户带来很大的麻烦。

注意:

神策 Web JS SDK 提供了是否打通的标志位,可以选择性的对部分 H5 页面进行打通,这一点在上面的流程图中没有体现,本篇文章默认 H5 的标志位都是打通的。

3.2 中期版本(2.0)

3.2.1 背景和原理

1.0 方案介绍了 H5 打通的早期版本的实现方式以及存在的两个问题:一是数据是通过 H5 页面发送的;二是无差别的对待方式会给其他客户的 H5 带来很大的麻烦。为了解决这两个问题,我们修改了 1.0 方案。

首先我们将 H5 页面产生的数据发送到 App,接着 App 端提供校验标识位,用来判断是否校验 H5 数据的数据接收地址和 App 端的数据接收地址。修改上面的流程图,如图 3-2 所示:

6c61fdf091f86feb8048502a2f85e877.png
图 3-2 中期版本的打通方案

图 3-2 描述了 2.0 版本的逻辑,首先 JSBridge 对象提供了 boolean sensorsdata_verify(String event) 方法用来接收和校验 H5 数据,注意这个方法的返回值,true 表示校验通过,数据会通过 App 发送;false 表示校验未通过,数据会通过 H5 发送。

通过这种方式解决了客户自己的 H5 数据可以通过 App 发送,对于其他集成了神策 Web JS SDK 的 H5 页面,因为校验 server_url 不通过,H5 自己发送数据。

了解了原理以后,我们来看一下代码实现:

注入 JSBridge

webView.addJavascriptInterface(new AppWebViewInterface(mContext, enableVerify), "SensorsData_APP_JS_Bridge");

此处代码是给 WebView 注入 JSBridge 对象,注意 AppWebViewInterface 构造方法中有一个 enableVerify 参数,作用是 App 端控制是否需要校验,我们再看 AppWebViewInterface 的代码:

AppWebViewInterface 类

class AppWebViewInterface {private static final String TAG = "SA.AppWebViewInterface";private Context mContext;private boolean enableVerify;AppWebViewInterface(Context c, boolean b) {this.mContext = c;this.enableVerify = b;}@JavascriptInterfacepublic boolean sensorsdata_verify(String event) {try {if (!enableVerify) {sensorsdata_track(event);return true;}return SensorsDataAPI.sharedInstance(mContext)._trackEventFromH5(event);} catch (Exception e) {SALog.printStackTrace(e);return false;}}
}

这里要注意的是 sensorsdata_verify 方法,当 enableVerify 为 false 的时候表示不校验。

因此只要是 H5 发过来的任何数据都通过 App 发送,并且 H5 调用这个方法得到的返回值为 true,表示数据已经在 App 端处理了,H5 将不会再发送此条数据;如果 enableVerify 为 true 的时候,App 会校验 H5 发送数据的 server_url 和 App 的 server_url 是否相同。

如果相同也会返回 true 表示 App 处理此条数据,如果不同会返回 false,表示校验失败,数据还是通过 H5 端去发送。

3.2.2 方案缺陷

通过上一节的原理介绍和代码展示可以发现几个问题:

  • 假如 App 端调用如下代码为 WebView 注入 JSBridge:
webView.addJavascriptInterface(new AppWebViewInterface(mContext, false), "SensorsData_APP_JS_Bridge");

其中,enableVerify 总是设置为 false,那么还是会存在其他客户集成神策 Web JS SDK 的 H5 数据发送到 App 上,造成其他客户数据的丢失以及当前客户脏数据的增多;

  • 假如客户的 App 中有很多 WebView 需要打通,那么我们就需要给每一个 WebView 调用上面这段代码,显得不够优雅;
  • 还有一个原因使得我们必须去改善,那就是我们的可视化全埋点功能依赖于打通功能,或者说就算不打通也希望能做到客户使用可视化全埋点功能的时候可以提示客户去打通。关于可视化全埋点功能可以参考我们官网的『可视化全埋点介绍』[2]。

3.3 成熟版本(3.0)

3.3.1 背景和原理

我们可以看到 2.0 版本是对 1.0 版本缺陷的一个改善,但是并没有解决当客户不校验数据时产生的 “其他客户集成有神策 Web JS SDK 的 H5 页面发送到 App 上,造成其他客户数据的丢失以及当前客户脏数据的增多” 问题。

如果 App 中 WebView 有很多,不得不为每一个 WebView 都注入 JSBridge,使得客户的工作量会很大。同时,也需要为可视化全埋点功能做好技术准备。那么为了解决这些问题,神策对 2.0 版本进行了升级,具体方案分两步:

  1. 为每个 WebView 建立一个通道。通道注入 AppWebViewInterfaceBridge,还可以注入其他的 JSBridge,例如可视化全埋点功能需要的 JSBridge;
  2. 更改数据校验规则。2.0 版本是将校验放在 App 端,现在将校验放在 H5 端,由 H5 端来判断是否需要将数据发送到 App,而 App 只提供 H5 端用于校验的 server_url,server_url 是服务端地址,采集的数据会发往该地址。

此方案的流程如图 3-3 所示:

e620bff42313d5b5041fdfc28664a342.png
图 3-3 成熟版本的打通方案

方案的第一步是建立通道,这需要用到神策 SDK 插件[3]从字节码层面上去实现。具体原理是插件扫描 class 文件中的方法,方法中如果有类似 webview.loadUrl(String url),这样的方法,我们会用将其替换成 SensorsDataAutoTrackHelper.loadUrl(webview, url) ,这个方法就是我们建立的通道,代码如下:

SensorsDataAutoTrackHelper

public static void loadUrl(View webView, String url) { //webView 可能是原生的 Android WebView 也可能是腾讯 X5WebView,这里设置类型为 View,是为了做兼容if (webView == null) {throw new NullPointerException("WebView has not initialized.");}setupH5Bridge(webView); //设置 JSBridge//... 其他类型的 JSBridgeinvokeWebViewLoad(webView, "loadUrl", new Object[]{url}, new Class[]{String.class});//通过反射调用 webview.loadUrl
}private static void setupH5Bridge(View webView) {if (SensorsDataAPI.sharedInstance() instanceof SensorsDataAPIEmptyImplementation) {return;}if (isSupportJellyBean() && SensorsDataAPI.sharedInstance().getConfigOptions() != null && SensorsDataAPI.sharedInstance().getConfigOptions().isAutoTrackWebView) {setupWebView(webView);}if (isSupportJellyBean()) {addWebViewVisualInterface(webView);}}private static void invokeWebViewLoad(View webView, String methodName, Object[] params, Class[] paramTypes) {try {Class<?> clazz = webView.getClass();Method loadMethod = clazz.getMethod(methodName, paramTypes);loadMethod.invoke(webView, params);} catch (Exception e) {SALog.printStackTrace(e);}}

上面的代码是神策 Android SDK 中 SensorsDataAutoTrackHelper 类的代码片段。当集成了神策 Android SDK 插件后,插件会将如下的代码替换掉:

H5Activity

class H5Activity : BaseActivity() {private val TAG: String = "H5Activity"override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_h5)androidWebView.loadUrl("https://www.sensorsdata.cn") 此处的代码将会被插件替换成  SensorsDataAutoTrackHelper.loadUrl(androidWebView, "https://www.sensorsdata.cn")}
}

通过这种操作,我们将原始加载页面的方法替换为通道方法,并且在通道方法中通过反射的方式去调用原本加载页面的逻辑。当然,我们不光可以在通道中注入打通的 JSBridge,还可以注入其他业务需要的 JSBridge,而且不需要客户再去写代码来实现了。

上面介绍了建立通道的方案,那么如何实现在 H5 端进行校验呢,这个比较容易实现了,看一下我们的 JSBridge 类的代码:

AppWebViewInterface

class AppWebViewInterface {private static final String TAG = "SA.AppWebViewInterface";AppWebViewInterface() {}//提供给 H5 端调用,用来获取 App 配置的 server_url@JavascriptInterfacepublic String sensorsdata_get_server_url() {return SensorsDataAPI.sharedInstance().getConfigOptions().isAutoTrackWebView ? SensorsDataAPI.sharedInstance().getServerUrl() : "";}
}

可以看到我们只提供了一个 sensorsdata_get_server_url 方法,H5 会调用此方法获取 App 的 server_url,然后与自己的白名单列表对比。

如果白名单中存在此 server_url,就认为校验通过,数据会发往 App;如果 App 的 server_url 为空或者跟自己的白名单不匹配就认为校验失败,H5 直接发送数据。通过这种方式把校验的主动权放在了 H5 端,解决了 “其他客户集成有神策 Web JS SDK 的 H5 页面发送到 App 上,造成其他客户数据的丢失以及当前客户脏数据的增多” 这个问题。关于神策 Web JS SDK 的信息可以参考技术指南[4]。

3.3.2 方案缺陷

从上一节的介绍能够知道神策 Android 插件需要扫描方法里的代码,这增加了插件编译的时间,不过目前没有更好的办法。后面会将更多需要在 SDK 端完成的配置通过插件来完成,尽量让客户不写代码或者尽量少些代码就能使用我们的 SDK 功能。

四、总结

神策 Android SDK 的 H5 打通方案演进可以说是建立在业务驱动和需求的基础上不断发展的,现在到了 3.0 版本。从目前来看,此版本的思路更好、扩展性也更好。未来有没有更好的方案还是要看具体的业务需要,我们也会持续探索,看看有没有更好的方案。读者如果有更好的想法,也希望能加入开源社区与我们分享。

参考文献

[1]https://developer.android.com/guide/webapps/webview

[2]https://manual.sensorsdata.cn/sa/latest/%E5%8F%AF%E8%A7%86%E5%8C%96%E5%85%A8%E5%9F%8B%E7%82%B9-7541326.html

[3]https://github.com/sensorsdata/sa-sdk-android-plugin2

[4]https://manual.sensorsdata.cn/sa/latest/tech_sdk_client_web-7548149.html

本文作者

2d29b1110c42f26ad1725c05c0592b04.png

张伟

神策数据 | Android 研发工程师

我是张伟,神策数据 Android 研发工程师,主要从事神策 Android SDK 和 Android Plugin 的开发工作,希望通过开源社区这个平台与大家共同学习进步。生活中热爱篮球、看书、旅游,希望我们相聚神策,一起维护神策开源社区和打篮球。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/399370.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简单人物画像_你真的理解用户画像吗?| 船说

“「设计师沙龙」是ARK下半年开始逐渐形成的传统&#xff0c;由ARKers自发组织&#xff0c;分为视觉和交互两类&#xff0c;每月各举办一次。大家围绕一个话题展开&#xff0c;聊聊行业最新案例和工作上的心得&#xff0c;帮助大家共同进步。ARKers表达的观点均为个人见解&…

emoji表情引发的JNI崩溃

今天突然接到客服那边的反馈说&#xff0c;有玩家反馈进游戏后不久就崩溃了&#xff0c;我先是怀疑网络问题&#xff0c;因为一连接聊天成功后就挂了。之后用logcat抓日志&#xff0c;发现挂在jni那里了 JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8…

8cm等于多少像素_「前端剑指offer第5期」物理像素、逻辑像素、CSS像素、PPI、设备像素比是什么...

# 提问物理像素、逻辑像素、CSS像素、PPI、设备像素比是什么&#xff1f;# 回答物理像素代表屏幕上有多少个点&#xff0c;比如1080x2340表示屏幕一排包含1080个物理像素点。逻辑像素表示屏幕展示物体的视觉尺寸是多少。逻辑像素相同表示物体看起来或者打印出来大小一样&#x…

php中获取系统信息的方法

2019独角兽企业重金招聘Python工程师标准>>> $root getenv(DOCUMENT_ROOT); 服务器文档根目录$port getenv(SERVER_PORT); 服务器端口$file getenv(SCRIPT_NAME); 当前执行文件$ua getenv(HTTP_USER_AGENT); 用户UA$method getenv(REQUEST_METHOD); 请求方法$p…

[独家全程图解]ThinkPHP6框架的下载与安装

http://www.php.cn/wenda/159638.html 1. ThinkPHP大事记 2017年4月27日,ThinkPHP5.1-beta.1发布 2017年12月31日, ThinkPHP5.1.0发布,标志着快速进入迭代期 2019年3月3日, ThinkPHP5.1已更新迭代到第35个版本(5.1.35) 2019年3月22日, ThinkPHP5.2的 dev 开发版本也发布了 …

玩转CSS3(一)----CSS3实现页面布局

请珍惜小编劳动成果&#xff0c;该文章为小编原创&#xff0c;转载请注明出处。 摘要&#xff1a; CSS3相对CSS2增加了一些新的布局方式&#xff1a;多栏布局和盒子布局。在这篇文章中&#xff0c;将对CSS2的布局进行简单的回忆&#xff0c;并总结CSS3的布局方式。DIVCSS其实是…

openglshader实现虚拟场景_云桌面,实现办公终端的统一管理与运维

随着无纸化办公和智能化办公的不断推进&#xff0c;在办公过程中传统PC电脑的缺点愈发凸显。传统电脑的性能会随着使用时长增加而降低&#xff0c;系统维护处理时效性较弱&#xff0c;出现问题需要运维人员到现场解决&#xff0c;费时费力。如果出现更换设备的情况&#xff0c;…

前端 == Ajax

Django-Ajax 1.目录 ajax 准备知识&#xff1a;json ajax 简介 jquery 实现的ajax js 实现的ajax jquery.serialize() 上传文件 同源策略与jsonp 2.准备知识&#xff1a;json 1.什么是 json ? 个人回答&#xff1a; json 的作用是 数据交换格式。&#xff08;通过序列化和反序…

WordPress 5.0 换回老版”Classic Editor”经典编辑器教程

WordPress 5.0 正式采用了全新的“Block Editor”编辑器&#xff0c;从而替换了原有“Classic Editor”编辑器&#xff0c;相信有很多人和子凡一样会不习惯或者不喜欢新编辑器&#xff0c;那么新版 WordPress 该如何换回原来的 WordPress 编辑器呢&#xff1f; 不可否认 WordPr…

html5视频播放

<video width"320" height"240" controls"controls"> <source src"test.mp4" type"video/mp4"> Your browser does not support the video tag. </video> IE9对于video标签确实是不支持的&…

判断是否存在此对象_JVM的垃圾回收机制,判断对象是否死亡

这节我们主要讲垃圾收集的一些基本概念&#xff0c;先了解垃圾收集是什么、然后触发条件是什么、最后虚拟机如何判断对象是否死亡。一、前言我们都知道Java和C有一个非常大的区别就是Java有自动的垃圾回收机制&#xff0c;经过半个多世纪的发展&#xff0c;Java已经进入了“自动…

Software--Developer Tools_

Log4net 日志框架 由 Java log4j 移植到 .Net 平台的开源日志框架。 http://logging.apache.org/log4net/index.html 转载于:https://www.cnblogs.com/masterSoul/p/7832317.html

万兆网卡实际吞吐量_AKITIO 10G/NBASE-T PCIe 网卡开箱拆解评测

前言今天来到koolshare评测室的是AKITIO的10G/NBASE-T PCIe扩展网卡(官网链接)&#xff0c;采用PCIe2.0 x4接口&#xff0c;支持10G/5G/2.5G/1G/100Mbps&#xff0c;可以在100m的CAT-6A线缆上达到最高10Gbps的链接速率&#xff0c;或者在100m的CAT-5e线缆上达到最高5Gbps的链接…

【RabbitMQ】8、RabbitMQ之mandatory和immediate

1. 概述 mandatory和immediate是AMQP协议中basic.publish方法中的两个标识位&#xff0c;它们都有当消息传递过程中不可达目的地时将消息返回给生产者的功能。对于刚开始接触RabbitMQ的朋友特别容易被这两个参数搞混&#xff0c;这里博主整理了写资料&#xff0c;简单讲解下这两…

获取异常信息_如何在 ASP.NET Core 中实现全局异常拦截

异常是一种运行时错误&#xff0c;当异常没有得到适当的处理&#xff0c;很可能会导致你的程序意外终止&#xff0c;这篇就来讨论一下如何在 ASP.Net Core MVC 中实现全局异常处理&#xff0c;我会用一些 样例代码 和 截图 来说明这些概念。全局异常处理其实在 ASP.Net Core MV…

Hadoop学习笔记—15.HBase框架学习(基础知识篇)

Hadoop学习笔记—15.HBase框架学习&#xff08;基础知识篇&#xff09; HBase是Apache Hadoop的数据库&#xff0c;能够对大型数据提供随机、实时的读写访问。HBase的目标是存储并处理大型的数据。HBase是一个开源的&#xff0c;分布式的&#xff0c;多版本的&#xff0c;面向列…

二维数组删除_「leetcode」数组:总结篇!(一文搞懂数组题目)

数组理论基础数组是非常基础的数据结构&#xff0c;在面试中&#xff0c;考察数组的题目一般在思维上都不难&#xff0c;主要是考察对代码的掌控能力也就是说&#xff0c;想法很简单&#xff0c;但实现起来 可能就不是那么回事了。首先要知道数组在内存中的存储方式&#xff0c…

TextView显示颜色高亮的问题

TextView textView (TextView) findViewById( R.id.tv );String text "<font color\"#d93b3a\">" "快过年了" "</font>" "<font color\"#666666\">" "哈哈哈哈" "</…

开源GIS解决方案,暨GeoServer+OpenLayer结合开发总结

http://linking123.github.io/2018/07/21/%E5%BC%80%E6%BA%90GIS%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%EF%BC%8C%E6%9A%A8GeoServer-OpenLayer%E7%BB%93%E5%90%88%E5%BC%80%E5%8F%91%E6%80%BB%E7%BB%93/ – 0.感叹 – 1.文档说明 – 2.文档内容 — 2.1 GeoServer - 地图服务 —…