日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android Webview H5 秒开方案实现

發(fā)布時間:2025/6/17 Android 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android Webview H5 秒开方案实现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本文首發(fā)于微信公眾號「玉剛說」

原文鏈接:Android Webview H5 秒開方案實現(xiàn)

前言

現(xiàn)在許多app都嵌入了H5頁面, 然而WebView加載速度慢這個問題卻一直影響著用戶的體驗, 所以本文就如何提高H5頁面的加載速度展開討論。

問題原因

首先我們需要知道為什么WebView的加載速度那么慢。H5頁面的渲染速度其實主要取決于兩個

  • js解析效率
    如果js文件較多、解析比較復(fù)雜, 就會導(dǎo)致渲染速度較慢?;蛘呤謾C(jī)的硬件性能比較差的話, 也會導(dǎo)致渲染速度比較慢。
  • 頁面資源的下載
    一般加載一個H5頁面, 都會產(chǎn)生較多的網(wǎng)絡(luò)請求, 如圖片、js文件、css文件等, 需要將這些資源都下載完成之后才能完成渲染, 這樣也會導(dǎo)致頁面渲染速度變慢
  • 對于上面的第一點, 其實主要是由前端代碼和手機(jī)硬件決定的, 因為我們這里討論的是對于app的性能優(yōu)化, 暫時不考慮, 所以我們可以從第二點做文章, 主要思路就是一些資源文件都使用App本地資源, 而不需要從網(wǎng)絡(luò)下載, 從而提高頁面的打開速度。

    代碼實現(xiàn)

    以加載玉剛說的renyugang.io/post/75這個頁面為例。

    首先將一些資源文件放在本地的assets目錄, 然后重寫WebViewClient的shouldInterceptRequest(WebView view, String url)和shouldInterceptRequest(WebView view, WebResourceRequest request)這兩個方法, 對訪問地址進(jìn)行攔截, 當(dāng)url地址命中本地配置的url時, 使用本地資源替代, 否則就使用網(wǎng)絡(luò)上的資源。

    YuGangShuoWebActivity:

    mWebview.setWebViewClient(new?WebViewClient()?{
    ????//?設(shè)置不用系統(tǒng)瀏覽器打開,直接顯示在當(dāng)前Webview
    ????@Override
    ????public?boolean?shouldOverrideUrlLoading(WebView?view,?String?url)?{
    ??????view.loadUrl(url);
    ??????return?true;
    ????}

    ????@Override
    ????public?WebResourceResponse?shouldInterceptRequest(WebView?view,?String?url)?{
    ??????//?如果命中本地資源,?使用本地資源替代
    ??????if?(mDataHelper.hasLocalResource(url))?{
    ??????????WebResourceResponse?response?=
    ??????????????????mDataHelper.getReplacedWebResourceResponse(getApplicationContext(),
    ??????????????????????????url);
    ??????????if?(response?!=?null)?{
    ??????????????return?response;
    ??????????}
    ??????}
    ??????return?super.shouldInterceptRequest(view,?url);
    ????}

    ????@TargetApi(VERSION_CODES.LOLLIPOP)
    ????@Override
    ????public?WebResourceResponse?shouldInterceptRequest(WebView?view,
    ??????????WebResourceRequest?request)?{
    ??????String?url?=?request.getUrl().toString();
    ??????if?(mDataHelper.hasLocalResource(url))?{
    ??????????WebResourceResponse?response?=
    ??????????????????mDataHelper.getReplacedWebResourceResponse(getApplicationContext(),
    ??????????????????????????url);
    ??????????if?(response?!=?null)?{
    ??????????????return?response;
    ??????????}
    ??????}
    ??????return?super.shouldInterceptRequest(view,?request);
    ????}

    });?
    復(fù)制代碼

    DataHelper是一個工具類, 代碼如下:

    public?class?DataHelper?{

    ????private?Map<String,?String>?mMap;

    ????public?DataHelper()?{
    ????????mMap?=?new?HashMap<>();
    ????????initData();
    ????}

    ????private?void?initData()?{
    ????????String?imageDir?=?"images/";
    ????????String?pngSuffix?=?".png";
    ????????mMap.put("http://renyugang.io/wp-content/themes/twentyseventeen/style.css?ver=4.9.8",
    ????????????????"css/style.css");
    ????????mMap.put("http://renyugang.io/wp-content/uploads/2018/06/cropped-ryg.png",
    ????????????????imageDir?+?"cropped-ryg.png");
    ????????...
    ????}

    ????public?boolean?hasLocalResource(String?url)?{
    ????????return?mMap.containsKey(url);
    ????}

    ????public?WebResourceResponse?getReplacedWebResourceResponse(Context?context,?String?url)?{
    ????????String?localResourcePath?=?mMap.get(url);
    ????????if?(TextUtils.isEmpty(localResourcePath))?{
    ????????????return?null;
    ????????}
    ????????InputStream?is?=?null;
    ????????try?{
    ????????????is?=?context.getApplicationContext().getAssets().open(localResourcePath);
    ????????}?catch?(Exception?e)?{
    ????????????e.printStackTrace();
    ????????????return?null;
    ????????}
    ????????String?mimeType;
    ????????if?(url.contains("css"))?{
    ????????????mimeType?=?"text/css";
    ????????}?else?if?(url.contains("jpg"))?{
    ????????????mimeType?=?"image/jpeg";
    ????????}?else?{
    ????????????mimeType?=?"image/png";
    ????????}
    ????????WebResourceResponse?response?=?new?WebResourceResponse(mimeType,?"utf-8",?is);
    ????????return?response;
    ????}


    }
    復(fù)制代碼

    我們抓包看一下修改前后的網(wǎng)絡(luò)請求的對比。

    優(yōu)化前, 有n個實際發(fā)出的網(wǎng)絡(luò)請求:

    優(yōu)化后, 只有一個實際發(fā)出的網(wǎng)絡(luò)請求。并且為了和網(wǎng)絡(luò)的資源圖片做區(qū)分, 我在兩張本地圖片中加了“本地”的水印, 能明顯看到這時候加載的是本地圖片:

    另外再提一點, 對于WebViewClient的shouldInterceptRequest(WebView view, String url)和shouldInterceptRequest(WebView view, WebResourceRequest request)這兩個方法, 經(jīng)本人親測, 重寫其中的任何一個都能生效, 后面一個shouldInterceptRequest(WebView view, WebResourceRequest request)一般是5.0以上的系統(tǒng)使用。我個人的建議是把這兩個方法都重寫了。

    關(guān)于WebView的緩存

    我們再看一個有意思的現(xiàn)象, 在不配置本地資源的時候, 我們第一次打開頁面, 產(chǎn)生了n多個請求。但是當(dāng)我們退出后再次打開這個頁面(沒有設(shè)置加載本地資源)的時候, 居然只發(fā)生了一次請求, 這現(xiàn)象與加載本地資源十分相似。

    這是為什么呢?
    我們卸載app, 抓包, 再次打開頁面, 以banner圖片請求的舉例。

    我們觀察這個請求的response的headers中的參數(shù), 注意到這么幾個字段:
    Last-Modified、ETag、Expires、Cache-Control。

    • Cache-Control
      例如Cache-Control:max-age=2592000, 表示緩存時長為2592000秒, 也就是一個月30天的時間。如果30天內(nèi)需要再次請求這個文件,那么瀏覽器不會發(fā)出請求,直接使用本地的緩存的文件。這是HTTP/1.1標(biāo)準(zhǔn)中的字段。

    • Expires
      例如Expires:Tue,25 Sep 2018 07:17:34 GMT, 這表示這個文件的過期時間是格林尼治時間2018年9月25日7點17分。因為我是北京時間2018年8月26日15點請求的, 所以可以看出也是差不多一個月有效期。在這個時間之前瀏覽器都不會再次發(fā)出請求去獲取這個文件。Expires是HTTP/1.0中的字段,如果客戶端和服務(wù)器時間不同步會導(dǎo)致緩存出現(xiàn)問題,因此才有了上面的Cache-Control。當(dāng)它們同時出現(xiàn)時,Cache-Control優(yōu)先級更高。

    • Last-Modified
      標(biāo)識文件在服務(wù)器上的最新更新時間, 下次請求時,如果文件緩存過期,瀏覽器通過If-Modified-Since字段帶上這個時間,發(fā)送給服務(wù)器,由服務(wù)器比較時間戳來判斷文件是否有修改。如果沒有修改,服務(wù)器返回304(未修改)告訴瀏覽器繼續(xù)使用緩存;如果有修改,則返回200,同時返回最新的文件。

    • Etag
      Etag的取值是一個對文件進(jìn)行標(biāo)識的特征字串, 在向服務(wù)器查詢文件是否有更新時,瀏覽器通過If-None-Match字段把特征字串發(fā)送給服務(wù)器,由服務(wù)器和文件最新特征字串進(jìn)行匹配,來判斷文件是否有更新:沒有更新回包304,有更新回包200。Etag和Last-Modified可根據(jù)需求使用一個或兩個同時使用。兩個同時使用時,只要滿足基中一個條件,就認(rèn)為文件沒有更新。

    常見用法是Cache-Control與Last-Modified一起使用, Expires與 Etag一起使用。

    但是實際情況可能并不是這樣。

    現(xiàn)在過了5分鐘, 我們再次打開頁面, 觀察請求。

    在上面這個請求中, 我們在request中沒有看到If-None-Match字段, 說明Etag這個字段沒有用到。但是在request中有If-Modified-Since這個字段, 表示緩存文件的上次的修改日期, 是1984年, 表示當(dāng)時從服務(wù)器請求下來的文件最后一次的修改時間是1984年, 而我們在response中看到Last-Modified字段還是那個時間, 說明服務(wù)器上的文件沒有修改過, 所以返回了304(未修改), 而Cache-Control在這里是300秒, 表示5分鐘就會過期, 而Expires在這里雖然也出現(xiàn)了, 但是我們上面說過, 當(dāng)Cache-Control和Expires同時出現(xiàn)時, Cache-Control的優(yōu)先級較高。

    所以說, 大部分情況下, 我們其實看Cache-Control和Last-Modified字段足矣。

    好了, 話說回來, 現(xiàn)在我們知道為什么會有之前提到的現(xiàn)象了, 是因為WebView的緩存。

    那么如何才能使WebView支持這些緩存協(xié)議呢?答案是不配置(使用默認(rèn)的CacheMode), 或者手動設(shè)置

    WebSettings?webSettings?=?webView.getSettings();
    webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
    復(fù)制代碼

    下面是5中緩存模式的解釋:

    • LOAD_CACHE_ONLY: 不使用網(wǎng)絡(luò),只讀取本地緩存數(shù)據(jù)。
    • LOAD_DEFAULT: 根據(jù)cache-control決定是否從網(wǎng)絡(luò)上取數(shù)據(jù)。
    • LOAD_CACHE_NORMAL: API level 17中已經(jīng)廢棄,從API level 11開始作用同LOAD_DEFAULT模式
    • LOAD_NO_CACHE: 不使用緩存,只從網(wǎng)絡(luò)獲取數(shù)據(jù)。
    • LOAD_CACHE_ELSE_NETWORK,只要本地有,無論是否過期,或者no-cache,都使用緩存中的數(shù)據(jù)。本地沒有緩存時才從網(wǎng)絡(luò)上獲取。

    所以我們一般設(shè)置為默認(rèn)的緩存模式就可以了。關(guān)于緩存的配置, 主要還是靠web前端和后臺設(shè)置。

    除了WebView自帶的緩存, 還有Application Cache緩存, Dom Storage緩存, Web SQL Database緩存, IndexedDB緩存。但是剩下的幾種緩存, 根據(jù)官方文檔, AppCache已經(jīng)不推薦使用了, 標(biāo)準(zhǔn)也不會再支持。而其他的幾種也不是文件緩存, 和我們今天討論的主題不符, 所以我也不再介紹了。有興趣可以看H5 緩存機(jī)制淺析 移動端 Web 加載性能優(yōu)化和Android:手把手教你構(gòu)建 全面的WebView 緩存機(jī)制 & 資源加載方案

    其他提升WebView速度的方案

    WebView的初始化

    本地Webview初始化都要不少時間, 首次初始化webview與第二次初始化不同,首次會比第二次慢很多。原因預(yù)計是webview首次初始化后,即使 webview 已經(jīng)釋放,但一些webview 共用的全局服務(wù)或資源對象仍沒有釋放,第二次初始化時不需要再生成這些對象從而變快。我們可以在Application預(yù)先初始化好WebView, 當(dāng)?shù)诙纬跏蓟疻ebView的時候速度就快多了, 或者直接將其拿來使用。

    預(yù)加載數(shù)據(jù)

    預(yù)加載數(shù)據(jù)就是在客戶端初始化WebView的同時,直接由native開始網(wǎng)絡(luò)請求數(shù)據(jù), 當(dāng)頁面初始化完成后,向native獲取其代理請求的數(shù)據(jù), 數(shù)據(jù)請求和WebView初始化可以并行進(jìn)行,縮短總體的頁面加載時間。簡單來說就是配置一個預(yù)加載列表,在APP啟動或某些時機(jī)時提前去請求,這個預(yù)加載列表需要包含所需H5模塊的頁面和資源, 客戶端可以接管所有請求的緩存,不走webview默認(rèn)緩存邏輯, 自行實現(xiàn)緩存機(jī)制, 原理其實就是攔截WebViewClient的那兩個shouldInterceptRequest方法。

    離線包

    離線包的意思就是將H5的頁面和資源進(jìn)行打包后下發(fā)到客戶端,并由客戶端直接解壓到本地儲存中。優(yōu)點是由于其本地化,首屏加載速度快,用戶體驗更為接近原生, 可以不依賴網(wǎng)絡(luò),離線運行, 缺點就是開發(fā)流程/更新機(jī)制復(fù)雜化, 需要客戶端、甚至服務(wù)端的共同協(xié)作。這里我以Hybrid App技術(shù)解析 -- 實戰(zhàn)篇中提到的思路為例子供大家參考。

    資源:

    • H5: 每個代碼包都有一個唯一且遞增的版本號;
    • Native: 提供包下載且解壓資源文件到對應(yīng)目錄
    • 服務(wù)端: 提供一個接口,可以獲取線上最新代碼包的版本號和下載地址。

    流程:

    • 前端更新代碼打包后按版本號上傳至指定的服務(wù)器上;
    • 每次打開頁面時,H5請求接口獲取線上最新代碼包版本號,并與本地包進(jìn)行版本號比對,當(dāng)線上的版本號大于本地包版本號時,調(diào)用原生下載離線包
    • 客戶端直接去線上地址下載最新的代碼包,并解壓替換到當(dāng)前目錄文件。

    關(guān)于離線包的機(jī)制需要注意的問題還很多, 本文肯定無法照顧完全, 大家可以參考移動H5首屏秒開優(yōu)化方案探討、美團(tuán)大眾點評 Hybrid 化建設(shè)、《移動端本地 H5 秒開方案探索與實現(xiàn)》這幾篇文章看看。

    一些開源方案

    CacheWebView
    這個庫的介紹鏈接在這里my.oschina.net/yale8848/bl…, 據(jù)作者說主要是為了解決Android自身緩存空間太小(12M)的問題, 代碼我簡單看了一下, 主要也是攔截這兩個方法:

    ????@TargetApi(Build.VERSION_CODES.LOLLIPOP)
    ????@Override
    ????public?WebResourceResponse?interceptRequest(?WebResourceRequest?request)?{
    ????????if?(mInterceptor==null){
    ????????????return?null;
    ????????}
    ????????return?mInterceptor.interceptRequest(request);
    ????}

    ????@Override
    ????public?WebResourceResponse?interceptRequest(String?url)?{
    ????????if?(mInterceptor==null){
    ????????????return?null;
    ????????}
    ????????return?mInterceptor.interceptRequest(url);
    ????}
    復(fù)制代碼

    然后使用Okhttp去下載資源, 同時給OkHttpClient配置了緩存攔截器, 因為OkHttp能夠很好的支持緩存, 這樣就突破了WebView緩存空間太小和緩存不可控的問題。

    VasSonic
    騰訊出品的一個輕量級的高性能的Hybrid框架,專注于提升頁面首屏加載速度,完美支持靜態(tài)直出頁面和動態(tài)直出頁面,兼容離線包等方案。優(yōu)點是性能好, 速度快, 大廠出品, 缺點是配置復(fù)雜, 同時需要前后端接入。VasSonic的代碼我沒有看, 感興趣的可以看他們的VasSonic/wiki和騰訊祭出大招VasSonic,讓你的H5頁面首屏秒開!

    總結(jié)

    怎樣提高WebView的加載速度其實涉及到的方面很多, 需要注意的細(xì)節(jié)也很多, 沒有辦法一概而論。大家需要按照公司的業(yè)務(wù)需要量體裁衣, 按需配置。

    本文Demo
    github.com/mundane7996…

    參考:

    Android:手把手教你構(gòu)建 全面的WebView 緩存機(jī)制 & 資源加載方案
    WebView緩存原理分析和應(yīng)用
    H5 和移動端 WebView 緩存機(jī)制解析與實戰(zhàn)
    騰訊祭出大招VasSonic,讓你的H5頁面首屏秒開!
    《移動端本地 H5 秒開方案探索與實現(xiàn)》
    移動 H5 首屏秒開優(yōu)化方案探討
    美團(tuán)大眾點評 Hybrid 化建設(shè)
    H5 緩存機(jī)制淺析 移動端 Web 加載性能優(yōu)化
    QQ會員基于 Hybrid 的高質(zhì)量 H5 架構(gòu)實踐
    從WebView緩存聊到Http 的緩存機(jī)制 | 掘金技術(shù)征文
    美團(tuán): WebView性能、體驗分析與優(yōu)化

    歡迎關(guān)注我的微信公眾號「玉剛說」,接收第一手技術(shù)干貨

    總結(jié)

    以上是生活随笔為你收集整理的Android Webview H5 秒开方案实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。