摘要:使用這種方案攔截的網(wǎng)絡(luò)請求造成的問題就是請求數(shù)據(jù)被清空,還是所為,看源碼主要看代碼中間那兩句注釋,大致的意思就是不會在進(jìn)程間通信發(fā)送的。如何解決終極思路就是雖然的會在進(jìn)程間通信時(shí)被丟棄,但不會。
github地址:JXBWKWebView,如果覺得項(xiàng)目不錯可以點(diǎn)個(gè)star支持一下,謝謝~
前言目前iOS系統(tǒng)已經(jīng)更新到iOS11,大多數(shù)項(xiàng)目向下兼容最多兼容到iOS8,因此,在項(xiàng)目中對WebView組件進(jìn)行重構(gòu)再封裝時(shí),打算直接舍棄UIWebView轉(zhuǎn)用WKWebView。
如果你目前正在網(wǎng)上瀏覽關(guān)于WKWebView的一些文章,相信你已經(jīng)清楚了WKWebView的優(yōu)點(diǎn),也目睹了大家在使用WKWebView的過程中遇到的坑,而這篇文章,會對到目前為止大家遇到的關(guān)于WKWebView的問題給出詳細(xì)的解決方案,文章的最后,也會講述關(guān)于對WKWebView進(jìn)行性能優(yōu)化的方案。
解決的問題goback返回頁面不刷新
Cookie
POST請求失效
crash
navigationBackItem
進(jìn)度條
Native與JS的交互
優(yōu)化H5頁面啟動速度
入坑goback Api返回不刷新
在之前使用UIWebView時(shí),調(diào)用goback后,頁面會刷新。使用WKWebView后,調(diào)用goback,即便調(diào)用reload方法,H5依然不會刷新。
原因是調(diào)用goback時(shí),UIWebView會觸發(fā)onload事件,WKWebView不會觸發(fā)onload事件,如果前端依舊在onload事件中處理iOS的頁面返回事件,是處理不了的,解決方案是讓前端使用onpageshow事件監(jiān)聽WKWebView的頁面goback事件。
前端代碼如下:
window.addEventListener("pageshow", function(event){ if(event.persisted){ location.reload(); } });
為了查看頁面是直接從服務(wù)器上載入還是從緩存中讀取,可以使用 PageTransitionEvent對象的persisted屬性來判斷。
如果頁面從瀏覽器的緩存中讀取該屬性返回ture,否則返回 false。然后在根據(jù)true或false在執(zhí)行相應(yīng)的頁面刷新動作或者直接ajax請求接口更新數(shù)據(jù)。
關(guān)于onload和onpageshow事件在safari和chrome上的區(qū)別如下:
. | 事件 | Chrome | Safari |
---|---|---|---|
第一次加載頁面 | onload | 觸發(fā) | 觸發(fā) |
第一次加載頁面 | onpageshow | 觸發(fā) | 觸發(fā) |
從其他頁面返回 | onload | 觸發(fā) | 不觸發(fā) |
從其他頁面返回 | 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之外,我們暫且稱為WK Process(隨便起的)。
在使用AFN進(jìn)行網(wǎng)絡(luò)請求時(shí),如果server使用set-cookie將cookie寫入header,AFN接受到響應(yīng)后會將cookie保存到NSHTTPCookieStorage,下次如果是同域的request url,AFN會將cookie從NSHTTPCookieStorage 中取出然后作為request header的cookie發(fā)送給server端,而這一切發(fā)生在App Process。
那么在WK Process工作的WKWebView在發(fā)送網(wǎng)絡(luò)請求及收到響應(yīng)后對cookie的處理是否也會使用NSHTTPCookieStorage 呢,經(jīng)過測試后,答案是yes,但在存取的過程中會有一些問題需要注意。
測試進(jìn)行:iphone 6p iOS:10
測試過程:
1.client使用AFN發(fā)送一個(gè)網(wǎng)絡(luò)請求
2.server接收到請求后,使用set-cookie寫入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所在頁面,使用loadRequest隨便發(fā)送一個(gè)同域的網(wǎng)絡(luò)請求,在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); }
也可以使用如下代碼輸出該請求的server response header的set-cookie:
NSString *cookieString = [[response allHeaderFields] valueForKey:@"Set-Cookie"];
那么,WKWebView將cookie存入NSHTTPCookieStorage 的時(shí)機(jī)是什么時(shí)候?
1.JS執(zhí)行document.cookie或服務(wù)器set-cookie注入的Cookie會很快同步到NSHTTPCookieStorage中。
2.H5頁面進(jìn)行跳轉(zhuǎn)時(shí)會將Cookie同步到NSHTTPCookieStorage中。
3.控制器頁面跳轉(zhuǎn)時(shí)會將Cookie同步到NSHTTPCookieStorage中。
WKWebView使用loadRequest發(fā)送網(wǎng)絡(luò)時(shí)不會主動將cookie存入到NSHTTPCookieStorage 中,即使是同域的請求。
所以,如果你有一個(gè)請求需要附帶cookie,就不能直接加載URL,需要你根據(jù)URL創(chuàng)建一個(gè)URLMutableRequest對象,將需要附加的cookie使用addValue:forHTTPHeaderField:方法手動將cookie添加到request header中,但這僅能解決首次請求不帶cookie的問題,如果頁面發(fā)送ajax請求,cookie同樣帶不上,解決方案是通過document.cookie設(shè)置cookie,也就是說在你實(shí)例化WKWebView時(shí)就應(yīng)該注入相關(guān)script。
上面我們說的都是在同域的情況下,如果發(fā)生302請求(可以理解域名發(fā)生變化,也就是說不同域),上面的解決方案就用不了了,這時(shí)就需要你在WKWebView的decidePolicyForNavigationAction代理方法中攔截URL,判斷當(dāng)前URL與初次請求的URL是否同域,如果不同域,在該代理方法中獲取到當(dāng)前請求的request對象并copy出一個(gè)新的對象,通過addValue:forHeaderField:方法將cookie手動添加到header中,然后讓WKWebView使用loadRequest重新加載這個(gè)copy出來的新的request對象。
問題就沒了嗎?NO,上面的解決方法同樣有局限,即只能解決后續(xù)的同域ajax請求不加cookie的問題。如果發(fā)生iframe跨域請求,我們攔截不到請求,所以也沒法給請求的header手動添加cookie,WKWebView只適合加載mainFrame 請求。
所以,要和前端同學(xué)提前打好招呼,盡量避免使用iframe,能使用ajax的地方盡量使用ajax,另一方面,iframe現(xiàn)在已經(jīng)不怎么提倡使用了,除非是解決一些特殊的問題。
POST請求
使用WKWebView無法正常發(fā)送POST請求。
所以,這個(gè)時(shí)候我們需要通過自定義NSURLProtocol攔截WKWebView的網(wǎng)絡(luò)請求,并且,使用NSURLProtocol攔截WKWebView網(wǎng)絡(luò)請求的好處還有就是:
1.如果產(chǎn)品需求要求client需要日志采集,包括所有的網(wǎng)絡(luò)請求記錄,通過這種方式你是可以獲取到的。
2.如果公司對用戶體驗(yàn)的要求較高,可以在這里實(shí)現(xiàn)WKWebView初始化和相關(guān)網(wǎng)絡(luò)請求的并發(fā)執(zhí)行,以縮短用戶在client打開H5的速度,甚至可以秒開,達(dá)到和native相同的體驗(yàn)。
但問題是正常情況下NSURLProtocol是攔截不到WKWebView的網(wǎng)絡(luò)請求的。
通過觀看webkit的源碼(github直接搜webkit)可以得到的結(jié)果是,通過WKWebView發(fā)送一個(gè)網(wǎng)絡(luò)請求其實(shí)也會走NSURLProtocol,只不過Apple把http和https這兩個(gè)scheme給過濾掉了,導(dǎo)致我們攔截不到WKWebView發(fā)送的網(wǎng)路請求。
因此,在我們自定義NSURLProtocol時(shí),要通過使用私有api來注冊一些scheme,注冊scheme的類名叫WKBrowsingContextController ,WKWebView中有一個(gè)屬性叫browsingContextController,就是這個(gè)類的對象。注冊的方法叫registerSchemeForCustomProtocol:,知道這個(gè)私有api,我們就可以通過target-action的方式,注冊WKWebView發(fā)起網(wǎng)絡(luò)請求時(shí)需要攔截的URL scheme,此時(shí)注冊的scheme至少要包括3種,分別是http、https、post。
問題還沒玩,解決一個(gè)問題的同時(shí)往往伴隨另一個(gè)問題的產(chǎn)生。
使用這種方案攔截WKWebView的網(wǎng)絡(luò)請求造成的問題就是post請求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()); }
主要看代碼中間那兩句注釋,大致的意思就是Apple不會在進(jìn)程間通信發(fā)送http的body。
因?yàn)?b>WKWebView屬于webkit框架,因此WKWebView的網(wǎng)絡(luò)請求、內(nèi)容加載/渲染都是在WK Process中進(jìn)行,但NSURLProtocol攔截請求還在App Process,一旦注冊http(s) scheme后,網(wǎng)絡(luò)請求將從獨(dú)立進(jìn)程中發(fā)送到App Process,這樣自定義的NSURLProtocol才能攔截到網(wǎng)絡(luò)請求,為了提升進(jìn)程間通信效率,出于性能上的考慮,Apple會將request的body數(shù)據(jù)丟棄,因?yàn)?b>body數(shù)據(jù)(二進(jìn)制類型)大小沒有限制,size偏大的話就會對數(shù)據(jù)傳輸效率有嚴(yán)重影響進(jìn)而影響到攔截請求時(shí)的操作及延時(shí)后續(xù)的網(wǎng)絡(luò)請求,因此,Apple在進(jìn)行進(jìn)程間通信時(shí)會把post請求的body丟棄。
如何解決?
終極思路就是雖然http的body會在進(jìn)程間通信時(shí)被丟棄,但header不會。
因此,解決問題步驟如下:
WKWebView在loadRequest前對request對象進(jìn)行一些處理,這個(gè)request對象我們記為old request。
1.記下old request的scheme和NSData類型的http body。
2.獲取當(dāng)前old request的URL,替換URL的scheme為post(這也是我們?yōu)槭裁匆谇懊媸褂?b>NSURLProtocol注冊post scheme的原因),并根據(jù)這個(gè)替換好的URL重新生成一個(gè)新的NSMutableURLRequest對象,這個(gè)對象記為new request。
3.給new request的header賦值,把步驟1中獲取的scheme和http body手動添加到這個(gè)new request的header中,如果這個(gè)post請求需要附帶cookie的話,你也要把cookie從old request中拿出來放到new request的header中。
4.讓WKWebView加載這個(gè)new request。
WKWebView發(fā)送新的request時(shí)(這個(gè)request url的scheme是post),我們可以在自定義NSURLProtocol中攔截到這個(gè)請求,執(zhí)行如下步驟:
1.替換scheme,此時(shí)的scheme是post,你需要把post scheme替換成old request的scheme,這個(gè)字段我們之前已經(jīng)保存下來了。
2.替換scheme后會生成一個(gè)新的URL,根據(jù)這個(gè)新的URL生成一個(gè)NSURLMutableRequest對象,將之前保存的http body、cookie放到這個(gè)新的request對象的header中。
3.使用NSURLSession,根據(jù)新的request對象發(fā)送網(wǎng)絡(luò)請求,然后通過NSURLProtocol Client將加載結(jié)果返回給WKWebView。
注意:在這幾個(gè)步驟中一共產(chǎn)生了3個(gè)request對象。
crash
1.alert彈窗
引起crash的原因是js調(diào)用alert()引起的,也就是說,當(dāng)WKWebView銷毀的時(shí)候,JS剛好執(zhí)行了alert(),原生的 alert 彈窗可能彈不出來,completionHandler回調(diào)最后沒有被執(zhí)行,導(dǎo)致crash;另一種情況是在WKWebView剛打開,JS就執(zhí)行alert(),這個(gè)時(shí)候由于 WKWebView所在的UIViewController的push或present的動畫尚未結(jié)束,alert框可能彈不出來,completionHandler最后沒有被執(zhí)行,導(dǎo)致crash。
解決方案:獲取當(dāng)前window上最終的UIViewController,判斷UIViewController是否未被銷毀、UIViewController是否已經(jīng)加載完成、動畫是否執(zhí)行完畢。
2.另一個(gè)crash發(fā)生在WKWebView退出前調(diào)用:
執(zhí)行JS代碼的情況下。WKWebView 退出并被釋放后導(dǎo)致completionHandler變成野指針,而此時(shí) javaScript Core 還在執(zhí)行JS代碼,待 javaScript Core 執(zhí)行完畢后會調(diào)用completionHandler(),導(dǎo)致crash。這個(gè)crash只發(fā)生在iOS 8 系統(tǒng)上,參考Apple Open Source,在iOS9及以后系統(tǒng)蘋果已經(jīng)修復(fù)了這個(gè)bug,主要是對completionHandler block做了copy(refer:?https://trac.webkit.org/changeset/179160);對于iOS 8系統(tǒng),可以通過在completionHandler里retain WKWebView防止completionHandler被過早釋放。
解決方案是使用method swizzling hook了這個(gè)系統(tǒng)方法,在回調(diào)中對self進(jìn)行了強(qiáng)引用來保證在執(zhí)行completionHandler的時(shí)候self還在。
navigationBackItem
實(shí)現(xiàn)導(dǎo)航欄back item的方式有兩種。
自定義導(dǎo)航欄
這個(gè)比較簡單,根據(jù)WebView是否可以goback決定navigationBarButtonItems的個(gè)數(shù)和功能。
使用系統(tǒng)默認(rèn)的導(dǎo)航返回按鈕,類似于微信
難點(diǎn)在于我們要獲取到點(diǎn)擊系統(tǒng)導(dǎo)航返回按鈕時(shí)的事件,然后進(jìn)行一些處理。
點(diǎn)擊返回按鈕時(shí),實(shí)際上調(diào)用了UINavigationController的navigationBar:shouldPopItem方法,我們可以使用method swizzling hook住這個(gè)方法,在這個(gè)方法中通過調(diào)用代理方法的方式告訴WKWebView所在的UIViewController進(jìn)行相應(yīng)的處理。
UIProgressView
這個(gè)簡單,也不多說了。
Native與JS的交互
攔截URL
在WKWebView的decidePolicyForNavigationAction代理方法中可對URL進(jìn)行攔截,一般使用攔截URL的方式URL的格式如下:
scheme://host?paramKey=paramValue
一般情況下scheme對應(yīng)業(yè)務(wù),host是業(yè)務(wù)對應(yīng)的服務(wù)(method),?后面就是參數(shù)。
使用攔截URL的交互方式時(shí),業(yè)務(wù)邏輯不復(fù)雜情況下,JS調(diào)用Native沒什么問題,但當(dāng)業(yè)務(wù)邏輯復(fù)雜時(shí),JS需要拿到Native處理好的回調(diào)數(shù)據(jù)的話,處理起來將十分麻煩。
并且使用攔截URL的交互方式,不利于今后JS與Native的業(yè)務(wù)拓展。
使用Bridge
WKWebView對JS與Native通過Bridge交互提供了非常好的支持,我們可以通過ScriptMessageHandler來達(dá)成各種交互的目的。使用ScriptMessageHandler添加腳本的具體代碼在此不多贅述,大家可自行研究。重點(diǎn)說一下Bridge的腳本代碼。
現(xiàn)在關(guān)于Bridge的開源解決方案有很多,但基本都遵循一個(gè)模式,在注入的Bridge腳本代碼中,定義好供JS調(diào)用的方法名稱,該方法通常包括如下幾個(gè)參數(shù):
1.要調(diào)用的native業(yè)務(wù)模塊名稱(有些有,有些沒有,如果項(xiàng)目中實(shí)施模塊化建議加上)。
2.要調(diào)用的native服務(wù)名稱(通常是方法名)。
3.傳遞給native的參數(shù)(也就是方法需要的參數(shù))。
4.callback,JS調(diào)用native的方法后腳本需要調(diào)用的回調(diào)。
詳細(xì)來描述一下使用Bridge整個(gè)交互過程,從創(chuàng)建Bridge腳本到Bridge腳本執(zhí)行callback:
Bridge腳本下稱腳本。
1.腳本為JS提供JavaScript語言的方法,該方法用來調(diào)用native方法,方法的4個(gè)參數(shù)如前所述。
2.在該方法中,會根據(jù)前述的部分參數(shù)生成一個(gè)唯一標(biāo)識符,記為identifier。
3.在腳本中給全局對象(window)綁定一個(gè)字典屬性,key是步驟2中的identifier,value是callback。
4.調(diào)用messagehandler的postMessage函數(shù),將前述的參數(shù)和identifier 都發(fā)送給native(沒發(fā)callback,callback的作用主要就是步驟3)。
5.前端調(diào)用你的腳本中的代碼調(diào)用native的方法,具體代碼可參見Apple官方文檔。
5.native在自定義的MessageHandler對象的userContentController:didReceiveScriptMessage:代理方法中接收到JS傳過來的參數(shù)(記為param)。獲取到了模塊名稱、服務(wù)名稱、參數(shù)、identifier等,額外的,需要創(chuàng)建幾個(gè)block,對應(yīng)JS那邊的callback,比如JS那邊有個(gè)success callback,那么在native就要有一個(gè)success block,而創(chuàng)建的這些block,我們會賦值給前面說的那個(gè)param里面,那么現(xiàn)在,這個(gè)param有如下幾個(gè)值:
targetName(模塊名稱) actionName(服務(wù)名稱) identifier(通過該屬性最后我們可以找到j(luò)s的callback) success block failure block progress block 上面這些參數(shù)基本上已經(jīng)夠了,如果需要擴(kuò)展就自己加吧
那么這些block里面的操作主要是什么呢?block封裝了WKWebView的evaluateJavaScript操作,這個(gè)block最后可以拿到native處理任務(wù)后的結(jié)果和identifier,然后把結(jié)果轉(zhuǎn)換為json數(shù)據(jù),通過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í)例化對象,根據(jù)actionName調(diào)用方法,并把參數(shù)(param)傳遞過去,目標(biāo)對象將任務(wù)處理完成后,調(diào)用param的success block, failure block, progress block,將任務(wù)處理的結(jié)果回傳給JS。
交互總結(jié)
無論是攔截URL還是使用Bridge,最后調(diào)用native方法的機(jī)制都是利用target-action,使用target-action機(jī)制的原因之一就是可減少類與類之間的耦合程度,減少硬編碼的同時(shí)有利于今后的業(yè)務(wù)擴(kuò)展。
當(dāng)然,如果你不喜歡target-action的方案,也可以自行擴(kuò)展。
攔截WKWebView的網(wǎng)絡(luò)請求
通過觀看WebKit的源碼可以了解到WKWebView是支持?jǐn)r截網(wǎng)絡(luò)請求的,但是WebKit沒有注冊需要攔截的scheme,所以我們只能進(jìn)行手動注冊了。
手動注冊需要調(diào)用WKWebView的私有api,注冊scheme的私有api是registerSchemeForCustomProtocol:,注銷的私有api是unregisterSchemeForCustomProtocol:,有些同學(xué)會考慮到在項(xiàng)目中使用私有api在審核時(shí)會被蘋果爸爸打回,我這里測試不會,如果你遇到了被打回的情況,可以把私有api拆分成多個(gè)字符串,然后把多個(gè)字符串拼接在一起。
所以攔截WKWebView網(wǎng)絡(luò)請求的步驟是:
(1)自定義NSURLProtocol,用來處理攔截到的網(wǎng)絡(luò)請求。
(2)利用系統(tǒng)提供的NSURLProtocol注冊(1)中自定義的NSURLProtocol。
(3)通過私有api注冊需要攔截的網(wǎng)絡(luò)請求的scheme。
(4)在合適的時(shí)機(jī)注銷(3)中注冊的scheme。
H5啟動性能優(yōu)化
H5最讓人詬病的一點(diǎn)就是它的用戶體驗(yàn)沒有native好,其實(shí)H5的交互效果(不包括復(fù)雜的動效)已經(jīng)非常接近于native了,所以剩下的缺點(diǎn)總體來說就是關(guān)于WebView的渲染問題,我們在寫native界面的時(shí)候,頁面一打開就能看到我們創(chuàng)建的UI元素,但是遠(yuǎn)程的H5不能,因?yàn)檫h(yuǎn)程H5的頁面元素都需要去服務(wù)器獲取,隨后經(jīng)過渲染才能展示,過程大致如下:
所以,一個(gè)H5頁面完全展示給用戶所需要的時(shí)間遠(yuǎn)比native頁面長的多。
所以針對于移動端來說,優(yōu)化H5啟動性能的點(diǎn)主要有兩個(gè):
(1)優(yōu)化WebView的啟動速度
(2)讓HTML/CSS/JavaScript文件下載的更快一些,也就是離線包方案。
App打開的時(shí)候并不會初始化瀏覽器內(nèi)核,當(dāng)我們創(chuàng)建一個(gè)WKWebView的時(shí)候,系統(tǒng)才會初始化瀏覽器內(nèi)核,也就是說,當(dāng)我們第一次用WebView打開H5的時(shí)候,H5的顯示時(shí)間需要加上瀏覽器內(nèi)核啟動時(shí)間,所以優(yōu)化點(diǎn)就在于優(yōu)化瀏覽器內(nèi)核啟動時(shí)間。
很多解決方案是初始化一個(gè)單例WebView,讓這一個(gè)WebView全局可用,這樣打開每個(gè)H5的時(shí)候用的都是同一個(gè)WebView對象,工作原理有點(diǎn)接近PC端瀏覽器,這樣做的缺點(diǎn)就是如果這個(gè)WebView因?yàn)槟承┰驅(qū)е庐惓=K止之后,再用這個(gè)WebView打開H5可能會產(chǎn)生一些意料之外的問題,所以,這里推薦使用另外一種解決方案。
另外一種解決方案就是維護(hù)一個(gè)全局的WebView復(fù)用池,復(fù)用原理同UITableViewCell一樣,這里不細(xì)講。如果一個(gè)WebView一直是正常工作的就放入復(fù)用池中,如果一個(gè)WebView因?yàn)槟承┰虍惓=K止,那么就把這個(gè)WebView從復(fù)用池中移除。
無論是哪種復(fù)用方案,都會產(chǎn)生一個(gè)新問題,當(dāng)我們利用復(fù)用WebView打開一個(gè)新H5的時(shí)候,瀏覽器的瀏覽歷史記錄里還保留著上一次打開的H5的痕跡,所以,我們需要在復(fù)用時(shí)清除這個(gè)痕跡并讓頁面打開一個(gè)空白頁。
我們通過一個(gè)遠(yuǎn)程URL打開H5就可以理解為是在線打開的。
把一個(gè)H5的HTML/CSS/JavaScript文件分別打包成靜態(tài)資源文件保存在服務(wù)器,這些保存在服務(wù)器的靜態(tài)資源文件就可以理解為是離線包,移動端可以選擇一個(gè)合適的時(shí)機(jī)下載離線包,然后在本地解壓縮,當(dāng)我們打開一個(gè)H5的時(shí)候其實(shí)打開的是已經(jīng)下載到本地的HTML文件,免去了在線拉取資源的過程,從而節(jié)省了時(shí)間。
當(dāng)H5頁面需要更新的時(shí)候,直接對離線包做增量更新可以了。
更多細(xì)節(jié)可參考bang的這篇文章。
基于WKWebView封裝的JXBWKWebView1.內(nèi)核決定了goback返回不刷新問題需要前端支持
2.支持natigationBackItem & navigationLeftItems
3.支持自定義rightBarButtonItem
4.支持進(jìn)度條
5.提供cookie解決方案,首次自己加,后續(xù)的ajax請求自動加,302請求自動加
6.支持?jǐn)r截WKWebView攔截網(wǎng)絡(luò)請求
7.支持POST請求
8.支持子類繼承
9.支持?jǐn)r截URL的交互方式,支持自定義攔截URL操作。
10.提供native與H5的交互解決方案,支持自定義MessageHandler操作。
11.提供H5秒開解決方案,server使用Go實(shí)現(xiàn)。
12.iOS和Android為JS提供統(tǒng)一的原生調(diào)用方式。
github地址:JXBWKWebView,如果覺得項(xiàng)目不錯可以點(diǎn)個(gè)star支持一下,謝謝~
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/53203.html
摘要:使用這種方案攔截的網(wǎng)絡(luò)請求造成的問題就是請求數(shù)據(jù)被清空,還是所為,看源碼主要看代碼中間那兩句注釋,大致的意思就是不會在進(jìn)程間通信發(fā)送的。如何解決終極思路就是雖然的會在進(jìn)程間通信時(shí)被丟棄,但不會。 github地址:JXBWKWebView,如果覺得項(xiàng)目不錯可以點(diǎn)個(gè)star支持一下,謝謝~ 前言 目前iOS系統(tǒng)已經(jīng)更新到iOS11,大多數(shù)項(xiàng)目向下兼容最多兼容到iOS8,因此,在項(xiàng)目中對...
摘要:雖然蘋果官方提供了關(guān)于的與使用說明,但這并不能滿足開發(fā)者們的需求,各類復(fù)雜場景依舊讓我們焦頭爛額,而解決方案卻不易尋找。二源碼下載編譯及調(diào)試之前我們首先需要獲取一份蘋果官方的源碼。 一、前言移動互聯(lián)網(wǎng)時(shí)代,網(wǎng)頁依舊是內(nèi)容展示的重要媒介,這離不開 WebKit 瀏覽內(nèi)核技術(shù)的支持與發(fā)展。在 iOS 平臺下開發(fā)者們...
摘要:上面提到在安卓完全不需要像這樣大費(fèi)周章的繞彎路,所以安卓可能就不需要這個(gè)自定義的,這樣又會導(dǎo)致面臨著與安卓差異化嚴(yán)重問題。前言 最早接觸離線包的概念要追溯到16年初,項(xiàng)目迎來大改版,其中重點(diǎn)項(xiàng)目之一就是離線包方案的制定與實(shí)施。離線包顧名思義就是將H5/CSS/JS和資源文件打包提前下發(fā)到App中,這樣App在加載網(wǎng)頁的時(shí)候?qū)嶋H上加載的是本地的文件,減少網(wǎng)絡(luò)請求來提高網(wǎng)頁的渲染速度,并實(shí)現(xiàn)動態(tài)...
閱讀 2660·2021-10-11 10:58
閱讀 1254·2021-09-29 09:34
閱讀 1660·2021-09-26 09:46
閱讀 3906·2021-09-22 15:31
閱讀 801·2019-08-30 15:54
閱讀 1532·2019-08-30 13:20
閱讀 1312·2019-08-30 13:13
閱讀 1556·2019-08-26 13:52