欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android?WebView預(yù)渲染介紹

 更新時(shí)間:2022年09月05日 09:08:45   作者:孝之請(qǐng)回答???????  
這篇文章主要介紹了Android?WebView預(yù)渲染介紹,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

前言

在一個(gè)Hybrid項(xiàng)目中,必不可少的就是加載h5頁(yè)面。h5頁(yè)面的加載性能極大影響著用戶體驗(yàn),并會(huì)從各方面影響到我們APP的業(yè)務(wù)數(shù)據(jù)。試想,假設(shè)一個(gè)h5頁(yè)面要花好幾秒才能打開(kāi),那用戶還會(huì)使用我們的APP嗎?所以今天我們講一講,客戶端上優(yōu)化h5頁(yè)面加載速度的一種方式:預(yù)渲染。

照例,拋出本篇文章要解決的幾個(gè)問(wèn)題:

  • 客戶端可以從哪些方面優(yōu)化h5頁(yè)面的加載速度?
  • 預(yù)渲染的基本實(shí)現(xiàn)邏輯是怎樣的?
  • 預(yù)渲染存在哪些局限性?

術(shù)語(yǔ)對(duì)齊

術(shù)語(yǔ)描述
WebView用于承載h5頁(yè)面的native組件。
預(yù)創(chuàng)建在用戶打開(kāi)一個(gè)h5頁(yè)面之前,在內(nèi)存中先創(chuàng)建好一個(gè)WebView實(shí)例。當(dāng)用戶打開(kāi)h5頁(yè)面時(shí),可以直接取到預(yù)先創(chuàng)建好的WebView實(shí)例,用于承載h5頁(yè)面。
預(yù)渲染在用戶打開(kāi)一個(gè)h5頁(yè)面之前,在內(nèi)存中不僅預(yù)創(chuàng)建好了WebView實(shí)例,還進(jìn)一步根據(jù)url提前渲染好了WebView。當(dāng)用戶打開(kāi)指定的url頁(yè)面時(shí),可以直接拿預(yù)先渲染好了的WebView進(jìn)行展示。

客戶端可以從哪些方面優(yōu)化h5頁(yè)面的加載速度?

我們可以看一下,在Android上完整打開(kāi)一個(gè)WebView需要經(jīng)歷怎樣的一個(gè)鏈路(來(lái)自優(yōu)秀的前輩們):

image.png

所以,要想優(yōu)化WebView的加載速度,就要想辦法去縮短鏈路中這些節(jié)點(diǎn)所耗費(fèi)的時(shí)間。

從客戶端的角度上來(lái)講,Page初始化就是H5容器的初始化。一般而言,容器初始化時(shí)間是比較快的(如果沒(méi)有太多初始化邏輯的話),優(yōu)化空間有限。WebView的初始化相對(duì)就比較復(fù)雜,涉及到了瀏覽器內(nèi)核在主線程的初始化。APP冷啟動(dòng)后,首次創(chuàng)建WebView時(shí)需要去初始化瀏覽器內(nèi)核。

這里要分3種情況去看:

  • 全新安裝APP,冷啟動(dòng)后首次打開(kāi)一個(gè)WebView,耗時(shí)最長(zhǎng),可能會(huì)需要1000ms左右,取決于瀏覽器內(nèi)核。
  • 非全新安裝APP,冷啟動(dòng)后首次打開(kāi)一個(gè)WebView,耗時(shí)分布在500ms左右。
  • 冷啟動(dòng)后,非首次打開(kāi)一個(gè)WebView,耗時(shí)就非常短了,分布在15ms左右。

這里我們可以看到,第1種和第2種情況是存在比較大的提升空間的。

