成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

WKWebView使用指南|功能豐富的JXBWKWebView

Cobub / 1518人閱讀

摘要:使用這種方案攔截的網(wǎng)絡(luò)請(qǐng)求造成的問(wèn)題就是請(qǐng)求數(shù)據(jù)被清空,還是所為,看源碼主要看代碼中間那兩句注釋?zhuān)笾碌囊馑季褪遣粫?huì)在進(jìn)程間通信發(fā)送的。如何解決終極思路就是雖然的會(huì)在進(jìn)程間通信時(shí)被丟棄,但不會(huì)。

github地址:JXBWKWebView,如果覺(jué)得項(xiàng)目不錯(cuò)可以點(diǎn)個(gè)star支持一下,謝謝~

前言

目前iOS系統(tǒng)已經(jīng)更新到iOS11,大多數(shù)項(xiàng)目向下兼容最多兼容到iOS8,因此,在項(xiàng)目中對(duì)WebView組件進(jìn)行重構(gòu)再封裝時(shí),打算直接舍棄UIWebView轉(zhuǎn)用WKWebView。

如果你目前正在網(wǎng)上瀏覽關(guān)于WKWebView的一些文章,相信你已經(jīng)清楚了WKWebView的優(yōu)點(diǎn),也目睹了大家在使用WKWebView的過(guò)程中遇到的坑,而這篇文章,會(huì)對(duì)到目前為止大家遇到的關(guān)于WKWebView的問(wèn)題給出詳細(xì)的解決方案,文章的最后,也會(huì)講述關(guān)于對(duì)WKWebView進(jìn)行性能優(yōu)化的方案。

解決的問(wèn)題

goback返回頁(yè)面不刷新

Cookie

POST請(qǐng)求失效

crash

navigationBackItem

進(jìn)度條

NativeJS的交互

優(yōu)化H5頁(yè)面啟動(dòng)速度

入坑
goback Api返回不刷新

在之前使用UIWebView時(shí),調(diào)用goback后,頁(yè)面會(huì)刷新。使用WKWebView后,調(diào)用goback,即便調(diào)用reload方法,H5依然不會(huì)刷新。

原因是調(diào)用goback時(shí),UIWebView會(huì)觸發(fā)onload事件,WKWebView不會(huì)觸發(fā)onload事件,如果前端依舊在onload事件中處理iOS的頁(yè)面返回事件,是處理不了的,解決方案是讓前端使用onpageshow事件監(jiān)聽(tīng)WKWebView的頁(yè)面goback事件。

前端代碼如下:

window.addEventListener("pageshow", function(event){
    if(event.persisted){
        location.reload();
    }
});

為了查看頁(yè)面是直接從服務(wù)器上載入還是從緩存中讀取,可以使用 PageTransitionEvent對(duì)象的persisted屬性來(lái)判斷。

如果頁(yè)面從瀏覽器的緩存中讀取該屬性返回ture,否則返回 false。然后在根據(jù)truefalse在執(zhí)行相應(yīng)的頁(yè)面刷新動(dòng)作或者直接ajax請(qǐng)求接口更新數(shù)據(jù)。

關(guān)于onloadonpageshow事件在safarichrome上的區(qū)別如下:

. 事件 Chrome Safari
第一次加載頁(yè)面 onload 觸發(fā) 觸發(fā)
第一次加載頁(yè)面 onpageshow 觸發(fā) 觸發(fā)
從其他頁(yè)面返回 onload 觸發(fā) 不觸發(fā)
從其他頁(yè)面返回 onpageshow 觸發(fā) 觸發(fā)
關(guān)于cookie

WKWebView屬于webkit框架,其將瀏覽器內(nèi)核渲染進(jìn)程提取出 App主進(jìn)程,由另外一個(gè)進(jìn)程進(jìn)行管理,減少了相當(dāng)一部分的性能損失,這也是性能上比UIWebView優(yōu)越的原因之一。

既然WKWebView的工作進(jìn)程獨(dú)立于App Process之外,我們暫且稱(chēng)為WK Process(隨便起的)。

在使用AFN進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí),如果server使用set-cookiecookie寫(xiě)入header,AFN接受到響應(yīng)后會(huì)將cookie保存到NSHTTPCookieStorage,下次如果是同域的request url,AFN會(huì)將cookieNSHTTPCookieStorage 中取出然后作為request headercookie發(fā)送給server端,而這一切發(fā)生在App Process。

