H5预加载的实现

预加载说小了就是个提前load,说大了,就是HybridApp的最基础功能。
Html5官方有个prefetch的attribute,用来做页面间的prefetch。其实在native跳h5容器的过程中更需要这种流程,否则很容易导致体验劣化。
其实方案蛮多的:

  • 直接下载url + WebView#loadDataWithBaseURL
    优点是简单,缺点就是只能预加载下个页面的html内容,这样一来,实际效果很差,常常只能预加载1%不到的数据量

  • 用隐藏的WebViewFragment加载,需要时做动画显示
    优点也是速度最快,坑就非常多了:

    • html加载过程完整,控制行为需要在WebChromeClient/WebViewClient做非常多的分支判断
    • 需要兼容动画效果
    • 需要控制back
    • 最重要的,会导致js中的window#onload之类的函数不合理的触发,引发逻辑问题
  • 用WebView提前load,靠WebView的cache
    仍然不复杂,因为在预加载时可以禁掉js,真正加载的时候允许js,上一个方案的控制部分就解决了。问题在于,WebView的Cache是一体的,一清全清,一旦需要清理缓存那就会出现各种各样的全局问题了。

  • 用WebView提前load,手写缓存
    最复杂的方案,但是目前看没什么坑。
    具体来说:

    • prefetch阶段,重写WebViewClient#onLoadResource,将所有资源都下载到本地
      • 本地缓存的文件结构:用baseUrl#hashCode生成一个文件夹,每一个资源都缓存到baseUrl#hashCode/resourceUrl#hashCode的文件中
      • 缓存过程中,要标记一下,保证未完成的资源不被读取
      • mime和charset通过HttpURLConnection的content-type来解析
    • 复用阶段,重写WebViewClient#shouldInterceptRequest,重定向所有资源请求到本地stream
    • 清理阶段,交给外部,外部通过baseUrl,或者根目录来清理对应的缓存

上面的思路发现了一个问题:所有预加载的网页会被下载两遍,因为并没有阻断WebView自己的下载。解决这个最好办法就是给WebView加一个proxy,在proxy中存储所有数据。乍看之下,WebView没有Proxy接口,所以网上就奔放了。然而,细想一下,shouldInterceptRequest不就是Proxy吗?只需要在原始的InputStream上wrap一层,在wrapper中做想做的事情就好了。所以最终方案是:

  • prefetch阶段,重写WebViewClient#shouldInterceptRequest,为每一个请求生成HttpUrlConnection,为得到的InputStream注册一个onValueRead回调,回调中存储读取到的数据
    • mime会有不严谨的情况,导致显示错误,解决方法

其他部分和上一个方法一致。

细说起来,还有几个值得改进的地方:
- fat32对于多文件支持很差。所以最好能做自己的资源定位方法
- VasSonic对于流的处理更加细致,可以做一个adapter层,如果文件只下载了一半也能用
- 目前所有的东西都是基于md5,其实改成原始文件路径可能也不错
- 部分资源是可以做预置的,需要的只是在stream的来源上进行区分(File或net)

猜你喜欢

转载自blog.csdn.net/pouloghost/article/details/76512339