當(dāng)我們創(chuàng)建好WebView,執(zhí)行WebView#loadUrl()時(shí),WebView就會(huì)經(jīng)歷上圖中的白屏-loading-可交互狀態(tài)。這幾個(gè)階段,可以說(shuō)是整個(gè)鏈路中耗時(shí)占比最大的一部分。但客戶端在這里能做的優(yōu)化是很有限的,而且通常需要跟前端、服務(wù)端去配合優(yōu)化,比如可以并行請(qǐng)求數(shù)據(jù),這里就先不發(fā)散了。但如果換個(gè)角度思考,客戶端先不去干涉后面這幾個(gè)階段的邏輯,而是提前去執(zhí)行后面這幾個(gè)階段的邏輯,那么不也就相當(dāng)于提高加載速度了么?這其實(shí)就是我們要講的預(yù)加載。

優(yōu)化思路

所以我們的優(yōu)化思路主要針對(duì)WebView初始化階段,以及WebView加載階段。

通過(guò)預(yù)創(chuàng)建WebView,去解決首次創(chuàng)建WebView耗時(shí)長(zhǎng)的問(wèn)題。

通過(guò)預(yù)渲染W(wǎng)ebView,去提前經(jīng)歷用戶需要等待的白屏-loading階段,當(dāng)用戶打開(kāi)相應(yīng)頁(yè)面時(shí),能夠直接上屏展示,給用戶的感覺(jué)就是秒開(kāi)。

預(yù)渲染的基本實(shí)現(xiàn)邏輯是怎樣的?

預(yù)創(chuàng)建

預(yù)創(chuàng)建是預(yù)渲染的前提(沒(méi)有預(yù)創(chuàng)建好怎么預(yù)渲染呢..),所以我們先講下預(yù)創(chuàng)建。預(yù)創(chuàng)建WebView,一個(gè)基本原則就是,當(dāng)內(nèi)存中沒(méi)有預(yù)創(chuàng)建的WebView可以復(fù)用(即預(yù)創(chuàng)建沒(méi)有命中)時(shí),就走原來(lái)創(chuàng)建WebView的邏輯。

預(yù)創(chuàng)建個(gè)數(shù)

這里我們選擇只預(yù)創(chuàng)建1個(gè)WebView。之所以選擇1個(gè),是因?yàn)槲覀冾A(yù)創(chuàng)建WebView的根本目的,是為了解決APP首次安裝/冷啟動(dòng)時(shí),第一個(gè)WebView加載慢的問(wèn)題。后續(xù)的WebView實(shí)例的創(chuàng)建都是很快的。所以,即使后面沒(méi)有命中預(yù)創(chuàng)建的WebView,用的重新創(chuàng)建的WebView,也就是多花了15ms左右的時(shí)間,影響是很小的。所以綜合下來(lái),預(yù)創(chuàng)建1個(gè)WebView的性價(jià)比是最高的,多了反而浪費(fèi)內(nèi)存。

預(yù)創(chuàng)建時(shí)機(jī)

這里的時(shí)機(jī)要分為三個(gè),第一個(gè)時(shí)機(jī)是在冷啟動(dòng)后,我們需要進(jìn)行預(yù)創(chuàng)建。可以選擇把這個(gè)時(shí)機(jī)放到進(jìn)入首頁(yè)后,用IdleHandler進(jìn)行主線程閑時(shí)創(chuàng)建。當(dāng)然也可以選擇前置。前置的話有可能會(huì)影響到APP的啟動(dòng),所以如果不是特別有必要的話,建議還是后置一些。

第二個(gè)時(shí)機(jī)是在預(yù)創(chuàng)建的WebView被拿去復(fù)用后,此時(shí)也是需要預(yù)創(chuàng)建的。因?yàn)橐坏┍荒萌?fù)用,意味著我們緩存中已經(jīng)沒(méi)有可用的WebView了,若一個(gè)pha頁(yè)面又打開(kāi)了另外一個(gè)pha頁(yè)面,我們?cè)谶@個(gè)case最好也能提供預(yù)創(chuàng)建的WebView。