那么在WK Process工作的WKWebView在發(fā)送網(wǎng)絡(luò)請(qǐng)求及收到響應(yīng)后對(duì)cookie的處理是否也會(huì)使用NSHTTPCookieStorage 呢,經(jīng)過(guò)測(cè)試后,答案是yes,但在存取的過(guò)程中會(huì)有一些問(wèn)題需要注意。

先說(shuō)存:

測(cè)試進(jìn)行:iphone 6p iOS:10
測(cè)試過(guò)程:
1.client使用AFN發(fā)送一個(gè)網(wǎng)絡(luò)請(qǐng)求
2.server接收到請(qǐng)求后,使用set-cookie寫(xiě)入cookie
3.client接收到success response后,使用如下方式輸出log

NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:fields forURL:url];
for (NSHTTPCookie *cookie in cookies) {
    NSLog(@"cookie,name:= %@,valuie = %@",cookie.name,cookie.value);
}

4.進(jìn)入WKWebView所在頁(yè)面,使用loadRequest隨便發(fā)送一個(gè)同域的網(wǎng)絡(luò)請(qǐng)求,在decidePolicyForNavigationResponse代理方法中,使用如下代碼輸出log

NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
for (NSHTTPCookie *cookie in cookies) {
    NSLog(@"wkwebview中的cookie:%@", cookie);
}

也可以使用如下代碼輸出該請(qǐng)求的server response headerset-cookie

NSString *cookieString = [[response allHeaderFields] valueForKey:@"Set-Cookie"];

那么,WKWebViewcookie存入NSHTTPCookieStorage 的時(shí)機(jī)是什么時(shí)候?
1.JS執(zhí)行document.cookie或服務(wù)器set-cookie注入的Cookie會(huì)很快同步到NSHTTPCookieStorage中。
2.H5頁(yè)面進(jìn)行跳轉(zhuǎn)時(shí)會(huì)將Cookie同步到NSHTTPCookieStorage中。
3.控制器頁(yè)面跳轉(zhuǎn)時(shí)會(huì)將Cookie同步到NSHTTPCookieStorage中。

再說(shuō)取:

WKWebView使用loadRequest發(fā)送網(wǎng)絡(luò)時(shí)不會(huì)主動(dòng)將cookie存入到NSHTTPCookieStorage 中,即使是同域的請(qǐng)求。

所以,如果你有一個(gè)請(qǐng)求需要附帶cookie,就不能直接加載URL,需要你根據(jù)URL創(chuàng)建一個(gè)URLMutableRequest對(duì)象,將需要附加的cookie使用addValue:forHTTPHeaderField:方法手動(dòng)將cookie添加到request header中,但這僅能解決首次請(qǐng)求不帶cookie的問(wèn)題,如果頁(yè)面發(fā)送ajax請(qǐng)求,cookie同樣帶不上,解決方案是通過(guò)document.cookie設(shè)置cookie,也就是說(shuō)在你實(shí)例化WKWebView時(shí)就應(yīng)該注入相關(guān)script

上面我們說(shuō)的都是在同域的情況下,如果發(fā)生302請(qǐng)求(可以理解域名發(fā)生變化,也就是說(shuō)不同域),上面的解決方案就用不了了,這時(shí)就需要你在WKWebViewdecidePolicyForNavigationAction代理方法中攔截URL,判斷當(dāng)前URL與初次請(qǐng)求的URL是否同域,如果不同域,在該代理方法中獲取到當(dāng)前請(qǐng)求的request對(duì)象并copy出一個(gè)新的對(duì)象,通過(guò)addValue:forHeaderField:方法將cookie手動(dòng)添加到header中,然后讓WKWebView使用loadRequest重新加載這個(gè)copy出來(lái)的新的request對(duì)象。

問(wèn)題就沒(méi)了嗎?NO,上面的解決方法同樣有局限,即只能解決后續(xù)的同域ajax請(qǐng)求不加cookie的問(wèn)題。如果發(fā)生iframe跨域請(qǐng)求,我們攔截不到請(qǐng)求,所以也沒(méi)法給請(qǐng)求的header手動(dòng)添加cookieWKWebView只適合加載mainFrame 請(qǐng)求。

所以,要和前端同學(xué)提前打好招呼,盡量避免使用iframe,能使用ajax的地方盡量使用ajax,另一方面,iframe現(xiàn)在已經(jīng)不怎么提倡使用了,除非是解決一些特殊的問(wèn)題。

