开发

厂商宿主系统浏览内核能力拓展

1 背景

  • 为提升用户使用体验,持续优化小程序启动性能等原因,小程序需要对浏览内核进行功能拓展,以便实现统计小程序性能指标等功能。
  • 目前百度浏览内核中已经实现了相关功能,没有接入百度浏览内核的厂商浏览器宿主则需要按要求拓展系统浏览内核功能。

2 方案

  1. 宿主侧:引入小程序扩展模块,WebView实现小程序拓展接口SwanWebViewExtension,要求详见章节§3.2。
  2. 小程序:尝试调用宿主WebViewsetWebSwanClient方法,注入一个小程序拓展实现对象WebSwanClient
  3. 宿主侧:维护WebSwanClient对象,并在相关的事件发生时,回调WebSwanClient中对应的事件方法,要求详见章节§3.3。

3 宿主侧实现要求

3.1 WebView 工程引入小程序扩展模块的依赖

  • 依赖组件:implementation deps.business.swan.webview.extension

WebView 模块 build.gradle 示例如下:

1
2
3
dependencies {
implementation deps.business.swan.webview.extension
}

3.2 WebView 实现小程序拓展接口

  • WebView 类实现接口:com.baidu.swan.webview.extension.SwanWebViewExtension

WebView.java 修改示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package android.webkit;

import com.baidu.swan.webview.extension.SwanWebViewExtension;

/**
* A View that displays web pages.
*/
@Widget
public class WebView extends AbsoluteLayout
implements SwanWebViewExtension, // WebView 类的实现 WebSwanClientHolder 接口
ViewTreeObserver.OnGlobalFocusChangeListener,
ViewGroup.OnHierarchyChangeListener, ViewDebug.HierarchyHandler {

/** 维护 WebSwanClient 对象 */
private WebSwanClient mWebSwanClient = WebSwanClient.DEFAULT;

/**
* 设置小程序扩展对象
* @param webSwanClient 小程序扩展对象
*/
public void setWebSwanClient(WebSwanClient webSwanClient) {
mWebSwanClient = webSwanClient;
}

/**
* 返回小程序扩展对象
* @return 小程序扩展对象
*/
public WebSwanClient getWebSwanClient() {
return mWebSwanClient;
}
}

3.3 WebSwanClient中各方法的调用要求

宿主浏览内核按照WebSwanClient各方法的要求,在浏览内核中实现相应的埋点和调用逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class WebSwanClient implements NoProGuard {
public static final WebSwanClient DEFAULT = new WebSwanClient();

/**
* 首次内容绘制,FCP(First Contentful Paint)
* 浏览内核性能 api 的 TimeLine 中给出 FCP 时向小程序回调该事件
*
* @param webView WebView
* @param currentUrl 当前 url
*/
public void onFirstContentfulPaint(WebView webView, String currentUrl) {
}

/**
* 首次有效绘制,FMP(First Meaningful Paint)
* 需要宿主实现回调规则:页面元素首次绘满一屏时向小程序回调该事件,(元素高度>=页面高度);
*
* @param webView WebView
* @param currentUrl 当前 url
*/
public void onFirstScreenPaintFinished(WebView webView, String currentUrl) {
}
}

附1 浏览内核打点方案参考

首屏回调整体流程图如下所示:
首屏回调整体流程图
内核实现方案:主要实现是在blink内核的LocalFrameView类里面,在这个类里添加私有变量FirstScreenState first_screen_state_,并通过更新first_screen_state_
状态来实现上屏时间打点。 FirstScreenState定义如下:

1
2
3
4
5
6
7
enum FirstScreenState {
beforeFirstScreen,
didFirstPaint,
didFirstScreenLayout,
didFirstScreenPaint,
maxFirstScreenState
};

覆盖场景:

  • 网页高度大于WebView高度(通俗理解为屏幕高度)网页:first_screen_state_状态更新顺序为 didFirstPaint->didFirstScreenLayout->didFirstScreenPaint。在解析网页过程中不断更新布局高度,当已布局的网页高度大于WebView高度时会触发;等再次来绘制任务时,会触发首屏回调打点。
  • 网页高度小于等于WebView高度页面:在Document的domComplete(页面加载完毕)状态时强制触发didFirstScreenPaint状态,触发首屏回调,并且按优先级把FirstTextPaint/ImagePaint/FirstContentfullPaint时间校准为上屏时间。