以上兩個(gè)時(shí)機(jī)都是自動(dòng)觸發(fā)。后來(lái)發(fā)現(xiàn)一個(gè)場(chǎng)景,當(dāng)用戶在某個(gè)路徑比較深的頁(yè)面時(shí),若需要預(yù)加載下一個(gè)頁(yè)面,那么這個(gè)頁(yè)面往往是不需要一冷啟動(dòng)就預(yù)渲染的。這時(shí)候就需要一個(gè)接口讓業(yè)務(wù)方能在用戶打開(kāi)頁(yè)面之前將該頁(yè)面進(jìn)行預(yù)加載。

void preload(Context context, String url);

預(yù)創(chuàng)建復(fù)用

復(fù)用WebView需要注意一點(diǎn),每個(gè)WebView都是跟指定的Context綁定的,但預(yù)創(chuàng)建時(shí),還獲取不到WebView未來(lái)要綁定的Context。因此預(yù)創(chuàng)建時(shí)可以用MutableContextWrapper包ApplicationContext去創(chuàng)建。MutableContextWrapper支持我們將其中的BaseContext進(jìn)行替換,復(fù)用預(yù)創(chuàng)建的WebView時(shí),將ApplicationContext替換為需要綁定的Context即可。同時(shí)根據(jù)“預(yù)創(chuàng)建時(shí)機(jī)”中說(shuō)的,在復(fù)用時(shí),往棧頂插入一個(gè)新的預(yù)創(chuàng)建的WebView。相應(yīng)的,當(dāng)頁(yè)面關(guān)閉時(shí),我們也需要將綁定的Context解綁,防止內(nèi)存泄漏。

大致的邏輯如下圖所示:

image.png

這里也貼出部分偽代碼:

/**
 * 閑時(shí)預(yù)創(chuàng)建
 */
private void preCreateWebView() {
    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
        @Override
        public boolean queueIdle() {
            // 預(yù)創(chuàng)建webView
            WebView webView = new WebView(new MutableContextWrapper(APPLICATION_CONTEXT));
            cache.add(webView);
            return false;
        }
    });
}

/**
 * 獲取預(yù)創(chuàng)建的WebView
 * @param context 要與使用的webview綁定的context
 * @return
 */
public PHAWebView acquirePreCreateWebView(Context context) {
    WebView webview;
    // 緩存中無(wú)可用WebView時(shí),直接新建
    if (cache.isEmpty()) {
        webview = createWebView();
    } else {
        webview = cache.peekWebView();
    }
    // 更改context
    if (webview != null && webview.getContext() instanceof MutableContextWrapper) {
        MutableContextWrapper webViewMutableContext = (MutableContextWrapper) webview.getContext();
        webViewMutableContext.setBaseContext(context);
    }
    return webview;
}

預(yù)渲染

預(yù)渲染,其實(shí)就是在預(yù)創(chuàng)建的基礎(chǔ)上,執(zhí)行WebView#loadUrl(),將頁(yè)面提前渲染完成。但這里面還有一些細(xì)節(jié)點(diǎn)需要關(guān)注。

預(yù)渲染時(shí)機(jī)

預(yù)渲染的時(shí)機(jī)也是分為兩個(gè),一個(gè)是冷啟動(dòng)后的主線程閑時(shí)階段進(jìn)行預(yù)渲染,這一點(diǎn)跟預(yù)加載的時(shí)機(jī)保持一致。還有一個(gè)我們可以選擇在頁(yè)面關(guān)閉時(shí)進(jìn)行預(yù)渲染。打比方說(shuō),假設(shè)我預(yù)渲染了頁(yè)面A,那么用戶在訪問(wèn)完頁(yè)面A后,我需要再次預(yù)渲染頁(yè)面A,從而保證頁(yè)面A的實(shí)效性。

預(yù)渲染白名單

首先,預(yù)渲染是有對(duì)象的,預(yù)渲染的對(duì)象就是頁(yè)面url。而預(yù)創(chuàng)建是沒(méi)有特定對(duì)象的,只需要隨時(shí)準(zhǔn)備一個(gè)可用的WebView就行了,誰(shuí)都可以用。但預(yù)渲染不行,不告訴我預(yù)渲染誰(shuí),我還怎么預(yù)渲染。

