前言
近年来,混合开发越来越风行,App 与 H5 的买通需要也越来越迫切。
那什么是 App 与 H5 买通呢?
所谓“买通”,是指 H5 集成 JavaScript 数据采集 SDK 后,H5 触发的事件不间接同步给服务端,而是先发给 App 端的数据采集 SDK,经 App 端数据采集 SDK 二次加工解决后存入本地缓存再进行同步。
本文的内容,次要是答复以下两个问题:
App 与 H5 为什么要买通?
App 与 H5 该如何买通?
App 与 H5 买通起因
App 为什么要与 H5 买通呢?咱们次要是从如下几个角度思考:
数据失落率
数据准确性
用户标识
根底性能
上面别离为大家进行介绍。
数据失落率
在业界,App 端采集数据的失落率个别在 1% 左右,而 H5 采集数据的失落率个别在 5% 左右(次要是因为缓存、网络或切换页面等起因)。因而,如果 App 与 H5 买通,H5 触发的所有事件都能够先发给 App 端数据采集 SDK,通过 App 端二次加工解决后存入本地缓存。在合乎特定策略后再进行数据同步,即可把数据失落率由 5% 降到 1% 左右。
数据准确性
家喻户晓,H5 无奈间接获取设施的相干信息,只能通过解析 UserAgent 值获取无限的信息,而解析 UserAgent 值,至多会面临如下两个问题:
有些信息通过解析 UserAgent 值基本获取不到,如应用程序的版本号;
有些信息通过解析 UserAgent 值能够获取到,但内容可能不正确。
如果 App 与 H5 买通,由 App 端数据采集 SDK 补充这些信息,即可确保事件信息的准确性和完整性。
用户标识
对于用户在 App 端注册或登录之前,咱们个别都是应用匿名 ID 来标识用户。而 App 与 H5 标识匿名用户的规定不一样(Android 个别应用 Android ID,H5 个别应用 Cookie),进而导致一个用户呈现两个匿名 ID 的状况。如果 App 与 H5 买通,就能够将两个匿名 ID 做归一化解决(以 App 端匿名 ID 为准)。
根底性能
基于 App 与 H5 买通,能够实现诸如可视化全埋点等更加高级的性能。
介绍完买通的起因之后,咱们来看下 App 与 H5 如何进行买通。
买通计划演进
在买通方面,神策积攒了丰盛的教训,同时也踩了许多的坑。目前摸索出了三种买通计划,咱们将依照技术演进的程序为大家一一介绍这几种形式,并剖析其背景、原理和有余。
晚期版本(1.0)
背景和原理
上一节介绍了为什么要进行 H5 买通,其中有一个点十分要害,“App 与 H5 买通,就能够将两个匿名 ID 做归一化解决”,简略而言就是应用 App 的用户 ID 去标识 H5 的行为,行将 H5 传到服务端的数据增加上 App 的用户 ID,而后上传到服务端,从而对立挪动端的用户行为。
本着让 H5 产生的事件数据应用 App 的用户 ID 的思路,首先想到的是将 App 的用户信息发给 H5,神策的晚期买通计划也的确是这么做的。根本的原理是将 JSBridge 注入到 WebView(读者可查看官网的 Building web apps in WebView 理解 Android 和 H5 页面互相调用的操作),JSBridge 中提供办法给 H5 中的 JS 调用,提供的办法会返回 is_login(标识客户是否在 App 登录)、distinct_id(用户 ID)等信息。如图 3-1 所示:
图 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;} @JavascriptInterface public 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; }}
计划缺点
在理解了这种买通计划后,你可能会留神到这种形式是 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 的标记位都是买通的。
中期版本(2.0)
背景和原理
1.0 计划介绍了 H5 买通的晚期版本的实现形式以及存在的两个问题:一是数据是通过 H5 页面发送的;二是无差别的看待形式会给其余客户的 H5 带来很大的麻烦。为了解决这两个问题,咱们批改了 1.0 计划。
首先咱们将 H5 页面产生的数据发送到 App,接着 App 端提供校验标识位,用来判断是否校验 H5 数据的数据接管地址和 App 端的数据接管地址。批改下面的流程图,如图 3-2 所示:
图 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;} @JavascriptInterface public 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 端去发送。
计划缺点
通过上一节的原理介绍和代码展现能够发现几个问题:
如果 App 端调用如下代码为 WebView 注入 JSBridge:
webView.addJavascriptInterface(new AppWebViewInterface(mContext, false), “SensorsData_APP_JS_Bridge”);
其中,enableVerify 总是设置为 false,那么还是会存在其余客户集成神策 Web JS SDK 的 H5 数据发送到 App 上,造成其余客户数据的失落以及以后客户脏数据的增多;
如果客户的 App 中有很多 WebView 须要买通,那么咱们就须要给每一个 WebView 调用下面这段代码,显得不够优雅;
还有一个起因使得咱们必须去改善,那就是咱们的可视化全埋点性能依赖于买通性能,或者说就算不买通也心愿能做到客户应用可视化全埋点性能的时候能够提醒客户去买通。对于可视化全埋点性能能够参考咱们官网的『可视化全埋点介绍』。
成熟版本(3.0)
背景和原理
咱们能够看到 2.0 版本是对 1.0 版本缺点的一个改善,然而并没有解决当客户不校验数据时产生的“其余客户集成有神策 Web JS SDK 的 H5 页面发送到 App 上,造成其余客户数据的失落以及以后客户脏数据的增多”问题。如果 App 中 WebView 有很多,不得不为每一个 WebView 都注入 JSBridge,使得客户的工作量会很大。同时,也须要为可视化全埋点性能做好技术筹备。那么为了解决这些问题,神策对 2.0 版本进行了降级,具体计划分两步:
为每个 WebView 建设一个通道。这个通道不光能够注入 AppWebViewInterface Bridge 还能够注入其余的 JSBridge,例如可视化全埋点性能须要的 JSBridge;
更改数据校验规定。2.0 版本是将校验放在 App 端,当初将校验放在 H5 端,由 H5 端来判断是否须要将数据发送到 App,而 App 只提供 H5 端用于校验的 server_url(server_url 是服务端地址,采集的数据会发往该地址)。
此计划的流程如图 3-3 所示:
图 3-3 成熟版本的买通计划
计划的第一步是建设通道,这须要用到神策 SDK 插件从字节码层面下来实现。具体原理是插件扫描 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 //… 其余类型的 JSBridge invokeWebViewLoad(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 @JavascriptInterface public 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 的信息能够参考这里。
计划缺点
从上一节的介绍可能晓得神策 Android 插件须要扫描办法里的代码,这减少了插件编译的工夫,不过目前没有更好的方法。前面会将更多须要在 SDK 端实现的配置通过插件来实现,尽量让客户不写代码或者尽量少些代码就能应用咱们的 SDK 性能。
总结
神策 Android SDK 的 H5 买通计划演进能够说是建设在业务驱动和需要的根底上一直倒退的,当初到了 3.0 版本。从目前来看,此版本的思路更好、扩展性也更好。将来有没有更好的计划还是要看具体的业务须要,咱们也会继续摸索,看看有没有更好的计划。读者如果有更好的想法,也心愿能退出开源社区与咱们分享。
文章起源:公众号神策技术社区