POST請(qǐng)求

使用WKWebView無(wú)法正常發(fā)送POST請(qǐng)求。

所以,這個(gè)時(shí)候我們需要通過(guò)自定義NSURLProtocol攔截WKWebView的網(wǎng)絡(luò)請(qǐng)求,并且,使用NSURLProtocol攔截WKWebView網(wǎng)絡(luò)請(qǐng)求的好處還有就是:
1.如果產(chǎn)品需求要求client需要日志采集,包括所有的網(wǎng)絡(luò)請(qǐng)求記錄,通過(guò)這種方式你是可以獲取到的。
2.如果公司對(duì)用戶體驗(yàn)的要求較高,可以在這里實(shí)現(xiàn)WKWebView初始化和相關(guān)網(wǎng)絡(luò)請(qǐng)求的并發(fā)執(zhí)行,以縮短用戶在client打開(kāi)H5的速度,甚至可以秒開(kāi),達(dá)到和native相同的體驗(yàn)。

但問(wèn)題是正常情況下NSURLProtocol是攔截不到WKWebView的網(wǎng)絡(luò)請(qǐng)求的。

通過(guò)觀看webkit的源碼(github直接搜webkit)可以得到的結(jié)果是,通過(guò)WKWebView發(fā)送一個(gè)網(wǎng)絡(luò)請(qǐng)求其實(shí)也會(huì)走NSURLProtocol,只不過(guò)Applehttphttps這兩個(gè)scheme給過(guò)濾掉了,導(dǎo)致我們攔截不到WKWebView發(fā)送的網(wǎng)路請(qǐng)求。

因此,在我們自定義NSURLProtocol時(shí),要通過(guò)使用私有api來(lái)注冊(cè)一些scheme,注冊(cè)scheme的類(lèi)名叫WKBrowsingContextController ,WKWebView中有一個(gè)屬性叫browsingContextController,就是這個(gè)類(lèi)的對(duì)象。注冊(cè)的方法叫registerSchemeForCustomProtocol:,知道這個(gè)私有api,我們就可以通過(guò)target-action的方式,注冊(cè)WKWebView發(fā)起網(wǎng)絡(luò)請(qǐng)求時(shí)需要攔截的URL scheme,此時(shí)注冊(cè)的scheme至少要包括3種,分別是http、httpspost。

問(wèn)題還沒(méi)玩,解決一個(gè)問(wèn)題的同時(shí)往往伴隨另一個(gè)問(wèn)題的產(chǎn)生。

使用這種方案攔截WKWebView的網(wǎng)絡(luò)請(qǐng)求造成的問(wèn)題就是post請(qǐng)求body數(shù)據(jù)被清空,還是Apple所為,看webkit源碼:

void ArgumentCoder::encodePlatformData(Encoder& encoder, const ResourceRequest& resourceRequest)
{
    RetainPtr requestToSerialize = resourceRequest.cfURLRequest(DoNotUpdateHTTPBody);

    bool requestIsPresent = requestToSerialize;
    encoder << requestIsPresent;

    if (!requestIsPresent)
        return;

    // We don"t send HTTP body over IPC for better performance.
    // Also, it"s not always possible to do, as streams can only be created in process that does networking.
    RetainPtr requestHTTPBody = adoptCF(CFURLRequestCopyHTTPRequestBody(requestToSerialize.get()));
    RetainPtr requestHTTPBodyStream = adoptCF(CFURLRequestCopyHTTPRequestBodyStream(requestToSerialize.get()));
    if (requestHTTPBody || requestHTTPBodyStream) {
        CFMutableURLRequestRef mutableRequest = CFURLRequestCreateMutableCopy(0, requestToSerialize.get());
        requestToSerialize = adoptCF(mutableRequest);
        CFURLRequestSetHTTPRequestBody(mutableRequest, nil);
        CFURLRequestSetHTTPRequestBodyStream(mutableRequest, nil);
    }

    RetainPtr dictionary = adoptCF(WKCFURLRequestCreateSerializableRepresentation(requestToSerialize.get(), IPC::tokenNullTypeRef()));
    IPC::encode(encoder, dictionary.get());

    // The fallback array is part of CFURLRequest, but it is not encoded by WKCFURLRequestCreateSerializableRepresentation.
    encoder << resourceRequest.responseContentDispositionEncodingFallbackArray();
    encoder.encodeEnum(resourceRequest.requester());
}

