webview应用首屏优化

这里首屏优化是指用户安装应用后,首次打开应用消耗时间的优化。 其中比较耗时的一点是首次打开webview应用,加载静态资源。 优化思路是,首次打开应用,使用客户端拦截请求,返回本地文件。以后的请求优化由ServiceWorker接管。

webview请求过程

一个静态资源的请求,分为以下4个过程按顺序执行

  1. ServiceWorker
  2. http缓存
  3. App应用拦截
  4. 网络服务

注意:sw.js文件的规则不在此列,sw.js文件好像始终请求网络

image.png

有以下几点需要注意:

第一步 ServiceWorker 必须是安装好的

第一次进入应用,ServiceWorker并未安装,所以会被直接跳过,进入http缓存

ServiceWorker的请求,也会先查看http缓存

就是说,从ServiceWorker发出的请求,也会遵循http缓存规范,http缓存中有的文件,会被直接返回给ServiceWorker

只有ServiceWoker和http缓存都未命中,才会被App应用拦截

App应用拦截的限制

App应用拦截webview请求,是通过复写shouldInterceptRequest方法实现

@TargetApi(VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view,
                    WebResourceRequest request) {

经过测试,他有限制:
只会拦截html请求和写在html中的资源请求(即通过<link><script>等直接加载的资源。通过js请求的资源都不会被拦截,例如ajax请求、main.js文件中请求的sw.js、ServiceWorker中发起的请求)。没有深入研究测试,理解也可能有误。


优化思路

App应用安装时候,将首屏所需静态资源下载到本地文件中

首屏

首次打开应用,ServiceWorker未安装,也没有任何http缓存,所以所有请求会直接被App应用拦截,在这一步返回本地文件,达到秒开效果。
同时不拦截sw.js文件(ServiceWorker的注册文件),去网络服务器请求sw.js文件进行注册安装。

第二次打开

因为在首屏打开应用同时,sw.js也同时注册安装好了,同时通过cacheAll,也在ServiceWorker中缓存了最新的网络服务器中文件。
所以第二次打开,所有的请求,都可以被ServiceWorker进行拦截处理,根据上面的注意事项(通过js请求的资源都不会被App应用拦截),所以第二次及之后的请求,基本就和App应用拦截无关了。

ServiceWoker缓存的更新

  1. 在每次请求时候,返回ServiceWorker缓存同时,请求最新文件(副作用,在每次页面版本发生改变时候,第二次进入应用会卡,因为第一次返回ServiceWorker缓存,不会卡,但是请求到了新的index.html,第二次进入应用,根据新的index.html,发生变化的静态资源,都会从网络请求等待,第三次进入应用才不会卡)
  2. 更新sw.js文件时候,更新所有静态资源文件(未实践,应该可行,且无上面的副作用)

ServiceWoker文件sw.js自身的更新

对sw.js不设置缓存,每次都到网络请求最新sw.js文件即可(对于如何安装更新sw.js,本文不探讨)

具体实现

首屏加载

首屏资源请求流程
sw.js文件返回后,请求最新资源流程

1. App缓存文件存放位置

image.png

2. 声明拦截列表

private void initData() {
        // 主页
        mMap.put(BaseUrl, "index.html");
        mMap.put(BaseUrl+"/", "index.html");
        mMap.put(BaseUrl + "/index.html", "index.html");
        // js文件
        mMap.put(BaseUrl + "/main-v1.js", "main.js");
        // css文件
        mMap.put(BaseUrl + "/static/css/main-v1.css", "static/css/main-v1.css");
        // 图片
        mMap.put(BaseUrl + "/favicon.ico", "favicon.ico");
        mMap.put(BaseUrl + "/images/log.png", "images/log.png");
    }

3. App应用拦截

            @TargetApi(VERSION_CODES.LOLLIPOP)
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view,
                    WebResourceRequest request) {
                String url = request.getUrl().toString();
                Log.d(TAG, "shouldInterceptRequest>5.0: url = " + url);
                if (!mIsLoadLocal) {

                    return super.shouldInterceptRequest(view, request);
                }
                if (mDataHelper.hasLocalResource(url)) {
                    Log.d(TAG, "shouldInterceptRequest>5.0: 资源命中:url="+url);
                    // super.shouldInterceptRequest(view, request); // 同时去请求网络
                    WebResourceResponse response =
                            mDataHelper.getReplacedWebResourceResponse(getApplicationContext(),
                                    url);
                    if (response != null) {
                        return response;
                    }
                }
                return super.shouldInterceptRequest(view, request);
            }

第二次加载

第二次加载资源请求流程
ServiceWorker处理流程

之后的处理流程都是类似的

说明

1. 和http缓存结合使用

