在控制臺(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拱撵。
在控制臺(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;
}