主要看代碼中間那兩句注釋?zhuān)笾碌囊馑季褪?b>Apple不會(huì)在進(jìn)程間通信發(fā)送httpbody。

因?yàn)?b>WKWebView屬于webkit框架,因此WKWebView的網(wǎng)絡(luò)請(qǐng)求、內(nèi)容加載/渲染都是在WK Process中進(jìn)行,但NSURLProtocol攔截請(qǐng)求還在App Process,一旦注冊(cè)http(s) scheme后,網(wǎng)絡(luò)請(qǐng)求將從獨(dú)立進(jìn)程中發(fā)送到App Process,這樣自定義的NSURLProtocol才能攔截到網(wǎng)絡(luò)請(qǐng)求,為了提升進(jìn)程間通信效率,出于性能上的考慮,Apple會(huì)將requestbody數(shù)據(jù)丟棄,因?yàn)?b>body數(shù)據(jù)(二進(jìn)制類(lèi)型)大小沒(méi)有限制,size偏大的話就會(huì)對(duì)數(shù)據(jù)傳輸效率有嚴(yán)重影響進(jìn)而影響到攔截請(qǐng)求時(shí)的操作及延時(shí)后續(xù)的網(wǎng)絡(luò)請(qǐng)求,因此,Apple在進(jìn)行進(jìn)程間通信時(shí)會(huì)把post請(qǐng)求的body丟棄。

如何解決?
終極思路就是雖然httpbody會(huì)在進(jìn)程間通信時(shí)被丟棄,但header不會(huì)。

因此,解決問(wèn)題步驟如下:

WKWebViewloadRequest前對(duì)request對(duì)象進(jìn)行一些處理,這個(gè)request對(duì)象我們記為old request。

1.記下old requestschemeNSData類(lèi)型的http body。
2.獲取當(dāng)前old requestURL,替換URLschemepost(這也是我們?yōu)槭裁匆谇懊媸褂?b>NSURLProtocol注冊(cè)post scheme的原因),并根據(jù)這個(gè)替換好的URL重新生成一個(gè)新的NSMutableURLRequest對(duì)象,這個(gè)對(duì)象記為new request。
3.給new requestheader賦值,把步驟1中獲取的schemehttp body手動(dòng)添加到這個(gè)new requestheader中,如果這個(gè)post請(qǐng)求需要附帶cookie的話,你也要把cookieold request中拿出來(lái)放到new requestheader中。
4.讓WKWebView加載這個(gè)new request。

WKWebView發(fā)送新的request時(shí)(這個(gè)request urlschemepost),我們可以在自定義NSURLProtocol中攔截到這個(gè)請(qǐng)求,執(zhí)行如下步驟:

1.替換scheme,此時(shí)的schemepost,你需要把post scheme替換成old requestscheme,這個(gè)字段我們之前已經(jīng)保存下來(lái)了。
2.替換scheme后會(huì)生成一個(gè)新的URL,根據(jù)這個(gè)新的URL生成一個(gè)NSURLMutableRequest對(duì)象,將之前保存的http body、cookie放到這個(gè)新的request對(duì)象的header中。
3.使用NSURLSession,根據(jù)新的request對(duì)象發(fā)送網(wǎng)絡(luò)請(qǐng)求,然后通過(guò)NSURLProtocol Client將加載結(jié)果返回給WKWebView。

注意:在這幾個(gè)步驟中一共產(chǎn)生了3個(gè)request對(duì)象。

crash

1.alert彈窗
引起crash的原因是js調(diào)用alert()引起的,也就是說(shuō),當(dāng)WKWebView銷(xiāo)毀的時(shí)候,JS剛好執(zhí)行了alert(),原生的 alert 彈窗可能彈不出來(lái),completionHandler回調(diào)最后沒(méi)有被執(zhí)行,導(dǎo)致crash;另一種情況是在WKWebView剛打開(kāi),JS就執(zhí)行alert(),這個(gè)時(shí)候由于 WKWebView所在的UIViewControllerpushpresent的動(dòng)畫(huà)尚未結(jié)束,alert框可能彈不出來(lái),completionHandler最后沒(méi)有被執(zhí)行,導(dǎo)致crash。

解決方案:獲取當(dāng)前window上最終的UIViewController,判斷UIViewController是否未被銷(xiāo)毀、UIViewController是否已經(jīng)加載完成、動(dòng)畫(huà)是否執(zhí)行完畢。

