h5中performance.timing輕松獲取網(wǎng)頁(yè)各個(gè)數(shù)據(jù) 如dom加載時(shí)間 渲染時(shí)長(zhǎng) 加載完觸發(fā)時(shí)間

在控制臺(tái)中輸入window.performance.timing(html5的屬性);

各字段的含義:

·         navigationStart:當(dāng)前瀏覽器窗口的前一個(gè)網(wǎng)頁(yè)關(guān)閉栖袋,發(fā)生unload事件時(shí)的Unix毫秒時(shí)間戳石蔗。如果沒有前一個(gè)網(wǎng)頁(yè),則等于fetchStart屬性。

·   unloadEventStart:如果前一個(gè)網(wǎng)頁(yè)與當(dāng)前網(wǎng)頁(yè)屬于同一個(gè)域名入挣,則返回前一個(gè)網(wǎng)頁(yè)的unload事件發(fā)生時(shí)的Unix毫秒時(shí)間戳移宅。如果沒有前一個(gè)網(wǎng)頁(yè),或者之前的網(wǎng)頁(yè)跳轉(zhuǎn)不是在同一個(gè)域名內(nèi)泡徙,則返回值為0橱鹏。

·   unloadEventEnd:如果前一個(gè)網(wǎng)頁(yè)與當(dāng)前網(wǎng)頁(yè)屬于同一個(gè)域名,則返回前一個(gè)網(wǎng)頁(yè)unload事件的回調(diào)函數(shù)結(jié)束時(shí)的Unix毫秒時(shí)間戳堪藐。如果沒有前一個(gè)網(wǎng)頁(yè)莉兰,或者之前的網(wǎng)頁(yè)跳轉(zhuǎn)不是在同一個(gè)域名內(nèi),則返回值為0庶橱。

·   redirectStart:返回第一個(gè)HTTP跳轉(zhuǎn)開始時(shí)的Unix毫秒時(shí)間戳贮勃。如果沒有跳轉(zhuǎn),或者不是同一個(gè)域名內(nèi)部的跳轉(zhuǎn)苏章,則返回值為0寂嘉。

·   redirectEnd:返回最后一個(gè)HTTP跳轉(zhuǎn)結(jié)束時(shí)(即跳轉(zhuǎn)回應(yīng)的最后一個(gè)字節(jié)接受完成時(shí))的Unix毫秒時(shí)間戳。如果沒有跳轉(zhuǎn)枫绅,或者不是同一個(gè)域名內(nèi)部的跳轉(zhuǎn)泉孩,則返回值為0。

·   fetchStart:返回瀏覽器準(zhǔn)備使用HTTP請(qǐng)求讀取文檔時(shí)的Unix毫秒時(shí)間戳并淋。該事件在網(wǎng)頁(yè)查詢本地緩存之前發(fā)生寓搬。

·   domainLookupStart:返回域名查詢開始時(shí)的Unix毫秒時(shí)間戳。如果使用持久連接县耽,或者信息是從本地緩存獲取的句喷,則返回值等同于fetchStart屬性的值。

·   domainLookupEnd:返回域名查詢結(jié)束時(shí)的Unix毫秒時(shí)間戳兔毙。如果使用持久連接唾琼,或者信息是從本地緩存獲取的,則返回值等同于fetchStart屬性的值澎剥。

·   connectStart:返回HTTP請(qǐng)求開始向服務(wù)器發(fā)送時(shí)的Unix毫秒時(shí)間戳锡溯。如果使用持久連接(persistent connection),則返回值等同于fetchStart屬性的值。

·   connectEnd:返回瀏覽器與服務(wù)器之間的連接建立時(shí)的Unix毫秒時(shí)間戳祭饭。如果建立的是持久連接芜茵,則返回值等同于fetchStart屬性的值。連接建立指的是所有握手和認(rèn)證過(guò)程全部結(jié)束倡蝙。

·   secureConnectionStart:返回瀏覽器與服務(wù)器開始安全鏈接的握手時(shí)的Unix毫秒時(shí)間戳九串。如果當(dāng)前網(wǎng)頁(yè)不要求安全連接,則返回0悠咱。

·   requestStart:返回瀏覽器向服務(wù)器發(fā)出HTTP請(qǐng)求時(shí)(或開始讀取本地緩存時(shí))的Unix毫秒時(shí)間戳蒸辆。

·   responseStart:返回瀏覽器從服務(wù)器收到(或從本地緩存讀取)第一個(gè)字節(jié)時(shí)的Unix毫秒時(shí)間戳析既。

·   responseEnd:返回瀏覽器從服務(wù)器收到(或從本地緩存讀裙薄)最后一個(gè)字節(jié)時(shí)(如果在此之前HTTP連接已經(jīng)關(guān)閉,則返回關(guān)閉時(shí))的Unix毫秒時(shí)間戳眼坏。

·   domLoading:返回當(dāng)前網(wǎng)頁(yè)DOM結(jié)構(gòu)開始解析時(shí)(即Document.readyState屬性變?yōu)椤發(fā)oading”拂玻、相應(yīng)的readystatechange事件觸發(fā)時(shí))的Unix毫秒時(shí)間戳。

