Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
目前很多公司的 App 就只使用一個 WebView 作為整體框架, App 中的所有內(nèi)容全部使用 HTML5 進(jìn)行展示,這樣只需要寫一次 HTML5 代碼,就可以在 Android 和 iOS 平臺上運(yùn)行,這就是所謂的「 跨平臺 」。隨著 HTML5 的普及,很多 App 都會內(nèi)嵌 WebView 來加載 HTML5 頁面,即 Native 和 HTML5 共存,這就是當(dāng)下最流行的「 混合開發(fā) 」。HTML5 除了開發(fā)簡單,還有一個優(yōu)勢就是迭代方便, 只需要修改服務(wù)端的 HTML5 頁面,App 會同步更新,無論是做活動推廣 App 還是及時修復(fù) Bug 都帶來的極大的便利。不過 HTML5 劣勢也很明顯,受制于國內(nèi)的網(wǎng)速限制。 雖然國內(nèi)已經(jīng)普及了 4g 網(wǎng)絡(luò),但是網(wǎng)速還是不盡如人意。HTML5 加載受限于網(wǎng)絡(luò),沒有原生控件流暢,用戶體驗(yàn)相對較差, 所以目前完全使用 HTML5 開發(fā) App 并沒有成為主流。我所在的項(xiàng)目組也使用HTML5開發(fā)比較頻繁,這個時候了解WebView使用就變得尤為重要了,而WebView的坑也是非常的多,我在開發(fā)中就遇到了許多莫名其妙的問題。 所以準(zhǔn)備寫幾篇文章總結(jié)一下,文章按照類來進(jìn)行分類。WebView開發(fā)常用的類是 WebView , WebSettings , WebViewClient , WebChromeClient。
這里是WebView開發(fā)第一篇: WebView 的使用介紹;
A View that displays web pages. This class is the basis upon which you can roll your own web browser or simply display some online content within your Activity. It uses the WebKit rendering engine to display web pages and includes methods to navigate forward and backward through a history, zoom in and out, perform text searches and more.
總結(jié)起來就是:WebView是一個基于WebKit引擎的,并且可以在activity展現(xiàn)Web頁面的控件。
<uses-permission android:name="android.permission.INTERNET" />
3.2 添加布局頁面
這里介紹另外一種方式,通過addView添加進(jìn)去
1.在布局頁面添加布局
<FrameLayout
android:id="@+id/webViewWrap"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible" />
2.實(shí)體化webview并添加到布局中
webViewWrap = findViewById(R.id.webViewWrap); //上面的FrameLayout
webView = new WebView(MyApplication.getAppContext()); //使用應(yīng)用上下文,可以防止內(nèi)存泄露,如果傳入activity的上下文,需要將webview remove掉。下面有講解。
webViewWrap.addView(webView);
3.加載頁面
//方式1. 加載一個網(wǎng)頁:
webView.loadUrl("http://www.google.com/");
//方式2:加載apk包中的html頁面
webView.loadUrl("file:///android_asset/test.html");
//方式3:加載手機(jī)本地的html頁面
webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");
// 方式4: 加載 HTML 頁面的一小段內(nèi)容
WebView.loadData(String data, String mimeType, String encoding)
// 參數(shù)說明:
// 參數(shù)1:一段HTML代碼
// 參數(shù)2:展示內(nèi)容的類型
// 參數(shù)3:字節(jié)碼
到這一步我們就能夠展示一個WebView頁面了,不過是通過外部瀏覽器打開的。但是還有許多優(yōu)化的地方。
//激活WebView為活躍狀態(tài),能正常執(zhí)行網(wǎng)頁的響應(yīng),可以在activity 的回調(diào)方法中調(diào)用
webView.onResume() ;
//當(dāng)頁面被失去焦點(diǎn)被切換到后臺不可見狀態(tài),需要執(zhí)行onPause,可以在activity 的回調(diào)方法中調(diào)用
//通過onPause動作通知內(nèi)核暫停所有的動作,比如DOM的解析、plugin的執(zhí)行、JavaScript執(zhí)行。
webView.onPause();
//當(dāng)應(yīng)用程序(存在webview)被切換到后臺時,這個方法不僅僅針對當(dāng)前的webview而是全局的全應(yīng)用程序的webview
//它會暫停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
webView.pauseTimers()
//恢復(fù)pauseTimers狀態(tài)
webView.resumeTimers();
//銷毀Webview
//在關(guān)閉了Activity時,如果Webview的音樂或視頻,還在播放。就必須銷毀Webview
//但是注意:webview調(diào)用destory時,webview仍綁定在Activity上
//這是由于自定義webview構(gòu)建時傳入了該Activity的context對象
//因此需要先從父容器中移除webview,然后再銷毀webview:
ViewParent parent = mWebView.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(mWebView);// 防止內(nèi)存泄露
}
webView.destroy();
//是否可以后退
Webview.canGoBack()
//后退網(wǎng)頁
Webview.goBack()
//是否可以前進(jìn)
Webview.canGoForward()
//前進(jìn)網(wǎng)頁
Webview.goForward()
//以當(dāng)前的index為起始點(diǎn)前進(jìn)或者后退到歷史記錄中指定的steps
//如果steps為負(fù)數(shù)則為后退,正數(shù)則為前進(jìn)
Webview.goBackOrForward(intsteps)
常見用法:Back鍵控制網(wǎng)頁后退
問題:在不做任何處理前提下 ,瀏覽網(wǎng)頁時點(diǎn)擊系統(tǒng)的“Back”鍵,整個 Browser 會調(diào)用 finish()而結(jié)束自身
目標(biāo):點(diǎn)擊返回后,是網(wǎng)頁回退而不是推出瀏覽器
解決方案:在當(dāng)前Activity中處理并消費(fèi)掉該 Back 事件
//清除網(wǎng)頁訪問留下的緩存
//由于內(nèi)核緩存是全局的因此這個方法不僅僅針對webview而是針對整個應(yīng)用程序.
Webview.clearCache(true);
//清除當(dāng)前webview訪問的歷史記錄
//只會webview訪問歷史記錄里的所有記錄除了當(dāng)前訪問記錄
Webview.clearHistory();
//這個api僅僅清除自動完成填充的表單數(shù)據(jù),并不會清除WebView存儲到本地的數(shù)據(jù)
Webview.clearFormData();
WebView 的使用介紹就先到這里了,下一篇將會講解WebSettings的使用。
年做了大量的 HTML5 項(xiàng)目,遇到了很多坑。在這個過程中學(xué)到了一些之前不具備的知識,所以這篇文章就簡單分享一下這方面的話題。
傳統(tǒng)的MPA
首先,說一個比較古老的東西,叫做 MPA。
MPA 的全稱是 Multi-page Application,意思是整個應(yīng)用(站點(diǎn))由多個完整的 html 構(gòu)成。用戶在頁面 1 點(diǎn)擊跳轉(zhuǎn),需要向服務(wù)端請求頁面 2,請求成功后渲染。而用戶返回時,相當(dāng)于是點(diǎn)擊了瀏覽器的返回,頁面退回到之前的歷史記錄,并重新加載出來。
在這樣的模式下,頁面間切換慢、不流暢的問題比較突出,尤其是在移動端。
同時,它還產(chǎn)生了幾個小問題:
SPA
隨著對移動端體驗(yàn)需求的提高以及技術(shù)的進(jìn)步,另一種模式 SPA(Single-page Application)逐漸成為主流。
SPA 簡單來說,就是原來在 MPA 中的多個 html,現(xiàn)在被放在了一個 html 中,并被分成若干個片段。跳轉(zhuǎn)、返回的本質(zhì)變成了分段的「隱藏」與「顯示」。跳轉(zhuǎn)不需要反復(fù)對服務(wù)端進(jìn)行請求,從而使得頁面與頁面之間切換更加快速流暢。
在這樣的機(jī)制下,跳轉(zhuǎn)與返回完全由代碼控制,所以可以通過代碼定義頁面轉(zhuǎn)場的效果、返回。
在設(shè)計轉(zhuǎn)場動畫時,我們需要留意的是導(dǎo)航欄是 Native 的還是 HTML5 的。如果導(dǎo)航欄是 Native 的,那 HTML5 頁面不包括導(dǎo)航欄,它相當(dāng)于是網(wǎng)頁外的元素,不在轉(zhuǎn)場效果的設(shè)計范圍內(nèi)。
WebView
說 HTML5 的跳轉(zhuǎn),就不得不說 WebView。簡單來說,WebView 是在 App 中用于顯示 web 內(nèi)容的容器。上文提到的 MPA 和 SPA,都裝在了這個叫做 WebView 的容器中。
用戶點(diǎn)擊頁面中的元素進(jìn)行跳轉(zhuǎn),除了前面的兩種方式外,還有第三種:新打開 WebView 的方式。在這樣的方式下,跳轉(zhuǎn)的本質(zhì)是 HTML5「告訴」Native,由 Native 執(zhí)行打開新 WebView,并在新 WebView 中加載頁面。
因?yàn)?Native 的機(jī)制,打開新 WebView 的同時,之前的 WebView 會被自然、完整地保留。所以這時,之前的幾個問題就變?yōu)椋?/p>
不過需要注意的地方是,打開新 WebView 是一個資源消耗比較大的操作。如果我們在設(shè)計一個流程時,需要比較多的連續(xù)使用這種方式,需要和研發(fā)同學(xué)進(jìn)行充分的溝通。
比較特殊的Replace
前述的三種跳轉(zhuǎn),都會產(chǎn)生歷史記錄。MPA、SPA 的歷史記錄是在 HTML5 中產(chǎn)生,新開 WebView 中的記錄是在 Native 中產(chǎn)生。
在 MPA 或 SPA 中,如果跳轉(zhuǎn)時使用 Replace 方法,它會用新頁面替換之前的頁面,歷史記錄中沒有之前頁面的記錄。
這是一種特殊的跳轉(zhuǎn)方式,在設(shè)計一些不可逆的流程時可考慮使用。
多頁面回退
了解了上述的幾種機(jī)制后,我們來看一個小的應(yīng)用場景──多頁面回退。
我們在實(shí)際業(yè)務(wù)中,經(jīng)常會有這樣的需求。假設(shè)我們有 1、2、3 三個頁組成的一個流程,在頁面 3 上有個「完成」按鈕點(diǎn)擊回到頁面 1。在不同的交互模式下,實(shí)現(xiàn)這樣的跳轉(zhuǎn)有著不同的機(jī)制。
1. SPA模式下的正常跳轉(zhuǎn)
這種模式是 3 個頁面都在一個 WebView 中。點(diǎn)擊頁面 3 中的「完成」按鈕,回退 -2 ,即回退 2 步歷史記錄,到頁面 1。
2. 新打開WebView
打開新 WebView 又分三種方式。
如果我們把 3 個頁面,拆分到 2 個 WebView 中,如下圖,點(diǎn)擊完成按鈕,即關(guān)閉自身所在的 WebView。
同樣是打開新的 WebView,如果我們按如下圖的方法拆分會稍微復(fù)雜。這時點(diǎn)擊完成按鈕,首先關(guān)閉自身所在的 WebView,當(dāng)頁面 2「意識」到自己重新被展現(xiàn)時,自動退回 1 步到頁面 1。
每次打開新的 WebView,這時點(diǎn)擊完成,回退的本質(zhì)是 HTML5「告訴」Native 關(guān)閉多個 WebView。需要特別注意的是,HTML5 中實(shí)現(xiàn)這種方式不是天然具備的,它需要 Native 具有一次關(guān)閉多個 WebView 的能力。所以我們在設(shè)計方案時,需要了解清楚自家的 Native 是否有這樣的能力。
總結(jié)
以上,簡單說了幾種 HTML5 的跳轉(zhuǎn)方式。這些跳轉(zhuǎn)方式,沒有絕對的對與錯,我們在設(shè)計方案時,需要根據(jù)實(shí)際的業(yè)務(wù)需求與技術(shù)的限制,來整體考慮解決方案。
根據(jù)個人經(jīng)驗(yàn),也有幾點(diǎn)小帖士分享給大家:
根據(jù)Google公布的Android 各個系統(tǒng)版本市場占有率(Google Android dashboards), Android 4.0及其以上系統(tǒng)將近90%左右,發(fā)展趨勢必將是未來市面上幾乎是Android 4.0以上系統(tǒng)。本文主要關(guān)注Android 4.0及以上系統(tǒng)WebView的實(shí)現(xiàn),從Android WebView實(shí)現(xiàn)的Framework層大致可以分為三段Android 4.0系列,Android 4.1---4.3系列,Android 4.4及其以上系列。
WebView差異
WebView是Android系統(tǒng)提供能顯示網(wǎng)頁的系統(tǒng)控件,它是一個特殊的View,同時它也是一個ViewGroup可以有很多其他子View。在Android 4.4以下(不包含4.4)系統(tǒng)WebView底層實(shí)現(xiàn)是采用WebKit(http://www.webkit.org/)內(nèi)核,而在Android 4.4及其以上Google 采用了chromium(http://www.chromium.org/)作為系統(tǒng)WebView的底層內(nèi)核支持。在這一變化中Android 提供的WebView相關(guān)API并沒有發(fā)生大變化,在4.4上也兼容低版本的API并且引進(jìn)了少部分API。這里簡單介紹下基于Chromium 的Webview和基于Webkit webview的差異,基于Chromium Webview提供更廣的HTML5,CSS3,Javascript支持,在目前最新Android 系統(tǒng)版本5.0上基于chromium 37,Webview提供絕大多數(shù)的HTML5特性支持。Webkit JavaScript引起采用WebCore Javascript 在Android 4.4上換成了V8能直接提升JavaScript性能。另外Chromium 支持遠(yuǎn)程調(diào)試(Chrome DevTools)。
WebKit for WebView VS Chromium for WebView性能比對(測試環(huán)境 小米2. CM Browser. Android 4.1.1 VS 4.4.3)
Webkit for Webview | Chromium for Webview | 備注 | |
HTML5 | 278 | 434 | http://html5test.com/ |
遠(yuǎn)程調(diào)試 | 不支持 | 支持 | Android 4.4及以上支持 |
內(nèi)存占用 | 小 | 大 | 相差20-30M左右 |
WebAudio | 不支持 | 支持 | Android 5.0及以上支持 |
WebGL | 不支持 | 支持 | Android 5.0及以上支持 |
WebRTC | 不支持 | 支持 | Android 5.0及以上支持 |
Android 4.0 WebView結(jié)構(gòu)
Android WebView API層主要提供給我們應(yīng)用程序的接口,為了兼容向下版本Android在高版本中也是對這一層的API進(jìn)行支持,因此如果底層發(fā)生變化,這些API接口層也不會發(fā)生太大變化。Android 平臺不僅提供應(yīng)用層編程接口也提供native層編程。下面介紹上圖中的三個部分:
1)Android Framework:Android WebView是個特殊控件實(shí)現(xiàn)的支持需要Framework的代碼主要在./frameworks/base/core/java/android/webkit目錄下,在Android 4.0實(shí)現(xiàn)主要是在WebViewCore.java,BrowserFrame.java等文件。
2) Android JNI:需要有Native代碼支持,因此需要有JNI層實(shí)現(xiàn),Android WebView 4.0的JNI層實(shí)現(xiàn)WebView相關(guān)代碼在./external/webkit/Source/WebKit/android/jni/目錄下,這一層起到承上啟下的作用,鏈接Framework層以及WebKit層的橋梁,比如相關(guān)的一些實(shí)現(xiàn)在WebviewCore.cpp,WebCoreFrameBridge.cpp等。
3) WebKit: WebKit內(nèi)核,其核心主要是解析W3C標(biāo)準(zhǔn)以及渲染排版網(wǎng)頁,他是一個跨平臺的內(nèi)核引擎,那么需要支持各個平臺,需要我們的平臺實(shí)現(xiàn)層,在Android 4.0系統(tǒng)這一部分相關(guān)代碼主要在./external/webkit/Source/WebKit/android/WebCoreSupport/目錄下,比如FrameLoaderClientAndroid.cpp,ChromeClientAndroid.cpp,這一層負(fù)責(zé)WebCore與系統(tǒng)平臺的橋接,具體在不同平臺會有不同的實(shí)現(xiàn)。實(shí)現(xiàn)網(wǎng)頁的解析排版及渲染由WebCore來實(shí)現(xiàn)在Android 4.0源碼當(dāng)中代碼位于./external/webkit/Source/WebCore/下,下面有WebCore實(shí)現(xiàn)的各個模塊功能支持的相關(guān)代碼,比如頁面視圖部分在page目錄的chrome.cpp,比如加載頁面需要的資源的loader中得FrameLoader.cpp等,這里不在繼續(xù)深入詳解,有興趣的朋友可以下載Android 4.0源碼閱讀。
Android 4.1--4.3 WebView結(jié)構(gòu)
Android 4.1--4.3版本W(wǎng)ebView內(nèi)核實(shí)現(xiàn)還是基于WebKit,但在WebView的Framework層發(fā)生了變化,引入了工廠模式,目地是為了將內(nèi)核與上層API接口分離開來,分離的意義不僅僅是抽象接口,更重要的是將來能替換內(nèi)核部分的實(shí)現(xiàn)。 在4.1--4.3這一系列版本native結(jié)構(gòu)基本與4.0版本相同,下圖呈現(xiàn)新的變化:
Android 4.0--4.3 渲染
盡管之前4.0,與4.1--4.3是在不同的結(jié)構(gòu)系列,其兩者之間的差異主要是集中的Framework上的變化,這種變化更多體現(xiàn)在Framework層結(jié)構(gòu)上的變化,WebKit內(nèi)核極其在Android上的表現(xiàn)機(jī)制并沒有發(fā)生很大變化,他們的渲染機(jī)制是相同的。下面介紹Android 4.0--4.3的渲染機(jī)制:
在Android 4.0上已經(jīng)默認(rèn)開啟硬件加速,因此WebView的渲染默認(rèn)是基于硬件渲染的,通過本人分析其在WebView被隱藏的那一幀是采用軟件渲染,目的是減少硬件占用,讓其他UI能及時的響應(yīng)。在硬件渲染情況下WebView通過onDraw方法傳遞Canvas 并將其轉(zhuǎn)行為HardwareCanvas ,并生成native的 DrawGLFunction指針,通知native做渲染。在軟件模式下,WebView通過傳遞的Canvas 通知內(nèi)核webkitDraw將內(nèi)核的一幀生成picture傳輸?shù)紺anvas中,執(zhí)行Canvas draw bitmap。
Android 4.4 WebView結(jié)構(gòu)
在Android 4.4系統(tǒng)上 Google已經(jīng)將系統(tǒng)默認(rèn)的Webkit內(nèi)核替換成自己的開源項(xiàng)目chromium,通過之前的版本分析,我們可以看到Android 對WebView的Framework 結(jié)構(gòu)進(jìn)行調(diào)整使其更抽象,更重要的目的還是集成自己的開源chromium。下面我們來看看WebView的結(jié)構(gòu)發(fā)生了什么樣的變化:
目錄:
./frameworks/base/core/java/android/webkit
./frameworks/webview/chromium/java/com/android/webview/chromium
./external/chromium_org/android_webview
./external/chromium_org/content
為了將chromium項(xiàng)目集成到Android 中,chromium項(xiàng)目抽象出Android webview這一層,之前的接口抽離這時候已經(jīng)變得很明顯,Android Webview基于chromium content API這一層,第三方瀏覽器廠商也可以采用這種方式,目前所了解的廠商有Opera使用這種方式。Android 4.4WebView的渲染核心目前也沒有發(fā)生太大變化,還是基于WebView的Canvas,將Chromium composit 結(jié)構(gòu)繪制到WebView Canvas上。接入chromium內(nèi)核,WebView瀏覽性能大幅度提升,但是和chrome for Android還是有些不同,主要體現(xiàn)在一下幾點(diǎn):
1. chrome瀏覽器是多進(jìn)程架構(gòu),Chromium for Android Webview 是單進(jìn)程架構(gòu)。
2. chrome瀏覽器 內(nèi)存占用比 Android WebView大的多。
3. chrome支持更多的HTML5 feature。
Android WebView展望:
Chromium項(xiàng)目編譯"android_webview_apk“ 目前實(shí)現(xiàn)是基于Android SurfaceView,其渲染性能高于Android WebView的Canvas,歷史遺留問題以及Android 系統(tǒng)WebView的作用特點(diǎn),這一塊隨著Chromium 和 Android項(xiàng)目的整合,相信值得大家期待將來的Android WebView 的渲染性能會再次大幅提升。
本博客會持續(xù)更新Android WebView后續(xù)版本的變化,敬請關(guān)注 謝謝!
Android 5.0 Lollipop WebView
Lollipop版本中WebView的內(nèi)核實(shí)現(xiàn)采用Chromium 37版本,這個版本帶來更多的安全性和穩(wěn)定性。這個版本解決Android 4.4版本網(wǎng)頁當(dāng)中請求訪問打開本地文件選擇器問題,引入新的回調(diào)接口,onShowFileChooser方法,需要此功能的可以在5.0上接上這個回調(diào)接口,并實(shí)現(xiàn)功能。另外這個版本提供安全許可給用戶選擇,當(dāng)網(wǎng)頁需要訪問特殊資源時,會通知我們的應(yīng)用程序,請求允許,回調(diào)接口為onPermissionRequest。之前我們也提到這個版本使得WebView默認(rèn)支持WebAudio,WebGL,WebRTC等標(biāo)準(zhǔn)。
另外Google Android 還將webview做為一個能動態(tài)更新的app,能不更新Android版本情況下,更新WebView內(nèi)核。Android 5.0 Webview默認(rèn)提供減少內(nèi)存占用支持,并且智能選擇需要繪制的HTML document部門來提供性能。 當(dāng)然開發(fā)者可以在自己應(yīng)用程序需要時關(guān)閉這個選項(xiàng)(enableSlowWholeDocumentDraw)。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。