前言
我們大前端團隊內部 ?每週一練 的知識複習計劃繼續加油,本篇文章是 《Hybrid APP 混合應用專題》 主題的第二期和第三期的合集。
這一期共整理了 10 個問題,和相應的參考答案,文字和圖片較多,建議大家可以收藏,根據文章目錄來閱讀。
之前分享的每週內容,我都整理到掘金收藏集 ?《EFT每週一練》 上啦,歡迎點贊收藏咯??。
內容回顧:
文章收錄:
本系列所有文章,都將收錄在 Github 上,歡迎點選查閱。
注:本文整理部分資料來源網路,有些圖片/段落找不到原文出處,如有侵權,聯絡刪除。
一、iOS 平臺中 UIWebView 與 WKWebView 有什麼區別?
UIWebView
是蘋果繼承於 UIView
封裝的一個載入 web 內容的類,它可以載入任何遠端的web資料展示在你的頁面上,你可以像瀏覽器一樣前進後退重新整理等操作。不過蘋果在 iOS8 以後推出了 WKWebView
來載入 Web,並應用於 iOS 和 OSX 中,它取代了 UIWebView
和 WebView
,在兩個平臺上支援同一套 API。
它脫離於 UIWebView
的設計,將原本的設計拆分成14個類,和3個代理協議,雖然是這樣但是瞭解之後其實用法比較簡單,依照職責單一的原則,每個協議做的事情根據功能分類。
WKWebView
與 UIWebView
的區別:
-
WKWebView
的記憶體遠遠沒有UIWebView
的開銷大,而且沒有快取; -
WKWebView
擁有高達 60FPS 滾動重新整理率及內建手勢; -
WKWebView
支援了更多的 HTML5 特性; -
WKWebView
高效的 app 和 web 資訊交換通道; -
WKWebView
允許JavaScript
的Nitro
庫載入並使用,UIWebView
中限制了; -
WKWebView
目前缺少關於頁碼相關的 API; -
WKWebView
提供載入網頁進度的屬性; -
WKWebView
使用 Safari 相同的 JavaScript 引擎; -
WKWebView
增加載入進度屬性:estimatedProgress
; -
WKWebView
不支援頁面快取,需要自己注入cookie
, 而UIWebView
是自動注入cookie
; -
WKWebView
無法傳送POST
引數問題; -
WKWebView
可以和js直接互調函式,不像UIWebView
需要第三方庫WebViewJavascriptBridge
來協助處理和 js 的互動;
注意:
大多數App需要支援 iOS7
以上的版本,而 WKWebView
只在 iOS8
後才能用,所以需要一個相容性方案,既 iOS7
下用 UIWebView
,iOS8
後用 WKWebView
。但是目前 IOS10
以下的系統以及很少了,
小結:
WKWebView
相較於 UIWebView
在整體上有較大的提升,滿足 iOS 上面使用同一套控制元件的功能,同時對整個記憶體的開銷以及滾動重新整理率和 JS 互動做了優化的處理。
依據職責單一原則,拆分成了三個協議去實現 WebView
的響應,解耦了 JS 互動和載入進度的響應處理。
WKWebView
沒有做快取處理,所以對網頁需要快取的載入效能要求沒那麼高的還是可以考慮 UIWebView
。
二、WKWebView 有哪一些坑?
參考文章:《WKWebView 那些坑》
1. WKWebView 白屏問題
WKWebView
實際上是個多程式元件,這也是它載入速度更快,記憶體暫用更低的原因。
在 UIWebView
上當記憶體佔用太大的時候,App Process 會 crash;而在 WKWebView
上當總體的記憶體佔用比較大的時候,WebContent Process 會 crash,從而出現白屏現象。
解決辦法:
- 藉助 WKNavigtionDelegate
當 WKWebView
總體記憶體佔用過大,頁面即將白屏的時候,系統會呼叫上面的回撥函式,我們在該函式裡執行[webView reload]
(這個時候 webView.URL
取值尚不為nil
)解決白屏問題。在一些高記憶體消耗的頁面可能會頻繁重新整理當前頁面,H5側也要做相應的適配操作。
- 檢測 webView.title 是否為空
並不是所有 H5 頁面白屏的時候都會呼叫上面的回撥函式,比如,最近遇到在一個高記憶體消耗的 H5 頁面上 present 系統相機,拍照完畢後返回原來頁面的時候出現白屏現象(拍照過程消耗了大量記憶體,導致記憶體緊張,WebContent Process 被系統掛起),但上面的回撥函式並沒有被呼叫。在 WKWebView
白屏的時候,另一種現象是 webView.titile
會被置空, 因此,可以在 viewWillAppear
的時候檢測 webView.title
是否為空來 reload
頁面。
2. WKWebView Cookie 問題
WKWebView
Cookie
問題在於 WKWebView
發起的請求不會自動帶上儲存於 NSHTTPCookieStorage
容器中的 Cookie
,而在 UIWebView
會自動帶上 Cookie
。
原因是:
WKWebView
擁有自己的私有儲存,不會將 Cookie
存入到標準的 Cookie
容器NSHTTPCookieStorage
中。
實踐發現 WKWebView
例項其實也會將 Cookie
儲存於 NSHTTPCookieStorage
中,但儲存時機有延遲,在 iOS 8
上,當頁面跳轉的時候,當前頁面的 Cookie
會寫入 NSHTTPCookieStorage
中,而在 iOS 10
上,JS 執行 document.cookie
或伺服器 set-cookie
注入的 Cookie
會很快同步到 NSHTTPCookieStorage
中,FireFox 工程師曾建議通過 reset WKProcessPool
來觸發 Cookie
同步到 NSHTTPCookieStorage
中,實踐發現不起作用,並可能會引發當前頁面 session cookie
丟失等問題。
解決辦法1:
WKWebView loadRequest
前,在 request header
中設定 Cookie
, 解決首個請求 Cookie
帶不上的問題;
解決辦法2:
通過 document.cookie
設定 Cookie
解決後續頁面(同域)Ajax``、iframe
請求的 Cookie
問題;(注意:document.cookie()
無法跨域設定 cookie
)。
3. WKWebView loadRequest 問題
在 WKWebView
上通過 loadRequest
發起的 post
請求 body
資料會丟失,同樣是由於程式間通訊效能問題, HTTPBody
欄位被丟棄。
4. WKWebView NSURLProtocol問題
WKWebView
在獨立於 app 程式之外的程式中執行網路請求,請求資料不經過主程式,因此,在 WKWebView
上直接使用 NSURLProtocol
無法攔截請求。
解決辦法:
由於 WKWebView
在獨立程式裡執行網路請求。一旦註冊 http(s) scheme
後,網路請求將從 Network Process
傳送到 App Process
,這樣 NSURLProtocol
才能攔截網路請求。在 webkit2
的設計裡使用 MessageQueue
進行程式之間的通訊,Network Process 會將請求 encode
成一個 Message
,然後通過 IPC 傳送給App Process
。出於效能的原因,encode
的時候 HTTPBody
和 HTTPBodyStream
這兩個欄位會被丟棄掉了。
5. WKWebView 頁面樣式問題
在 WKWebView
適配過程中,我們發現部分 H5 頁面元素位置向下偏移或被拉伸變形,追蹤後發現主要是 H5 頁面高度值異常導致。
解決辦法:
調整 WKWebView
佈局方式,避免調整webView.scrollView.contentInset
。實際上,即便在 UIWebView
上也不建議直接調整 webView.scrollView.contentInset
的值,這確實會帶來一些奇怪的問題。如果某些特殊情況下非得調整 contentInset
不可的話,可以通過下面方式讓H5頁面恢復正常顯示。
6. WKWebView 截圖問題
WKWebView 下通過 -[CALayer renderInContext:]實現截圖的方式失效,需要通過以下方式實現截圖功能:
@implementation UIView (ImageSnapshot)
- (UIImage*)imageSnapshot {
UIGraphicsBeginImageContextWithOptions(self.bounds.size,YES,self.contentScaleFactor);
[self drawViewHierarchyInRect:self.bounds afterScreenUpdates:YES];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
@end
複製程式碼
然而這種方式依然解決不了 webGL
頁面的截圖問題,截圖結果不是空白就是純黑圖片。
解決辦法:
無奈之下,我們只能約定一個JS介面,讓遊戲開發商實現該介面,具體是通過 canvas
getImageData()
方法取得圖片資料後返回 base64
格式的資料,客戶端在需要截圖的時候,呼叫這個JS介面獲取base64 String
並轉換成 UIImage
。
7. WKWebView crash問題
如果 WKWebView
退出的時候,JS剛好執行了 window.alert()
, alert 框可能彈不出來,completionHandler
最後沒有被執行,導致crash
;
另一種情況是在 WKWebView
一開啟,JS就執行 window.alert()
,這個時候由於 WKWebView
所在的 UIViewController
出現( push
或 present
)的動畫尚未結束,alert 框可能彈不出來,completionHandler
最後沒有被執行,導致 crash
。
8. 視訊自動播放
WKWebView
需要通過 WKWebViewConfiguration.mediaPlaybackRequiresUserAction
設定是否允許自動播放,但一定要在 WKWebView
初始化之前設定,在 WKWebView
初始化之後設定無效。
9. goBack API問題
WKWebView
上呼叫 -[WKWebView goBack]
, 回退到上一個頁面後不會觸發window.onload()
函式、不會執行JS。
10. 頁面滾動速率
WKWebView
需要通過 scrollView delegate
調整滾動速率:
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
}
複製程式碼
三、Crosswalk 是什麼,它有什麼作用?
參考網站: 《Crosswalk Github》 參考文章: 《Crosswalk入門》
Crosswalk 是一款開源的 web 引擎。目前 Crosswalk 正式支援的移動作業系統包括 Android 和 Tizen ,在 Android 4.0 及以上的系統中使用 Crosswalk 的 Web 應用程式在 HTML5 方面可以有一致的體驗,同時和系統的整合互動方面(比如啟動畫面、許可權管理、應用切換、社交分享等等)可以做到類似原生應用。
現在 Crosswalk 已經成為眾多知名 HTML5 平臺和應用的推薦引擎,包括 Google Mobile Chrome App 、 Intel XDK 、 Famo.us 和 Construct2 等等,未來的 Cordova 4.0 也計劃整合 Crosswalk 。
四、常見的 WebView 效能優化方案有哪一些?
0. 問題分析
首先需要了解,對於一個普通使用者來講,開啟一個 WebView 通常會經歷哪幾個階段,一般有這些:
-
互動無反饋;
-
到達新的頁面,頁面白屏;
-
頁面基本框架出現,但是沒有資料;頁面處於loading狀態;
-
出現所需的資料;
當 App 首次開啟時,預設是並不初始化瀏覽器核心的;只有當建立 WebView
例項的時候,才會建立 WebView
的基礎框架。
所以與瀏覽器不同,App 中開啟 WebView
的第一步並不是建立連線,而是啟動瀏覽器核心。
於是我們找到了“為什麼WebView總是很慢”的原因之一:
-
在瀏覽器中,我們輸入地址時(甚至在之前),瀏覽器就可以開始載入頁面。
-
而在客戶端中,客戶端需要先花費時間初始化
WebView
完成後,才開始載入。
而這段時間,由於WebView還不存在,所有後續的過程是完全阻塞的。因此由於這段過程發生在 native 的程式碼中,單純靠前端程式碼是無法優化的;大部分的方案都是前端和客戶端協作完成,以下是幾個業界採用過的方案:
1. 全域性 WebView
在客戶端剛啟動時,就初始化一個全域性的 WebView
待用,並隱藏,當使用者訪問了 WebView
時,直接使用這個 WebView
載入對應網頁,並展示。
這種方法可以比較有效的減少 WebView 在App中的首次開啟時間。當使用者訪問頁面時,不需要初始化 WebView 的時間。
當然這也帶來了一些問題,包括:
-
額外的記憶體消耗。
-
頁面間跳轉需要清空上一個頁面的痕跡,更容易記憶體洩露。
2. WebView 動態載入
參考文章:《WebView常用優化方案》
WebView
動態載入。就是不在 xml
中寫 WebView
,寫一個 layout
,然後把 WebView
add 進去。
WebView mWebView = new WebView(getApplicationgContext());
LinearLayout mll = findViewById(R.id.xxx);
mll.addView(mWebView);
protected void onDestroy() {
super.onDestroy();
mWebView.removeAllViews();
mWebView.destroy()
}
複製程式碼
這裡用的 getApplicationContext()
也是防止記憶體溢位,這種方法有一個問題。如果你需要在 WebView
中開啟連結或者你開啟的頁面帶有 flash,獲得你的 WebView
想彈出一個 dialog
,都會導致從 ApplicationContext
到 ActivityContext
的強制型別轉換錯誤,從而導致你應用崩潰。
這是因為在載入 flash 的時候,系統會首先把你的 WebView
作為父控制元件,然後在該控制元件上繪製 flash ,他想找一個 Activity
的 Context
來繪製他,但是你傳入的是 ApplicationContext
。然後就崩潰了。
3. 獨立的web程式,與主程式隔開
參考文章:《WebView常用優化方案》
這個方法被運用於類似 qq ,微信這樣的超級 app 中,這也是解決任何 WebView
記憶體問題屢試不爽的方法 對於封裝的 webactivity
,在manifest.xml
中設定。
<activity
android:name=".webview.WebViewActivity"
android:launchMode="singleTop"
android:process=":remote"
android:screenOrientation="unspecified"
/>
複製程式碼
然後在關閉 webactivity
時銷燬程式:
@Overrideprotected void onDestroy() {
super.onDestroy();
System.exit(0);
}
複製程式碼
關閉瀏覽器後便銷燬整個程式,這樣一般 95% 的情況下不會造成記憶體洩漏之類的問題,但這就涉及到 android 程式間通訊,比較不方便處理,優劣參半,也是可選的一個方案。
4. WebView 釋放
參考文章:《WebView常用優化方案》
public void destroy() {
if (mWebView != null) {
// 如果先呼叫destroy()方法,則會命中if (isDestroyed()) return;這一行程式碼,需要先onDetachedFromWindow(),再
// destory()
ViewParent parent = mWebView.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(mWebView);
}
mWebView.stopLoading();
// 退出時呼叫此方法,移除繫結的服務,否則某些特定系統會報錯
mWebView.getSettings().setJavaScriptEnabled(false);
mWebView.clearHistory();
mWebView.clearView();
mWebView.removeAllViews();
try {
mWebView.destroy();
} catch (Throwable ex) {
}
}
}
複製程式碼
五、在 Android 平臺下如何除錯 WebView?
1. 在 Chrome 瀏覽器上除錯
參考文章:《Android除錯webview》
1.1 條件:
-
在 Android 裝置或模擬器執行 Android4.4 或更高版本,Android 裝置上啟用 USB除錯模式。
-
Chrome 30 或更高版本。更強大的 WebView 介面除錯功能需要 Chrome31 或更高版本。
-
Android 應用程式中的 WebView 配置為可除錯模式。
1.2 Android 程式碼中配置 WebView 為可除錯:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
複製程式碼
注意 web 調測不受 app manifest
檔案中 debuggable
標記狀態的影響,如果希望僅 debuggable
為 true
時才能使用 web 調測,那麼執行時檢測此標記,如下:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if ( 0 != ( getApplcationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE ) ) {
WebView.setWebContentsDebuggingEnabled(true);
}
}
複製程式碼
1.3 手機開啟 USB 除錯選項,並用 USB 連線電腦:
開啟 Android 手機的開發者選項,一般在 系統設定 - Android版本 進行多次點選會觸發開啟開發者選項,然後進入開發者選項頁面,開啟USB除錯。
為了避免每次除錯時看到此警告,勾選“總是允許從這臺計算機”,並單擊“確定”。
1.4 在 Chrome 中啟用設定“USB web debugging”(不會影響WebView):
在 Chrome 上訪問 chrome://inspect/#devices
或 about:inspect
訪問已啟用除錯的 WebView 列表,需要翻牆。
然後在 WebView 列表中選擇你要除錯的頁面,點選“ Inspect
”選項,跟除錯 PC 網頁一樣,使用 Chrome 控制檯進行除錯。
1.5 小技巧:
(1)訪問 chrome://inspect/#devices
如果 chrome 沒有檢測到 Remote Target
中的頁面,可能需要安裝一下 Chrome 的 ADB 外掛,也可以在 Chrome 翻牆訪問 https://chrome-devtools-frontend.appspot.com
;
(2)對於騰訊系的 APP,預設採用 X5核心 ,我們可以在 APP 內部開啟 https://debugx5.qq.com/
來使用它的Vconsole除錯工具進行除錯。
2. 使用 DebugGap 除錯
參考文章:《Android下的webview除錯》
2.1 Windows 下載 DebugGap 並配置:
在電腦上面下載 Windows 版本的 DebugGap
軟體包(下載連結:DebugGap),下載完成後解壓下來。
安裝完成後,執行 DebugGap
,開始配置:
- 通常情況下,
DebugGap
可以自動獲取IP,並設定預設的埠,如果沒有,你可以手動設定; - 點選“連線”按鈕啟動各種客戶端的偵聽器;
2.2 在客戶端上配置:
在除錯專案中要進行測試的 HTML 介面中引入 debuggap.js
。
<script src="debuggap.js" type="text/javascript"></script>
複製程式碼
當除錯專案的載入時,您的應用程式將會有一個藍色的地方,點選會出現一個四葉三葉草的東西,點選“配置”,顯示配置頁面。輸入與遠端 DebugGap 上的主機和埠相同的主機和埠,例如 192.168.1.4:11111
,然後點選“連線”按鈕。
1.4電腦端遠端 DebugGap 將檢測即將到來的客戶端,開發人員可以單擊每個客戶端進行除錯。
六、在 iOS 平臺下如何除錯 WebView?
一般我們通過 Mac 的 Safari瀏覽器 來除錯,但是要注意兩點:
-
如果除錯的是 APP 中 WebView 的頁面,則需要這個 APP 的包支援除錯,如果不能除錯,需要讓 iOS 開發人員重簽名 APP(可能需要將我們 iOS 裝置的 ID 寫入到可信任裝置列表中,然後使用 iTunes 安裝客戶端提供的測試包即可)。
-
如果除錯的是 H5 頁面,可以直接在手機的 Safari瀏覽器 開啟直接除錯。
下面開始說說在 Mac 上如何除錯:
1. 開啟 Safari 開發選單
先將 iPhone 連線到 Mac,在 Mac 的 Safari 偏好設定中,開啟開發選單。具體步驟為:Safari -> 偏好設定… -> 高階 -> 勾選在選單欄顯示“開發”選單。
2. iPhone 開啟 Web檢查器
具體步驟為:設定 -> Safari -> 高階 -> Web 檢查器。
3. 除錯 APP 內的 WebView
在 Safari-> 開發中,看到自己的裝置以及 WebView 中網頁,點選後即可開啟對應頁面的 Inspector
,可以用來進行斷點除錯。
七、在內嵌版除錯過程中,Fiddler 或 Charles 能起到什麼作用?
這兩者都是強大的抓包工具,原理是以web代理伺服器的形式進行工作的,使用的代理地址是:127.0.0.1
,埠預設為8888
,我們也可以通過設定進行修改。
代理就是在客戶端和伺服器之間設定一道關卡,客戶端先將請求資料傳送出去後,代理伺服器會將資料包進行攔截,代理伺服器再冒充客戶端傳送資料到伺服器;同理,伺服器將響應資料返回,代理伺服器也會將資料攔截,再返回給客戶端。
Fiddler 或 Charles 的主要作用有:
- 可以代理請求,用來檢視頁面傳送的請求和接收的響應;
- 可以修改請求的響應,用來模擬自己想要的資料;
- 可以模擬網路請求的速度;
- 可以代理伺服器的靜態資源請求,指向本地檔案,省去頻繁釋出 H5 包,達到快速除錯目的;
補充連結:《Fiddler工具使用介紹一》
八、除錯企業微信、微信和釘釘版時,可以使用哪些工具?
1. 除錯企業微信、微信
- 微信開發者工具,可以用來除錯頁面基本功能;
- 企業微信介面除錯工具,可以用來除錯企業微信的介面;
2. 除錯釘釘
- 釘釘Android開發版,用來除錯Android上的釘釘應用;
3. 通用
- Fiddler 或 Charles,可以攔截介面替換檔案,來除錯應用;
九、常見的除錯技巧有哪些?
1. Chrome 控制檯除錯
1.1 Source 皮膚斷點除錯 JS
從左到右,各個圖示表示的功能分別為:
Pause/Resume script execution
:暫停/恢復指令碼執行(程式執行到下一斷點停止)。Step over next function call
:執行到下一步的函式呼叫(跳到下一行)。Step into next function call
:進入當前函式。Step out of current function
:跳出當前執行函式。Deactive/Active all breakpoints
:關閉/開啟所有斷點(不會取消)。Pause on exceptions
:異常情況自動斷點設定。
1.2 Element 皮膚除錯 DOM:
右擊元素,選擇 break on
選項:
Subtree modifications
選項,是指當節點內部子節點變化時斷點;
Attributes modifications
選項,是指當節點屬性發生變化時斷點;
node removal
選項,是指當節點被移除時斷點;
2. console 除錯
參考文章:《Console除錯常用用法》
2.1 顯示資訊的命令:
console.log("normal"); // 用於輸出普通訊息
console.info("information"); // 用於輸出提示性資訊
console.error("error"); // 用於輸出錯誤資訊
console.warn("warn"); // 用於輸出警示資訊
console.clear(); // 清空控制檯資訊
複製程式碼
2.2 計時功能:
console.time()
和 console.timeEnd()
:
console.time("控制檯計時器");
for(var i = 0; i < 10000; i++){
for(var j = 0; j < 10000; j++){}
}
console.timeEnd("控制檯計時器")
複製程式碼
2.3 資訊分組:
console.group()
和 console.groupEnd()
:
console.group("第一組資訊");
console.log("第一組第一條:我的部落格");
console.log("第一組第二條:CSDN");
console.groupEnd();
console.group("第二組資訊");
console.log("第二組第一條:程式愛好者QQ群");
console.log("第二組第二條:歡迎你加入");
console.groupEnd();
複製程式碼
2.4 將物件以樹狀結構展現:
console.dir()
可以顯示一個物件所有的屬性和方法:
var info = {
name : "Alan",
age : "27",
grilFriend : "nothing",
getName : function(){
return this.name;
}
}
console.dir(info);
複製程式碼
2.5 顯示某個節點的內容:
console.dirxml()
用來顯示網頁的某個節點( node
) 所包含的 html/xml
程式碼:
var node = document.getElementById("info");
node.innerHTML += "<p>追加的元素顯示嗎</p>";
console.dirxml(node);
複製程式碼
2.5 統計程式碼被執行的次數:
使用 console.count()
:
function myFunction(){
console.count("myFunction 被執行的次數");
}
myFunction(); //myFunction 被執行的次數: 1
myFunction(); //myFunction 被執行的次數: 2
myFunction(); //myFunction 被執行的次數: 3
複製程式碼
2.6 輸出表格:
console.table(mytable);
複製程式碼
3. 除錯各種頁面尺寸
雖然把各種各樣的手機都擺在桌子上看起來很酷,但卻很不現實。但是,瀏覽器內卻提供了你所需要的一切。進入檢查皮膚點選“切換裝置模式”按鈕。這樣,就可以在視窗內調整視窗的大小。
4. debugger 斷點
具體的說就是通過在程式碼中新增" debugger;
"語句,當程式碼執行到該語句的時候就會自動斷點。
結語
對於初入混合應用開發的小夥伴,還有經常需要除錯混合應用的小夥伴,相信會有幫助?
大家加油~
關於我
本文首發在 pingan8787個人部落格,如需轉載請保留個人介紹。
Author | 王平安 |
---|---|
pingan8787@qq.com | |
博 客 | www.pingan8787.com |
微 信 | pingan8787 |
每日文章推薦 | github.com/pingan8787/… |
ES小冊 | js.pingan8787.com |