·   domInteractive:返回當(dāng)前網(wǎng)頁(yè)DOM結(jié)構(gòu)結(jié)束解析宰译、開始加載內(nèi)嵌資源時(shí)(即Document.readyState屬性變?yōu)椤癷nteractive”檐蚜、相應(yīng)的readystatechange事件觸發(fā)時(shí))的Unix毫秒時(shí)間戳。

·   domContentLoadedEventStart:返回當(dāng)前網(wǎng)頁(yè)DOMContentLoaded事件發(fā)生時(shí)(即DOM結(jié)構(gòu)解析完畢沿侈、所有腳本開始運(yùn)行時(shí))的Unix毫秒時(shí)間戳闯第。

·   domContentLoadedEventEnd:返回當(dāng)前網(wǎng)頁(yè)所有需要執(zhí)行的腳本執(zhí)行完成時(shí)的Unix毫秒時(shí)間戳。

·   domComplete:返回當(dāng)前網(wǎng)頁(yè)DOM結(jié)構(gòu)生成時(shí)(即Document.readyState屬性變?yōu)椤癱omplete”缀拭,以及相應(yīng)的readystatechange事件發(fā)生時(shí))的Unix毫秒時(shí)間戳咳短。

·   loadEventStart:返回當(dāng)前網(wǎng)頁(yè)load事件的回調(diào)函數(shù)開始時(shí)的Unix毫秒時(shí)間戳。如果該事件還沒有發(fā)生蛛淋,返回0咙好。

·   loadEventEnd:返回當(dāng)前網(wǎng)頁(yè)load事件的回調(diào)函數(shù)運(yùn)行結(jié)束時(shí)的Unix毫秒時(shí)間戳。如果該事件還沒有發(fā)生褐荷,返回0勾效。通過(guò)while循環(huán)持續(xù)判斷直到loadEventEnd>0則表示完全加載完畢了!網(wǎng)絡(luò)不再有任何數(shù)據(jù)請(qǐng)求叛甫、dom也渲染完畢了2愎!其监!

Android webview交互性能監(jiān)測(cè)指標(biāo)獲取方法(白屏?xí)r間萌腿,domc,整頁(yè)時(shí)間棠赛,首屏?xí)r間)

業(yè)界衡量移動(dòng)web app交互性能的優(yōu)劣主要是通過(guò)監(jiān)測(cè)webview渲染頁(yè)面時(shí)白屏?xí)r間哮奇,DOM樹構(gòu)建時(shí)間整頁(yè)時(shí)間首屏?xí)r間這三個(gè)指標(biāo)來(lái)完成的睛约,那么這四個(gè)指標(biāo)分別的意義是什么呢鼎俘?我們從w3c提供的navigation Timing中看到交互性能指的是Processing和onLoad這兩部分的時(shí)間。
[圖片上傳失敗...(image-a70c42-1576075390535)]
在瀏覽器交互階段(Processing和onLoad時(shí)間段)瀏覽器接收服務(wù)器返回的基礎(chǔ)頁(yè)數(shù)據(jù)后辩涝,瀏覽器需要對(duì)HTML這個(gè)單純的文本內(nèi)容進(jìn)行解析贸伐,從文本中構(gòu)建出一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu),叫做DOM樹(DOM tree)怔揩,用于組織將要繪制在屏幕上的內(nèi)容捉邢。從HTML也能得到外聯(lián)或內(nèi)聯(lián)的CSS腳本和JavaScript腳本,當(dāng)然還有媒體文件商膊,比如圖片伏伐、視頻、聲音晕拆,這些都需要再次發(fā)起網(wǎng)絡(luò)請(qǐng)求下載藐翎。CSS文本內(nèi)容中的規(guī)則同樣會(huì)被構(gòu)建成一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu),叫做CSS樹(CSS tree)实幕,來(lái)決定DOM樹的節(jié)點(diǎn)在屏幕上的布局吝镣、顏色、狀態(tài)效果昆庇。JavaScript腳本被觸發(fā)執(zhí)行后末贾,除了計(jì)算業(yè)務(wù),往往還需要操作DOM樹整吆,就是所謂的DOM API拱撵。

image.png

在控制臺(tái)中輸入window.performance.timing(html5的屬性);

各字段的含義:

· navigationStart:當(dāng)前瀏覽器窗口的前一個(gè)網(wǎng)頁(yè)關(guān)閉,發(fā)生unload事件時(shí)的Unix毫秒時(shí)間戳掂为。如果沒有前一個(gè)網(wǎng)頁(yè)裕膀,則等于fetchStart屬性。

· unloadEventStart:如果前一個(gè)網(wǎng)頁(yè)與當(dāng)前網(wǎng)頁(yè)屬于同一個(gè)域名勇哗,則返回前一個(gè)網(wǎng)頁(yè)的unload事件發(fā)生時(shí)的Unix毫秒時(shí)間戳昼扛。如果沒有前一個(gè)網(wǎng)頁(yè),或者之前的網(wǎng)頁(yè)跳轉(zhuǎn)不是在同一個(gè)域名內(nèi)欲诺,則返回值為0抄谐。

· unloadEventEnd:如果前一個(gè)網(wǎng)頁(yè)與當(dāng)前網(wǎng)頁(yè)屬于同一個(gè)域名,則返回前一個(gè)網(wǎng)頁(yè)unload事件的回調(diào)函數(shù)結(jié)束時(shí)的Unix毫秒時(shí)間戳扰法。如果沒有前一個(gè)網(wǎng)頁(yè)蛹含,或者之前的網(wǎng)頁(yè)跳轉(zhuǎn)不是在同一個(gè)域名內(nèi),則返回值為0塞颁。