2.另一個(gè)crash發(fā)生在WKWebView退出前調(diào)用:
執(zhí)行JS代碼的情況下。WKWebView 退出并被釋放后導(dǎo)致completionHandler變成野指針,而此時(shí) javaScript Core 還在執(zhí)行JS代碼,待 javaScript Core 執(zhí)行完畢后會(huì)調(diào)用completionHandler(),導(dǎo)致crash。這個(gè)crash只發(fā)生在iOS 8 系統(tǒng)上,參考Apple Open Source,在iOS9及以后系統(tǒng)蘋(píng)果已經(jīng)修復(fù)了這個(gè)bug,主要是對(duì)completionHandler block做了copy(refer:?https://trac.webkit.org/changeset/179160);對(duì)于iOS 8系統(tǒng),可以通過(guò)在completionHandlerretain WKWebView防止completionHandler被過(guò)早釋放。

解決方案是使用method swizzling hook了這個(gè)系統(tǒng)方法,在回調(diào)中對(duì)self進(jìn)行了強(qiáng)引用來(lái)保證在執(zhí)行completionHandler的時(shí)候self還在。

navigationBackItem

實(shí)現(xiàn)導(dǎo)航欄back item的方式有兩種。

自定義導(dǎo)航欄

這個(gè)比較簡(jiǎn)單,根據(jù)WebView是否可以goback決定navigationBarButtonItems的個(gè)數(shù)和功能。

使用系統(tǒng)默認(rèn)的導(dǎo)航返回按鈕,類(lèi)似于微信

難點(diǎn)在于我們要獲取到點(diǎn)擊系統(tǒng)導(dǎo)航返回按鈕時(shí)的事件,然后進(jìn)行一些處理。

點(diǎn)擊返回按鈕時(shí),實(shí)際上調(diào)用了UINavigationControllernavigationBar:shouldPopItem方法,我們可以使用method swizzling hook住這個(gè)方法,在這個(gè)方法中通過(guò)調(diào)用代理方法的方式告訴WKWebView所在的UIViewController進(jìn)行相應(yīng)的處理。

UIProgressView

這個(gè)簡(jiǎn)單,也不多說(shuō)了。

Native與JS的交互

攔截URL

WKWebViewdecidePolicyForNavigationAction代理方法中可對(duì)URL進(jìn)行攔截,一般使用攔截URL的方式URL的格式如下:

scheme://host?paramKey=paramValue

一般情況下scheme對(duì)應(yīng)業(yè)務(wù),host是業(yè)務(wù)對(duì)應(yīng)的服務(wù)(method),?后面就是參數(shù)。

使用攔截URL的交互方式時(shí),業(yè)務(wù)邏輯不復(fù)雜情況下,JS調(diào)用Native沒(méi)什么問(wèn)題,但當(dāng)業(yè)務(wù)邏輯復(fù)雜時(shí),JS需要拿到Native處理好的回調(diào)數(shù)據(jù)的話,處理起來(lái)將十分麻煩。

并且使用攔截URL的交互方式,不利于今后JSNative的業(yè)務(wù)拓展。

使用Bridge

WKWebView對(duì)JSNative通過(guò)Bridge交互提供了非常好的支持,我們可以通過(guò)ScriptMessageHandler來(lái)達(dá)成各種交互的目的。使用ScriptMessageHandler添加腳本的具體代碼在此不多贅述,大家可自行研究。重點(diǎn)說(shuō)一下Bridge的腳本代碼。

現(xiàn)在關(guān)于Bridge的開(kāi)源解決方案有很多,但基本都遵循一個(gè)模式,在注入的Bridge腳本代碼中,定義好供JS調(diào)用的方法名稱(chēng),該方法通常包括如下幾個(gè)參數(shù):
1.要調(diào)用的native業(yè)務(wù)模塊名稱(chēng)(有些有,有些沒(méi)有,如果項(xiàng)目中實(shí)施模塊化建議加上)。
2.要調(diào)用的native服務(wù)名稱(chēng)(通常是方法名)。
3.傳遞給native的參數(shù)(也就是方法需要的參數(shù))。
4.callback,JS調(diào)用native的方法后腳本需要調(diào)用的回調(diào)。

詳細(xì)來(lái)描述一下使用Bridge整個(gè)交互過(guò)程,從創(chuàng)建Bridge腳本到Bridge腳本執(zhí)行callback
Bridge腳本下稱(chēng)腳本。
1.腳本為JS提供JavaScript語(yǔ)言的方法,該方法用來(lái)調(diào)用native方法,方法的4個(gè)參數(shù)如前所述。
2.在該方法中,會(huì)根據(jù)前述的部分參數(shù)生成一個(gè)唯一標(biāo)識(shí)符,記為identifier。
3.在腳本中給全局對(duì)象(window)綁定一個(gè)字典屬性,key是步驟2中的identifier,valuecallback
4.調(diào)用messagehandlerpostMessage函數(shù),將前述的參數(shù)和identifier 都發(fā)送給native(沒(méi)發(fā)callback,callback的作用主要就是步驟3)。
5.前端調(diào)用你的腳本中的代碼調(diào)用native的方法,具體代碼可參見(jiàn)Apple官方文檔。
5.native在自定義的MessageHandler對(duì)象的userContentController:didReceiveScriptMessage:代理方法中接收到JS傳過(guò)來(lái)的參數(shù)(記為param)。獲取到了模塊名稱(chēng)、服務(wù)名稱(chēng)、參數(shù)、identifier等,額外的,需要?jiǎng)?chuàng)建幾個(gè)block,對(duì)應(yīng)JS那邊的callback,比如JS那邊有個(gè)success callback,那么在native就要有一個(gè)success block,而創(chuàng)建的這些block,我們會(huì)賦值給前面說(shuō)的那個(gè)param里面,那么現(xiàn)在,這個(gè)param有如下幾個(gè)值:

targetName(模塊名稱(chēng))
actionName(服務(wù)名稱(chēng))
identifier(通過(guò)該屬性最后我們可以找到j(luò)s的callback)
success block
failure block
progress block
上面這些參數(shù)基本上已經(jīng)夠了,如果需要擴(kuò)展就自己加吧

那么這些block里面的操作主要是什么呢?block封裝了WKWebViewevaluateJavaScript操作,這個(gè)block最后可以拿到native處理任務(wù)后的結(jié)果和identifier,然后把結(jié)果轉(zhuǎn)換為json數(shù)據(jù),通過(guò)identifier找到JS那邊的callback,然后把結(jié)果的json數(shù)據(jù)作為callback的參數(shù)回傳給JS那邊。代碼如下:

NSString *resultDataString = [self jsonStringWithData:resultDictionary];
    
NSString *callbackString = [NSString stringWithFormat:@"window.Callback("%@", "%@", "%@")", identifier, result, resultDataString];

[message.webView evaluateJavaScript:callbackString completionHandler:nil];

6.利用target-action機(jī)制,根據(jù)targetName實(shí)例化對(duì)象,根據(jù)actionName調(diào)用方法,并把參數(shù)(param)傳遞過(guò)去,目標(biāo)對(duì)象將任務(wù)處理完成后,調(diào)用paramsuccess block, failure block, progress block,將任務(wù)處理的結(jié)果回傳給JS。

交互總結(jié)

無(wú)論是攔截URL還是使用Bridge,最后調(diào)用native方法的機(jī)制都是利用target-action,使用target-action機(jī)制的原因之一就是可減少類(lèi)與類(lèi)之間的耦合程度,減少硬編碼的同時(shí)有利于今后的業(yè)務(wù)擴(kuò)展。

當(dāng)然,如果你不喜歡target-action的方案,也可以自行擴(kuò)展。

攔截WKWebView的網(wǎng)絡(luò)請(qǐng)求

通過(guò)觀看WebKit的源碼可以了解到WKWebView是支持?jǐn)r截網(wǎng)絡(luò)請(qǐng)求的,但是WebKit沒(méi)有注冊(cè)需要攔截的scheme,所以我們只能進(jìn)行手動(dòng)注冊(cè)了。

手動(dòng)注冊(cè)需要調(diào)用WKWebView的私有api,注冊(cè)scheme的私有apiregisterSchemeForCustomProtocol:,注銷(xiāo)的私有apiunregisterSchemeForCustomProtocol:,有些同學(xué)會(huì)考慮到在項(xiàng)目中使用私有api在審核時(shí)會(huì)被蘋(píng)果爸爸打回,我這里測(cè)試不會(huì),如果你遇到了被打回的情況,可以把私有api拆分成多個(gè)字符串,然后把多個(gè)字符串拼接在一起。