ServiceWorker中的请求,也会去http缓存中抓取的,所以可以结合使用,缓存策略:

  • index.html设定no-cache
  • 静态资源文件缓存1年(通过每次打包后新版本号更新)
    以Nginx为例:
        location / {
            # root   html;
            root /Users/frru/nginxServers;
            index  index.html index.htm;
            autoindex on;               ##显示索引  
            autoindex_exact_size on;    ##显示大小
            autoindex_localtime on;     ##显示时间
            if ($request_uri ~* ".html|htm") {
                add_header Cache-Control  "no-cache";
            }
            if ($request_uri ~* ".css|js|png") {
                expires 360d;
                # add_header Cache-Control "public, max-age=2592000, s-maxage=2592000";
                add_header wall  "hey!guys!give me a star.";
            }
        }

2. 每次项目打包时候,请同步更新sw.js文件,主要是更新其中cacheAll部分文件列表

例如版本变为v2之后,sw.js中cacheAll部分应该为

//Service Worker安装事件,其中可以预缓存资源
this.addEventListener('install', function(event) {
 console.log('install'); 
  //需要缓存的页面资源
  var urlsToPrefetch = [
    './index.html',
    './main-v2.js',
    './static/css/main-v2.css',
    './static/image/log.png',
  ];

  event.waitUntil(
    caches.open(OFFLINE_CACHE_NAME).then(function(cache) {
      return cache.addAll(urlsToPrefetch);
    })
  );      
});

3. 注意:并未实践

以上部分,关于sw.js部分的主动更新文件,我并未实现调试,理论上可行。之前我是采取边返还ServiceWorker缓存,边请求最新数据的方案,副作用上面已经说明了。

4. sw.js主动更新文件和边返回缓存,边加载新资源,可以结合使用

5. 关于缓存文件列表

现在打包工具一般都会生成静态资源列表,例如Create React App打包项目会生成一个asset-manifest.json文件:

{
  "main.css": "./static/css/main.484bfb43.chunk.css",
  "main.js": "./static/js/main.a763f74c.chunk.js",
  "main.js.map": "./static/js/main.a763f74c.chunk.js.map",
  "runtime~main.js": "./static/js/runtime~main.9eb600ee.js",
  "runtime~main.js.map": "./static/js/runtime~main.9eb600ee.js.map",
  "static/css/2.81f174d0.chunk.css": "./static/css/2.81f174d0.chunk.css",
  "static/js/2.3caf1636.chunk.js": "./static/js/2.3caf1636.chunk.js",
  "static/js/2.3caf1636.chunk.js.map": "./static/js/2.3caf1636.chunk.js.map",
  "index.html": "./index.html",
  "precache-manifest.95764df349dcc4f784ad6dbed9254278.js": "./precache-manifest.95764df349dcc4f784ad6dbed9254278.js",
  "service-worker.js": "./service-worker.js",
  "static/css/2.81f174d0.chunk.css.map": "./static/css/2.81f174d0.chunk.css.map",
  "static/css/main.484bfb43.chunk.css.map": "./static/css/main.484bfb43.chunk.css.map"
}

App应用可以利用,拉取静态文件。
也可以用来生成ServiceWorker主动获取文件列表

腾讯VasSonic框架

简介:腾讯SNG增值产品部技术团队研发的轻量级高性能Hybrid框架VasSonic
官方介绍文档

业务场景

主要是应对手Q的业务,一些重点常用业务,例如游戏分发中心、会员特权中心、个性装扮商场等,是H5页面,首屏加载时候,会比较慢。
随着业务发展,有些业务形态发生了变化,例如个性推荐是动态内容。

解决方案

VasSonic的前身

  1. 终端优化
    一些常见的首页优化:懒加载、X5内核预加载、WebView对象复用
  2. 静态直出
  3. 离线预推(即熟悉的离线包)
    VasSonic做了功能增强,即离线增量包,大大减少了包大小。
  4. 动态直出功能
    因为业务形态发生了变化(个性推荐),做了对应的功能。
    实时拉取用户数据并在服务端渲染后返回给客户端,也就是动态直出的方案。
  5. webso的尝试
    QQ空间技术团队在解决动态直出方面的wns+html解决方案。不太适合手Q业务场景。有一定的借鉴参考。

综上,不管改进下,最后诞生了VasSonic。

VasSonic的诞生

  1. 并行加载
    就是把WebView初始化和请求资源由原来的串行改成并行,减少等待时间。
  2. 动态缓存
    把页面拆分成静态部分和动态部分,动态部分更新后去主动更新界面。
  3. 页面分离
    对上面动态缓存的具体实现

后面的小章节都是具体使用VasSonic的规范。

???

VasSonic预加载

这两种方式感觉都是针对手Q业务场景,在用户打开手Q时候,预加载一些重要页面和预判页面。

腾讯浏览服务 Service Worker最佳实践

官文

ServiceWorker的不用讲了,这里关于首屏的加载,比较狠,直接打成压缩文件,让x5内核注册成ServiceWorker,省去了用户需要打开页面才能注册ServiceWorker的过程。

X5内核Service Worker功能扩展

首次访问解决方案

首次访问解决方案旨在用户访问业务前实现业务的资源缓存,让用户在第一次真正访问业务时能够让业务页面以最快的速度展示出来。针对该主旨,X5内核实现了三套具体实现方案:

  1. 离线包方式
  2. X5内核后台云下发指令
  3. X5内核扩展接口
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350