· redirectStart:返回第一個(gè)HTTP跳轉(zhuǎn)開始時(shí)的Unix毫秒時(shí)間戳浦箱。如果沒有跳轉(zhuǎn)吸耿,或者不是同一個(gè)域名內(nèi)部的跳轉(zhuǎn),則返回值為0酷窥。

· redirectEnd:返回最后一個(gè)HTTP跳轉(zhuǎn)結(jié)束時(shí)(即跳轉(zhuǎn)回應(yīng)的最后一個(gè)字節(jié)接受完成時(shí))的Unix毫秒時(shí)間戳咽安。如果沒有跳轉(zhuǎn),或者不是同一個(gè)域名內(nèi)部的跳轉(zhuǎn)蓬推,則返回值為0妆棒。

· fetchStart:返回瀏覽器準(zhǔn)備使用HTTP請(qǐng)求讀取文檔時(shí)的Unix毫秒時(shí)間戳。該事件在網(wǎng)頁(yè)查詢本地緩存之前發(fā)生沸伏。

· domainLookupStart:返回域名查詢開始時(shí)的Unix毫秒時(shí)間戳糕珊。如果使用持久連接,或者信息是從本地緩存獲取的毅糟,則返回值等同于fetchStart屬性的值红选。

· domainLookupEnd:返回域名查詢結(jié)束時(shí)的Unix毫秒時(shí)間戳。如果使用持久連接姆另,或者信息是從本地緩存獲取的纠脾,則返回值等同于fetchStart屬性的值。

· connectStart:返回HTTP請(qǐng)求開始向服務(wù)器發(fā)送時(shí)的Unix毫秒時(shí)間戳蜕青。如果使用持久連接(persistent connection)苟蹈,則返回值等同于fetchStart屬性的值。

· connectEnd:返回瀏覽器與服務(wù)器之間的連接建立時(shí)的Unix毫秒時(shí)間戳右核。如果建立的是持久連接慧脱,則返回值等同于fetchStart屬性的值。連接建立指的是所有握手和認(rèn)證過(guò)程全部結(jié)束贺喝。

· secureConnectionStart:返回瀏覽器與服務(wù)器開始安全鏈接的握手時(shí)的Unix毫秒時(shí)間戳菱鸥。如果當(dāng)前網(wǎng)頁(yè)不要求安全連接,則返回0躏鱼。

· requestStart:返回瀏覽器向服務(wù)器發(fā)出HTTP請(qǐng)求時(shí)(或開始讀取本地緩存時(shí))的Unix毫秒時(shí)間戳氮采。

· responseStart:返回瀏覽器從服務(wù)器收到(或從本地緩存讀取)第一個(gè)字節(jié)時(shí)的Unix毫秒時(shí)間戳染苛。

· responseEnd:返回瀏覽器從服務(wù)器收到(或從本地緩存讀热的)最后一個(gè)字節(jié)時(shí)(如果在此之前HTTP連接已經(jīng)關(guān)閉,則返回關(guān)閉時(shí))的Unix毫秒時(shí)間戳茶行。

· domLoading:返回當(dāng)前網(wǎng)頁(yè)DOM結(jié)構(gòu)開始解析時(shí)(即Document.readyState屬性變?yōu)椤發(fā)oading”躯概、相應(yīng)的readystatechange事件觸發(fā)時(shí))的Unix毫秒時(shí)間戳。

· domInteractive:返回當(dāng)前網(wǎng)頁(yè)DOM結(jié)構(gòu)結(jié)束解析畔师、開始加載內(nèi)嵌資源時(shí)(即Document.readyState屬性變?yōu)椤癷nteractive”娶靡、相應(yīng)的readystatechange事件觸發(fā)時(shí))的Unix毫秒時(shí)間戳。

· domContentLoadedEventStart:返回當(dāng)前網(wǎng)頁(yè)DOMContentLoaded事件發(fā)生時(shí)(即DOM結(jié)構(gòu)解析完畢看锉、所有腳本開始運(yùn)行時(shí))的Unix毫秒時(shí)間戳姿锭。

· domContentLoadedEventEnd:返回當(dāng)前網(wǎng)頁(yè)所有需要執(zhí)行的腳本執(zhí)行完成時(shí)的Unix毫秒時(shí)間戳塔鳍。

· domComplete:返回當(dāng)前網(wǎng)頁(yè)DOM結(jié)構(gòu)生成時(shí)(即Document.readyState屬性變?yōu)椤癱omplete”,以及相應(yīng)的readystatechange事件發(fā)生時(shí))的Unix毫秒時(shí)間戳呻此。

· loadEventStart:返回當(dāng)前網(wǎng)頁(yè)load事件的回調(diào)函數(shù)開始時(shí)的Unix毫秒時(shí)間戳献幔。如果該事件還沒有發(fā)生,返回0趾诗。