所以,從初始化開(kāi)始,我們就已經(jīng)決定好了該預(yù)渲染哪些頁(yè)面,也就是預(yù)渲染白名單。白名單可通過(guò)服務(wù)器配置的方式進(jìn)行下發(fā)。但也不能一股腦把頁(yè)面全都配上,因?yàn)閮?nèi)存是有限的,配太多可能會(huì)引起低端機(jī)型的OOM。

預(yù)渲染有效性校驗(yàn)

所謂的有效性校驗(yàn),就是在復(fù)用預(yù)渲染W(wǎng)ebView時(shí),校驗(yàn)這個(gè)WebView是否被正常預(yù)渲染了。如果失效,就走預(yù)創(chuàng)建/重新創(chuàng)建的邏輯。這里分兩個(gè)角度來(lái)校驗(yàn)WebView的有效性:時(shí)間有效性與狀態(tài)有效性。

時(shí)間有效性

存在這樣一種場(chǎng)景:當(dāng)我們已經(jīng)預(yù)渲染了A頁(yè)面,且用戶一直沒(méi)有訪問(wèn)。某個(gè)時(shí)刻這個(gè)頁(yè)面做了更新,即重新發(fā)布了,那么如果用戶這時(shí)候去訪問(wèn)A頁(yè)面,看到的還是舊的A頁(yè)面。所以這里需要在預(yù)渲染頁(yè)面時(shí),給頁(yè)面設(shè)置一個(gè)過(guò)期時(shí)間,若復(fù)用預(yù)渲染W(wǎng)ebView時(shí)已經(jīng)過(guò)期了,就說(shuō)明WebView已經(jīng)失效了,需要重新loadUrl保證頁(yè)面的實(shí)效性。

狀態(tài)有效性

狀態(tài)有效性就是去校驗(yàn)預(yù)渲染的WebView最終是否有渲染成功,這一點(diǎn)我們可以通過(guò)WebViewClient的生命周期回調(diào)(onPageFinished/onReceivedError)來(lái)進(jìn)行判斷。之所以要做這個(gè)校驗(yàn),是為了防止一開(kāi)始預(yù)渲染就失敗了,卻還是拿這個(gè)WebView去進(jìn)行展示。比如,假設(shè)我們?cè)诰W(wǎng)絡(luò)異常狀態(tài)下去進(jìn)行了預(yù)渲染,在網(wǎng)絡(luò)恢復(fù)正常后用戶訪問(wèn)預(yù)渲染頁(yè)面,若不進(jìn)行狀態(tài)校驗(yàn),那么看到的就會(huì)是網(wǎng)絡(luò)異常狀態(tài)下的WebView了。

頁(yè)面顯示狀態(tài)通知

頁(yè)面顯示狀態(tài),通俗來(lái)講就是頁(yè)面現(xiàn)在是離屏的,還是上屏的。在h5的一些業(yè)務(wù)場(chǎng)景中,有一部分是需要感知到頁(yè)面的顯示狀態(tài)的。比如引導(dǎo)類的動(dòng)畫(huà),比如會(huì)場(chǎng)的一些倒計(jì)時(shí)等等。所以我們需要將頁(yè)面的顯示狀態(tài)同步到h5那邊。

實(shí)現(xiàn)上,就是要在預(yù)渲染W(wǎng)ebView時(shí)給h5注入一個(gè)全局的環(huán)境變量,window.page_on_screen=false。當(dāng)復(fù)用WebView,即上屏?xí)r,再將window.page_on_screen設(shè)置為true,同時(shí)發(fā)一個(gè)通知給h5。這樣h5就可以根據(jù)同步到的顯示狀態(tài)來(lái)控制自己的業(yè)務(wù)邏輯。

其它注意事項(xiàng)