所以攔截WKWebView網(wǎng)絡(luò)請(qǐng)求的步驟是:
(1)自定義NSURLProtocol,用來(lái)處理攔截到的網(wǎng)絡(luò)請(qǐng)求。
(2)利用系統(tǒng)提供的NSURLProtocol注冊(cè)(1)中自定義的NSURLProtocol。
(3)通過(guò)私有api注冊(cè)需要攔截的網(wǎng)絡(luò)請(qǐng)求的scheme。
(4)在合適的時(shí)機(jī)注銷(xiāo)(3)中注冊(cè)的scheme。

H5啟動(dòng)性能優(yōu)化

H5最讓人詬病的一點(diǎn)就是它的用戶體驗(yàn)沒(méi)有native好,其實(shí)H5的交互效果(不包括復(fù)雜的動(dòng)效)已經(jīng)非常接近于native了,所以剩下的缺點(diǎn)總體來(lái)說(shuō)就是關(guān)于WebView的渲染問(wèn)題,我們?cè)趯?xiě)native界面的時(shí)候,頁(yè)面一打開(kāi)就能看到我們創(chuàng)建的UI元素,但是遠(yuǎn)程的H5不能,因?yàn)檫h(yuǎn)程H5的頁(yè)面元素都需要去服務(wù)器獲取,隨后經(jīng)過(guò)渲染才能展示,過(guò)程大致如下:

所以,一個(gè)H5頁(yè)面完全展示給用戶所需要的時(shí)間遠(yuǎn)比native頁(yè)面長(zhǎng)的多。

所以針對(duì)于移動(dòng)端來(lái)說(shuō),優(yōu)化H5啟動(dòng)性能的點(diǎn)主要有兩個(gè):
(1)優(yōu)化WebView的啟動(dòng)速度
(2)讓HTML/CSS/JavaScript文件下載的更快一些,也就是離線包方案。

(1)優(yōu)化WebView的啟動(dòng)速度

App打開(kāi)的時(shí)候并不會(huì)初始化瀏覽器內(nèi)核,當(dāng)我們創(chuàng)建一個(gè)WKWebView的時(shí)候,系統(tǒng)才會(huì)初始化瀏覽器內(nèi)核,也就是說(shuō),當(dāng)我們第一次用WebView打開(kāi)H5的時(shí)候,H5的顯示時(shí)間需要加上瀏覽器內(nèi)核啟動(dòng)時(shí)間,所以?xún)?yōu)化點(diǎn)就在于優(yōu)化瀏覽器內(nèi)核啟動(dòng)時(shí)間。

很多解決方案是初始化一個(gè)單例WebView,讓這一個(gè)WebView全局可用,這樣打開(kāi)每個(gè)H5的時(shí)候用的都是同一個(gè)WebView對(duì)象,工作原理有點(diǎn)接近PC端瀏覽器,這樣做的缺點(diǎn)就是如果這個(gè)WebView因?yàn)槟承┰驅(qū)е庐惓=K止之后,再用這個(gè)WebView打開(kāi)H5可能會(huì)產(chǎn)生一些意料之外的問(wèn)題,所以,這里推薦使用另外一種解決方案。

另外一種解決方案就是維護(hù)一個(gè)全局的WebView復(fù)用池,復(fù)用原理同UITableViewCell一樣,這里不細(xì)講。如果一個(gè)WebView一直是正常工作的就放入復(fù)用池中,如果一個(gè)WebView因?yàn)槟承┰虍惓=K止,那么就把這個(gè)WebView從復(fù)用池中移除。

無(wú)論是哪種復(fù)用方案,都會(huì)產(chǎn)生一個(gè)新問(wèn)題,當(dāng)我們利用復(fù)用WebView打開(kāi)一個(gè)新H5的時(shí)候,瀏覽器的瀏覽歷史記錄里還保留著上一次打開(kāi)的H5的痕跡,所以,我們需要在復(fù)用時(shí)清除這個(gè)痕跡并讓頁(yè)面打開(kāi)一個(gè)空白頁(yè)。

(2)使用離線包打包H5的靜態(tài)資源。

我們通過(guò)一個(gè)遠(yuǎn)程URL打開(kāi)H5就可以理解為是在線打開(kāi)的。

把一個(gè)H5HTML/CSS/JavaScript文件分別打包成靜態(tài)資源文件保存在服務(wù)器,這些保存在服務(wù)器的靜態(tài)資源文件就可以理解為是離線包,移動(dòng)端可以選擇一個(gè)合適的時(shí)機(jī)下載離線包,然后在本地解壓縮,當(dāng)我們打開(kāi)一個(gè)H5的時(shí)候其實(shí)打開(kāi)的是已經(jīng)下載到本地的HTML文件,免去了在線拉取資源的過(guò)程,從而節(jié)省了時(shí)間。