· loadEventEnd:返回當(dāng)前網(wǎng)頁(yè)load事件的回調(diào)函數(shù)運(yùn)行結(jié)束時(shí)的Unix毫秒時(shí)間戳。如果該事件還沒有發(fā)生蹬蚁,返回0恃泪。通過(guò)while循環(huán)持續(xù)判斷直到loadEventEnd>0則表示完全加載完畢了!網(wǎng)絡(luò)不再有任何數(shù)據(jù)請(qǐng)求犀斋、dom也渲染完畢了1春酢!叽粹!

Android webview交互性能監(jiān)測(cè)指標(biāo)獲取方法(白屏?xí)r間览效,domc,整頁(yè)時(shí)間虫几,首屏?xí)r間)

業(yè)界衡量移動(dòng)web app交互性能的優(yōu)劣主要是通過(guò)監(jiān)測(cè)webview渲染頁(yè)面時(shí)白屏?xí)r間锤灿,DOM樹構(gòu)建時(shí)間整頁(yè)時(shí)間首屏?xí)r間這三個(gè)指標(biāo)來(lái)完成的辆脸,那么這四個(gè)指標(biāo)分別的意義是什么呢但校?我們從w3c提供的navigation Timing中看到交互性能指的是Processing和onLoad這兩部分的時(shí)間。

在瀏覽器交互階段(Processing和onLoad時(shí)間段)瀏覽器接收服務(wù)器返回的基礎(chǔ)頁(yè)數(shù)據(jù)后啡氢,瀏覽器需要對(duì)HTML這個(gè)單純的文本內(nèi)容進(jìn)行解析状囱,從文本中構(gòu)建出一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu),叫做DOM樹(DOM tree)倘是,用于組織將要繪制在屏幕上的內(nèi)容亭枷。從HTML也能得到外聯(lián)或內(nèi)聯(lián)的CSS腳本和JavaScript腳本,當(dāng)然還有媒體文件搀崭,比如圖片叨粘、視頻、聲音瘤睹,這些都需要再次發(fā)起網(wǎng)絡(luò)請(qǐng)求下載宣鄙。CSS文本內(nèi)容中的規(guī)則同樣會(huì)被構(gòu)建成一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu),叫做CSS樹(CSS tree)默蚌,來(lái)決定DOM樹的節(jié)點(diǎn)在屏幕上的布局冻晤、顏色、狀態(tài)效果绸吸。JavaScript腳本被觸發(fā)執(zhí)行后鼻弧,除了計(jì)算業(yè)務(wù)设江,往往還需要操作DOM樹,就是所謂的DOM API攘轩。
[圖片上傳失敗...(image-786357-1576075390535)]
1叉存、白屏?xí)r間
指瀏覽器開始顯示內(nèi)容的時(shí)間。但是在傳統(tǒng)的采集方式里度帮,是在HTML的head標(biāo)簽結(jié)尾里記錄時(shí)間戳歼捏,來(lái)計(jì)算白屏?xí)r間。在這個(gè)時(shí)刻笨篷,瀏覽器開始解析body標(biāo)簽內(nèi)的內(nèi)容瞳秽。而現(xiàn)代瀏覽器不會(huì)等待CSS樹(所有CSS文件下載和解析完成)和DOM樹(整個(gè)body標(biāo)簽解析完成)構(gòu)建完成才開始繪制,而是馬上開始顯示中間結(jié)果率翅。所以經(jīng)常在低網(wǎng)速的環(huán)境中练俐,觀察到頁(yè)面由上至下緩慢顯示完,或者先顯示文本內(nèi)容后再重繪成帶有格式的頁(yè)面內(nèi)容冕臭。
在android中我們通過(guò)使用webview.WebChromeClient的onReceivedTitle事件來(lái)近似獲得白屏?xí)r間腺晾。
2、DOM樹構(gòu)建時(shí)間
指瀏覽器開始對(duì)基礎(chǔ)頁(yè)文本內(nèi)容進(jìn)行解析到從文本中構(gòu)建出一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu)(DOM樹)的時(shí)間辜贵,這個(gè)事件是從HTML中的onLoad的延伸而來(lái)的悯蝉,當(dāng)一個(gè)頁(yè)面完成加載時(shí),初始化腳本的方法是使用load事件托慨,但這個(gè)類函數(shù)的缺點(diǎn)是僅在所有資源都完全加載后才被觸發(fā)泉粉,這有時(shí)會(huì)導(dǎo)致比較嚴(yán)重的延遲,開發(fā)人員隨后創(chuàng)建了domready事件榴芳,它在DOM加載之后及資源加載之前被觸發(fā)嗡靡。domready被眾多JavaScript庫(kù)所采用,它在本地瀏覽器中以DOMContentLoaded事件的形式被使用。
在android中我們通過(guò)注入js代碼到webview中的方式來(lái)實(shí)現(xiàn)窟感;具體實(shí)現(xiàn)上讨彼,在WebChromeClient的onReceivedTitle事件被觸發(fā)時(shí)注入我們的js代碼,然后通過(guò)WebChromeClient的onJsPrompt事件來(lái)獲取domc(window.DOMContentLoaded事件)時(shí)間柿祈,代碼:

@Override
public void onReceivedTitle (WebView view, String title) {
    view.loadUrl("javascript:" + 
 "window.addEventListener('DOMContentLoaded', function() {" +
        "prompt('domc:' + new Date().getTime());" + 
    );
}
 
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult r) {
 Log.i(UAQ_WEB_ACTIVITY, "**** Blocking Javascript Prompt :" + message);
 if(message != null){
 if(!preCacheRun){
 String[] strs = message.split(":");
 if(2 == strs.length){ 
 if("domc".equals(strs[0])){
 result.getCurrentRun().setDocComplete(Long.valueOf(strs[1].trim()));
 }
 }
 } 
 }
 r.confirm(defaultValue);
 return true;
}

3哈误、首屏?xí)r間
首屏?xí)r間(FirstScreen Time),是從網(wǎng)頁(yè)應(yīng)用的角度定義的指標(biāo)躏嚎,在Navigation Timing或者瀏覽器實(shí)現(xiàn)中并沒有相關(guān)指標(biāo)值蜜自。首屏?xí)r間,是指用戶看到第一屏卢佣,即整個(gè)網(wǎng)頁(yè)頂部大小為當(dāng)前窗口的區(qū)域重荠,顯示完整的時(shí)間。常用的方法有虚茶,頁(yè)面標(biāo)簽標(biāo)記法戈鲁、圖像相似度比較法和首屏高度內(nèi)圖片加載法仇参;
1)頁(yè)面標(biāo)簽標(biāo)記法,在HTML文檔中對(duì)應(yīng)首屏內(nèi)容的標(biāo)簽結(jié)束位置婆殿,使用內(nèi)聯(lián)的JavaScript代碼記錄當(dāng)前時(shí)間戳诈乒,比較局限;2)圖像相似度比較法婆芦,通過(guò)比較連續(xù)截屏圖像的像素點(diǎn)變化趨勢(shì)確定首屏?xí)r間怕磨,最為科學(xué)和直觀的方式,但是比較消耗本地設(shè)備的運(yùn)行資源消约;3)首屏高度內(nèi)圖片加載法肠鲫,通過(guò)尋找首屏區(qū)域內(nèi)的所有圖片,計(jì)算它們加載完的時(shí)間去得到首屏?xí)r間荆陆,這樣比較符合網(wǎng)頁(yè)的實(shí)際體驗(yàn)并且比較節(jié)省設(shè)備運(yùn)行資源;
具體實(shí)現(xiàn)上我采用的是最后一種集侯,即“首屏高度內(nèi)圖片加載法”被啼;因?yàn)橥ǔP枰紤]首屏?xí)r間的頁(yè)面,都是因?yàn)樵谑灼廖恢脙?nèi)放入了較多的圖片資源√耐鳎現(xiàn)代瀏覽器處理圖片資源時(shí)是異步的浓体,會(huì)先將圖片長(zhǎng)寬應(yīng)用于頁(yè)面排版,然后隨著收到圖片數(shù)據(jù)由上至下繪制顯示的辈讶。并且瀏覽器對(duì)每個(gè)頁(yè)面的TCP連接數(shù)限制命浴,使得并不是所有圖片都能立刻開始下載和顯示。因此我們?cè)贒OM樹構(gòu)建完成后即可遍歷獲得所有在設(shè)備屏幕高度內(nèi)的所有圖片資源標(biāo)簽贱除,在所有圖片標(biāo)簽中添加document.onload事件生闲,在整頁(yè)加載完成(window.onLoad事件發(fā)生)時(shí)遍歷圖片標(biāo)簽并獲得之前注冊(cè)的document.onload事件時(shí)間的最大值,該最大值減去navigationStart即認(rèn)為近似的首屏?xí)r間月幌。
在android中我們通過(guò)注入js代碼到webview中的方式來(lái)實(shí)現(xiàn)碍讯;具體實(shí)現(xiàn)上,在WebChromeClient的onReceivedTitle事件被觸發(fā)時(shí)注入我們的js代碼扯躺,然后通過(guò)WebChromeClient的onJsPrompt事件來(lái)獲取firstscreen時(shí)間捉兴;
js部分計(jì)算首屏?xí)r間的邏輯代碼:

function first_screen () {
 var imgs = document.getElementsByTagName("img"), fs = +new Date;
 var fsItems = [], that = this;
    function getOffsetTop(elem) {
        var top = 0;
        top = window.pageYOffset ? window.pageYOffset : document.documentElement.scrollTop;
        try{
            top += elem.getBoundingClientRect().top;    
        }catch(e){
 
        }finally{
            return top;
        }
 
    }
    var loadEvent = function() {
        //gif避免
        if (this.removeEventListener) {
            this.removeEventListener("load", loadEvent, false);
        }
        fsItems.push({
            img : this,
            time : +new Date
        });
    }
    for (var i = 0; i < imgs.length; i++) {
        (function() {
            var img = imgs[i];
 
            if (img.addEventListener) {
 
                !img.complete && img.addEventListener("load", loadEvent, false);
            } else if (img.attachEvent) {
 
                img.attachEvent("onreadystatechange", function() {
                    if (img.readyState == "complete") {
                        loadEvent.call(img, loadEvent);
                    }
 
                });
            }
 
        })();
    }
    function firstscreen_time() {
        var sh = document.documentElement.clientHeight;
        for (var i = 0; i < fsItems.length; i++) {
            var item = fsItems[i], img = item['img'], time = item['time'], top = getOffsetTop(img);
            if (top > 0 && top < sh) {
                fs = time > fs ? time : fs;
            }
        }
        return fs;
    }      
    window.addEventListener('load', function() {
     prompt('firstscreen:' + firstscreen_time());
     });
}