預(yù)渲染、預(yù)創(chuàng)建,本質(zhì)上是用空間換時(shí)間的優(yōu)化,所以是比較耗費(fèi)內(nèi)存的。所以我們需要在內(nèi)存不足的時(shí)候,及時(shí)將內(nèi)存中待使用的WebView給回收掉,避免APP發(fā)生OOM。

另外,因?yàn)轭A(yù)渲染離屏加載了頁(yè)面,所以頁(yè)面的初始化行為是需要納入評(píng)估的,只有評(píng)估通過(guò)后,才能放入預(yù)渲染白名單中。具體的初始化行為包括但不限于:業(yè)務(wù)的曝光埋點(diǎn)、前端邏輯(如倒計(jì)時(shí)、跨天活動(dòng))、消費(fèi)型(如首次引導(dǎo))、后端流量評(píng)估、頁(yè)面在后臺(tái)是否會(huì)有聲音、是否會(huì)彈框(系統(tǒng)框、權(quán)限框、對(duì)話框...)等等。

預(yù)渲染存在哪些局限性?

  • 低端機(jī)內(nèi)存空間有限,預(yù)渲染白名單的實(shí)際配置數(shù)量需要視情況進(jìn)行調(diào)整。
  • 預(yù)渲染頁(yè)面必須經(jīng)過(guò)白名單配置。頁(yè)面url、參數(shù)發(fā)生改變,配置也需要改變。這一點(diǎn)其實(shí)也是有優(yōu)化空間的,即離屏預(yù)渲染時(shí)加載url前綴域名,上屏?xí)r再根據(jù)完整的url參數(shù)做邏輯調(diào)整。實(shí)現(xiàn)上會(huì)比較麻煩,可以視ROI情況進(jìn)行投入。
  • 預(yù)渲染頁(yè)面的實(shí)效性無(wú)法保證。預(yù)渲染頁(yè)面一旦重新部署,端上是不能立刻感知到并重新加載的。按上面的預(yù)渲染時(shí)機(jī),目前只有以下二個(gè)場(chǎng)景會(huì)觸發(fā)端上對(duì)預(yù)渲染頁(yè)面的更新:
    • 1.冷啟動(dòng);
    • 2.頁(yè)面被訪問(wèn)后關(guān)閉;
    • 3.業(yè)務(wù)調(diào)用接口主動(dòng)注入。

所以大家如果有比較好的方案歡迎分享給我呀!

  • 命中率。預(yù)渲染頁(yè)面是不能百分百命中的,即即使我們把某個(gè)頁(yè)面配置進(jìn)了預(yù)渲染白名單,app也有可能沒(méi)預(yù)渲染上這個(gè)頁(yè)面。有很多異常場(chǎng)景會(huì)影響到命中率,比如:
    • 1.上面講到的預(yù)渲染的時(shí)間有效性與狀態(tài)有效性;
    • 2.服務(wù)端下發(fā)的預(yù)渲染白名單沒(méi)有及時(shí)拉取到;
    • 3.主線程一直繁忙,導(dǎo)致預(yù)渲染邏輯一直沒(méi)執(zhí)行;
    • 4.內(nèi)存不夠,將緩存的預(yù)渲染W(wǎng)ebView回收掉了。

總結(jié)

所以雖然預(yù)渲染能從表面上實(shí)現(xiàn)h5頁(yè)面的秒開(kāi),但也不是萬(wàn)能的,是存在一些缺陷的(否則也不需要?jiǎng)e的優(yōu)化手段了)。但我認(rèn)為是諸多優(yōu)化手段中比較簡(jiǎn)單卻又能立竿見(jiàn)影的一個(gè)手段,特別是對(duì)本身h5頁(yè)面加載就非常慢的app而言。所以如果還沒(méi)做起來(lái)的同學(xué)可以試一試,后面再結(jié)合其它優(yōu)化的手段抹除不足。今天就講到這里啦。

到此這篇關(guān)于Android WebView預(yù)渲染介紹的文章就介紹到這了,更多相關(guān)Android WebView預(yù)渲染內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論