當(dāng)H5頁(yè)面需要更新的時(shí)候,直接對(duì)離線包做增量更新可以了。

更多細(xì)節(jié)可參考bang的這篇文章。

基于WKWebView封裝的JXBWKWebView

1.內(nèi)核決定了goback返回不刷新問(wèn)題需要前端支持
2.支持natigationBackItem & navigationLeftItems
3.支持自定義rightBarButtonItem
4.支持進(jìn)度條
5.提供cookie解決方案,首次自己加,后續(xù)的ajax請(qǐng)求自動(dòng)加,302請(qǐng)求自動(dòng)加
6.支持?jǐn)r截WKWebView攔截網(wǎng)絡(luò)請(qǐng)求
7.支持POST請(qǐng)求
8.支持子類(lèi)繼承
9.支持?jǐn)r截URL的交互方式,支持自定義攔截URL操作。
10.提供nativeH5的交互解決方案,支持自定義MessageHandler操作。
11.提供H5秒開(kāi)解決方案,server使用Go實(shí)現(xiàn)。
12.iOSAndroidJS提供統(tǒng)一的原生調(diào)用方式。

github地址:JXBWKWebView,如果覺(jué)得項(xiàng)目不錯(cuò)可以點(diǎn)個(gè)star支持一下,謝謝~

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/98908.html

相關(guān)文章

  • WKWebView使用指南|功能豐富JXBWKWebView

    摘要:使用這種方案攔截的網(wǎng)絡(luò)請(qǐng)求造成的問(wèn)題就是請(qǐng)求數(shù)據(jù)被清空,還是所為,看源碼主要看代碼中間那兩句注釋?zhuān)笾碌囊馑季褪遣粫?huì)在進(jìn)程間通信發(fā)送的。如何解決終極思路就是雖然的會(huì)在進(jìn)程間通信時(shí)被丟棄,但不會(huì)。 github地址:JXBWKWebView,如果覺(jué)得項(xiàng)目不錯(cuò)可以點(diǎn)個(gè)star支持一下,謝謝~ 前言 目前iOS系統(tǒng)已經(jīng)更新到iOS11,大多數(shù)項(xiàng)目向下兼容最多兼容到iOS8,因此,在項(xiàng)目中對(duì)...

    mist14 評(píng)論0 收藏0
  • 深入理解 WKWebView(入門(mén)篇)—— WebKit 源碼調(diào)試與分析

    摘要:雖然蘋(píng)果官方提供了關(guān)于的與使用說(shuō)明,但這并不能滿足開(kāi)發(fā)者們的需求,各類(lèi)復(fù)雜場(chǎng)景依舊讓我們焦頭爛額,而解決方案卻不易尋找。二源碼下載編譯及調(diào)試之前我們首先需要獲取一份蘋(píng)果官方的源碼。 一、前言移動(dòng)互聯(lián)網(wǎng)時(shí)代,網(wǎng)頁(yè)依舊是內(nèi)容展示的重要媒介,這離不開(kāi) WebKit 瀏覽內(nèi)核技術(shù)的支持與發(fā)展。在 iOS 平臺(tái)下開(kāi)發(fā)者們...

    funnyZhang 評(píng)論0 收藏0
  • Web離線技術(shù)(一)—— 技術(shù)選型

    摘要:上面提到在安卓完全不需要像這樣大費(fèi)周章的繞彎路,所以安卓可能就不需要這個(gè)自定義的,這樣又會(huì)導(dǎo)致面臨著與安卓差異化嚴(yán)重問(wèn)題。前言 最早接觸離線包的概念要追溯到16年初,項(xiàng)目迎來(lái)大改版,其中重點(diǎn)項(xiàng)目之一就是離線包方案的制定與實(shí)施。離線包顧名思義就是將H5/CSS/JS和資源文件打包提前下發(fā)到App中,這樣App在加載網(wǎng)頁(yè)的時(shí)候?qū)嶋H上加載的是本地的文件,減少網(wǎng)絡(luò)請(qǐng)求來(lái)提高網(wǎng)頁(yè)的渲染速度,并實(shí)現(xiàn)動(dòng)態(tài)...

    null1145 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

Cobub

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<