webview的注入代碼:

private void registerOnLoadHandler(WebView view) {
    String jscontent = getJavaScriptAsString();
    view.loadUrl("javascript:" + jscontent + 
      "window.addEventListener('DOMContentLoaded', function() {" +
          "first_screen();
       });"
    );
}
 
@Override
public void onReceivedTitle (WebView view, String title) {
 registerOnLoadHandler(view);
}
 
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult r) {
 Log.i(UAQ_WEB_ACTIVITY, "**** Blocking Javascript Prompt :" + message);
 if(message != null){
 if(!preCacheRun){
 String[] strs = message.split(":");
 if(2 == strs.length){ 
 if("firstscreen".equals(strs[0])){
 result.getCurrentRun().setFirstScreen(Long.valueOf(strs[1].trim()));
 }
 }
 } 
 }
 r.confirm(defaultValue);
 return true;
}

4、整頁(yè)時(shí)間
整頁(yè)時(shí)間(Page Load Time)录语,是指頁(yè)面完成整個(gè)加載過(guò)程的時(shí)刻倍啥。從Navigation Timing API上采集,就是loadEventEnd減去navigationStart澎埠。在傳統(tǒng)采集方法中虽缕,會(huì)使用window對(duì)象的onload事件來(lái)記錄時(shí)間戳彼宠,它表示瀏覽器認(rèn)定該頁(yè)面已經(jīng)載入完全了拙已。
在android中我們通過(guò)注入js代碼到webview中的方式來(lái)實(shí)現(xiàn)摧冀;具體實(shí)現(xiàn)上建车,在WebChromeClient的onReceivedTitle事件被觸發(fā)時(shí)注入我們的js代碼椒惨,然后通過(guò)WebChromeClient的onJsPrompt事件來(lái)獲取load(window.onLoad事件)時(shí)間领斥,代碼:

@Override
public void onReceivedTitle (WebView view, String title) {
    view.loadUrl("javascript:" + 
 "window.addEventListener('load', function() {" +
        "prompt('load:' + new Date().getTime());" + 
    );
}
 
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult r) {
 Log.i(UAQ_WEB_ACTIVITY, "**** Blocking Javascript Prompt :" + message);
 if(message != null){
 if(!preCacheRun){
 String[] strs = message.split(":");
 if(2 == strs.length){ 
 if("load".equals(strs[0])){
 result.getCurrentRun().setFullyLoaded(Long.valueOf(strs[1].trim()));
 }
 }
 } 
 }
 r.confirm(defaultValue);
 return true;
}

1月洛、白屏?xí)r間
指瀏覽器開始顯示內(nèi)容的時(shí)間嚼黔。但是在傳統(tǒng)的采集方式里唬涧,是在HTML的head標(biāo)簽結(jié)尾里記錄時(shí)間戳爵卒,來(lái)計(jì)算白屏?xí)r間。在這個(gè)時(shí)刻陌僵,瀏覽器開始解析body標(biāo)簽內(nèi)的內(nèi)容碗短。而現(xiàn)代瀏覽器不會(huì)等待CSS樹(所有CSS文件下載和解析完成)和DOM樹(整個(gè)body標(biāo)簽解析完成)構(gòu)建完成才開始繪制总滩,而是馬上開始顯示中間結(jié)果闰渔。所以經(jīng)常在低網(wǎng)速的環(huán)境中冈涧,觀察到頁(yè)面由上至下緩慢顯示完营曼,或者先顯示文本內(nèi)容后再重繪成帶有格式的頁(yè)面內(nèi)容蒂阱。
在android中我們通過(guò)使用webview.WebChromeClient的onReceivedTitle事件來(lái)近似獲得白屏?xí)r間录煤。
2辐赞、DOM樹構(gòu)建時(shí)間
指瀏覽器開始對(duì)基礎(chǔ)頁(yè)文本內(nèi)容進(jìn)行解析到從文本中構(gòu)建出一個(gè)內(nèi)部數(shù)據(jù)結(jié)構(gòu)(DOM樹)的時(shí)間,這個(gè)事件是從HTML中的onLoad的延伸而來(lái)的窖梁,當(dāng)一個(gè)頁(yè)面完成加載時(shí)纵刘,初始化腳本的方法是使用load事件荸哟,但這個(gè)類函數(shù)的缺點(diǎn)是僅在所有資源都完全加載后才被觸發(fā)鞍历,這有時(shí)會(huì)導(dǎo)致比較嚴(yán)重的延遲惧蛹,開發(fā)人員隨后創(chuàng)建了domready事件香嗓,它在DOM加載之后及資源加載之前被觸發(fā)沧烈。domready被眾多JavaScript庫(kù)所采用,它在本地瀏覽器中以DOMContentLoaded事件的形式被使用掺出。
在android中我們通過(guò)注入js代碼到webview中的方式來(lái)實(shí)現(xiàn)汤锨;具體實(shí)現(xiàn)上,在WebChromeClient的onReceivedTitle事件被觸發(fā)時(shí)注入我們的js代碼铐维,然后通過(guò)WebChromeClient的onJsPrompt事件來(lái)獲取domc(window.DOMContentLoaded事件)時(shí)間锨并,代碼:

@Override
public void onReceivedTitle (WebView view, String title) {
    view.loadUrl("javascript:" + 
 "window.addEventListener('DOMContentLoaded', function() {" +
        "prompt('domc:' + new Date().getTime());" + 
    );
}
 
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult r) {
 Log.i(UAQ_WEB_ACTIVITY, "**** Blocking Javascript Prompt :" + message);
 if(message != null){
 if(!preCacheRun){
 String[] strs = message.split(":");
 if(2 == strs.length){ 
 if("domc".equals(strs[0])){
 result.getCurrentRun().setDocComplete(Long.valueOf(strs[1].trim()));
 }
 }
 } 
 }
 r.confirm(defaultValue);
 return true;
}

3、首屏?xí)r間
首屏?xí)r間(FirstScreen Time)包警,是從網(wǎng)頁(yè)應(yīng)用的角度定義的指標(biāo)害晦,在Navigation Timing或者瀏覽器實(shí)現(xiàn)中并沒有相關(guān)指標(biāo)值壹瘟。首屏?xí)r間,是指用戶看到第一屏严衬,即整個(gè)網(wǎng)頁(yè)頂部大小為當(dāng)前窗口的區(qū)域粱挡,顯示完整的時(shí)間榕堰。常用的方法有逆屡,頁(yè)面標(biāo)簽標(biāo)記法、圖像相似度比較法和首屏高度內(nèi)圖片加載法莺治;

1)頁(yè)面標(biāo)簽標(biāo)記法,在HTML文檔中對(duì)應(yīng)首屏內(nèi)容的標(biāo)簽結(jié)束位置榄审,使用內(nèi)聯(lián)的JavaScript代碼記錄當(dāng)前時(shí)間戳搁进,比較局限篮撑;2)圖像相似度比較法未蝌,通過(guò)比較連續(xù)截屏圖像的像素點(diǎn)變化趨勢(shì)確定首屏?xí)r間萧吠,最為科學(xué)和直觀的方式,但是比較消耗本地設(shè)備的運(yùn)行資源桐筏;3)首屏高度內(nèi)圖片加載法纸型,通過(guò)尋找首屏區(qū)域內(nèi)的所有圖片,計(jì)算它們加載完的時(shí)間去得到首屏?xí)r間,這樣比較符合網(wǎng)頁(yè)的實(shí)際體驗(yàn)并且比較節(jié)省設(shè)備運(yùn)行資源狰腌;
具體實(shí)現(xiàn)上我采用的是最后一種除破,即“首屏高度內(nèi)圖片加載法”;因?yàn)橥ǔP枰紤]首屏?xí)r間的頁(yè)面琼腔,都是因?yàn)樵谑灼廖恢脙?nèi)放入了較多的圖片資源」宸悖現(xiàn)代瀏覽器處理圖片資源時(shí)是異步的,會(huì)先將圖片長(zhǎng)寬應(yīng)用于頁(yè)面排版光坝,然后隨著收到圖片數(shù)據(jù)由上至下繪制顯示的土铺。并且瀏覽器對(duì)每個(gè)頁(yè)面的TCP連接數(shù)限制后德,使得并不是所有圖片都能立刻開始下載和顯示绵患。因此我們?cè)贒OM樹構(gòu)建完成后即可遍歷獲得所有在設(shè)備屏幕高度內(nèi)的所有圖片資源標(biāo)簽,在所有圖片標(biāo)簽中添加document.onload事件邪媳,在整頁(yè)加載完成(window.onLoad事件發(fā)生)時(shí)遍歷圖片標(biāo)簽并獲得之前注冊(cè)的document.onload事件時(shí)間的最大值逗柴,該最大值減去navigationStart即認(rèn)為近似的首屏?xí)r間耕拷。
在android中我們通過(guò)注入js代碼到webview中的方式來(lái)實(shí)現(xiàn)赃绊;具體實(shí)現(xiàn)上忠售,在WebChromeClient的onReceivedTitle事件被觸發(fā)時(shí)注入我們的js代碼,然后通過(guò)WebChromeClient的onJsPrompt事件來(lái)獲取firstscreen時(shí)間摇予;
js部分計(jì)算首屏?xí)r間的邏輯代碼:

function first_screen () {
 var imgs = document.getElementsByTagName("img"), fs = +new Date;
 var fsItems = [], that = this;
    function getOffsetTop(elem) {
        var top = 0;
        top = window.pageYOffset ? window.pageYOffset : document.documentElement.scrollTop;
        try{
            top += elem.getBoundingClientRect().top;    
        }catch(e){
 
        }finally{
            return top;
        }
 
    }
    var loadEvent = function() {
        //gif避免
        if (this.removeEventListener) {
            this.removeEventListener("load", loadEvent, false);
        }
        fsItems.push({
            img : this,
            time : +new Date
        });
    }
    for (var i = 0; i < imgs.length; i++) {
        (function() {
            var img = imgs[i];
 
            if (img.addEventListener) {
 
                !img.complete && img.addEventListener("load", loadEvent, false);
            } else if (img.attachEvent) {
 
                img.attachEvent("onreadystatechange", function() {
                    if (img.readyState == "complete") {
                        loadEvent.call(img, loadEvent);
                    }
 
                });
            }
 
        })();
    }
    function firstscreen_time() {
        var sh = document.documentElement.clientHeight;
        for (var i = 0; i < fsItems.length; i++) {
            var item = fsItems[i], img = item['img'], time = item['time'], top = getOffsetTop(img);
            if (top > 0 && top < sh) {
                fs = time > fs ? time : fs;
            }
        }
        return fs;
    }      
    window.addEventListener('load', function() {
     prompt('firstscreen:' + firstscreen_time());
     });
}

webview的注入代碼:

private void registerOnLoadHandler(WebView view) {
    String jscontent = getJavaScriptAsString();
    view.loadUrl("javascript:" + jscontent + 
      "window.addEventListener('DOMContentLoaded', function() {" +
          "first_screen();
       });"
    );
}
 
@Override
public void onReceivedTitle (WebView view, String title) {
 registerOnLoadHandler(view);
}
 
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult r) {
 Log.i(UAQ_WEB_ACTIVITY, "**** Blocking Javascript Prompt :" + message);
 if(message != null){
 if(!preCacheRun){
 String[] strs = message.split(":");
 if(2 == strs.length){ 
 if("firstscreen".equals(strs[0])){
 result.getCurrentRun().setFirstScreen(Long.valueOf(strs[1].trim()));
 }
 }
 } 
 }
 r.confirm(defaultValue);
 return true;
}

4翔烁、整頁(yè)時(shí)間
整頁(yè)時(shí)間(Page Load Time)贩耐,是指頁(yè)面完成整個(gè)加載過(guò)程的時(shí)刻寻狂。從Navigation Timing API上采集筋夏,就是loadEventEnd減去navigationStart。在傳統(tǒng)采集方法中蚕冬,會(huì)使用window對(duì)象的onload事件來(lái)記錄時(shí)間戳,它表示瀏覽器認(rèn)定該頁(yè)面已經(jīng)載入完全了赢乓。
在android中我們通過(guò)注入js代碼到webview中的方式來(lái)實(shí)現(xiàn)牌芋;具體實(shí)現(xiàn)上驯击,在WebChromeClient的onReceivedTitle事件被觸發(fā)時(shí)注入我們的js代碼择吊,然后通過(guò)WebChromeClient的onJsPrompt事件來(lái)獲取load(window.onLoad事件)時(shí)間粒蜈,代碼:

@Override
public void onReceivedTitle (WebView view, String title) {
    view.loadUrl("javascript:" + 
 "window.addEventListener('load', function() {" +
        "prompt('load:' + new Date().getTime());" + 
    );
}
 
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult r) {
 Log.i(UAQ_WEB_ACTIVITY, "**** Blocking Javascript Prompt :" + message);
 if(message != null){
 if(!preCacheRun){
 String[] strs = message.split(":");
 if(2 == strs.length){ 
 if("load".equals(strs[0])){
 result.getCurrentRun().setFullyLoaded(Long.valueOf(strs[1].trim()));
 }
 }
 } 
 }
 r.confirm(defaultValue);
 return true;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市旗国,隨后出現(xiàn)的幾起案子枯怖,更是在濱河造成了極大的恐慌,老刑警劉巖能曾,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件度硝,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡寿冕,警方通過(guò)查閱死者的電腦和手機(jī)蕊程,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)驼唱,“玉大人藻茂,你說(shuō)我怎么就攤上這事∶悼遥” “怎么了辨赐?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)京办。 經(jīng)常有香客問(wèn)我肖油,道長(zhǎng),這世上最難降的妖魔是什么臂港? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任森枪,我火速辦了婚禮,結(jié)果婚禮上审孽,老公的妹妹穿的比我還像新娘县袱。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著媒鼓,像睡著了一般鱼喉。 火紅的嫁衣襯著肌膚如雪裸删。 梳的紋絲不亂的頭發(fā)上噩翠,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天锈津,我揣著相機(jī)與錄音暮顺,去河邊找鬼乖篷。 笑死响驴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撕蔼。 我是一名探鬼主播豁鲤,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鲸沮!你這毒婦竟也來(lái)了琳骡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤讼溺,失蹤者是張志新(化名)和其女友劉穎楣号,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怒坯,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡炫狱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了敬肚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片毕荐。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖艳馒,靈堂內(nèi)的尸體忽然破棺而出憎亚,到底是詐尸還是另有隱情,我是刑警寧澤弄慰,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布第美,位于F島的核電站,受9級(jí)特大地震影響陆爽,放射性物質(zhì)發(fā)生泄漏什往。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一慌闭、第九天 我趴在偏房一處隱蔽的房頂上張望别威。 院中可真熱鬧,春花似錦驴剔、人聲如沸省古。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)豺妓。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間琳拭,已是汗流浹背训堆。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留白嘁,地道東北人坑鱼。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像权薯,于是被迫代替她去往敵國(guó)和親姑躲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子睡扬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,383評(píng)論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,100評(píng)論 1 32
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程盟蚣,因...
    小菜c閱讀 6,404評(píng)論 0 17
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,099評(píng)論 25 707
  • 昨晚諾諾給我打電話讓我去找她屎开,我匆匆掛斷電話,便火急火燎的趕到她的公寓马靠。 當(dāng)我推開房間的門時(shí)奄抽,滿屋酒氣撲面而來(lái),她...
    許一涵閱讀 1,082評(píng)論 24 24