① 關於android WebView的那些事
[TOC]
Webkit是一個開源瀏覽器項目,其中,對Android開發者來說,或多或少的都有些接觸。 在應用層來看,最經常使用無非這么幾個類:WebView(Android中最為復雜,也是最為簡單的一個View,繼承自AbsoluteLayout),WebViewClient、WebChromeClient(作為回調控制類)、WebSettings(進行設置項的配置)等;Webkit內部包含了網路請求、頁面渲染、Js引擎等等。在Android4.4之前的版本中,系統使用的是Webkit內核,其後,切換到Google的Chromium內核。本文主要介紹的是在Android中,如何使用Webkit進行H5頁面的展現,以及常見問題的分析手段。
下面的內容抄自網路 & 亂七八糟的地方,簡單了解一下。
<b><i>前面都是吹牛逼的信息,如何使用Webkit來更好的搬磚? 且聽如下分解</i></b>
XML布局中丟一個 <WebView> 標簽,然後再 Activity 或者 Fragment 中 findViewById ,進而 loadUrl ,一般也沒人這么簡單的用,除非寫Demo。很簡單,它就是一個Layout,提供了一個調用載入頁面的介面,不寫範例了,能看到這篇文章的都看過Google的API說明。
主要涉及到WebView和WebSettings兩個類。
例如:
其實就是WebView的父類ViewGroup和View的方法,不多說了。不過需要注意的是,不是所有的View或ViewGroup的方法對WebView都生效。
列舉幾類常用的,幾乎所有App的 WebView 都會設置的屬性:
</br>
如何處理頁面跳轉以及特殊 Scheme
這個回調可以說是最容易出問題的一個回調,表示什麼? 字面意思,讓你重寫這個URL 的loading,比如點擊html打電話的一個 <a href=「tel:110」> 標簽,作為一個有節操、有責任心的瀏覽器,你需要處理 H5常用的幾個Scheme :
除此之外,還有各個應用自定義的scheme ,舉個例子,支付寶的支付Scheme : alipay: 。 這里的返回值,就代表你有沒有能力處理這個url,沒有的話Webkit就默認處理了。
需要注意的是,這個回調的觸發的絕大多數情況是點擊頁面的 <a href="xxxx"> a標簽,在Android中 loadUrl("http://www..com") ,是不會回調的,為什麼不會回調,各位自行理解吧。
超鏈接 <a> 標簽怎麼寫: 點我
特別說下窗口常見的兩種打開方式:
針對單頁模式的WebView框架(所有的html窗口均使用同一個WebView實例),不需要關注target的。
如果作為一個成熟的瀏覽器框架的話,是需要支持Html、javaScript使用新窗口打開頁面,需要實現如下回調:
還有一個相關設置項: WebSettings.
此時,系統將不會再回調 shouldOverrideUrlLoading 。新窗口邏輯的具體實現機制,可以參考系統browser實現邏輯。
<b> 這里有個坑 </b>
Android 4.4版本 ,如果實現了onCreateWindow,也就是說頁面 <a> 標簽是這么寫的: <a href="http://www..com" target="_blank"> ,點擊此鏈接打開的新WebView窗口,此窗口中的url點擊,是不會觸發 shouldOverrideUrlLoading 。 這是剛替換成Chrominum內核出的一個bug。本人並沒在新版本上驗證是否已經修復。
另外,根據不同的Rom,底層實現是不一樣的,有的ROM會幫你處理各種調起scheme,也就是startActivity,有的ROM點一個url,就會拋一個intent出來,讓用戶選擇系統瀏覽器進行載入。
系統默認,提供了一個介面:
有什麼安全隱患呢?
戳這里
如果不知道Js怎麼寫, 請戳我
用PC的截圖意思一下,看出區別了吧。 這里確定、取消點擊以後就得調用 JsResult、JsPromptResult 的 confirm或者cancel。
因為安全問題,大一些的App Native與Js通信都不再用 WebView.addJavascriptInterface(Object) 了,都改用JsPrompt,因為JsPrompt中有message、有JsPromptResult可以返回給Js一些信息,所以橋選中了JsPrompt,另一個備選方案是JsConsole。
大體有這么幾種方式進行傳遞
具體方案實現時,多方面考慮使用何種方式。
還有一個比較牛逼的
系統源碼中均有方法注釋,怎麼用自己看吧。
那麼問題來了
查了下,只有這兩個相關的:
WebBackForwardList BackForwardList()
void clearHistory()
系統提供的關於歷史記錄的操作並不多,因為,不支持單條刪除啊,啊啊啊!
WebViewClient中,還有一個相關callback,當系統更新歷史記錄時回調:
void doUpdateVisitedHistory(WebView view, String url, boolean isReload)
<b>相關問題分析法:歷史棧回退錯誤的定位</b>
絕大多數回退錯誤是由於介面調用、回調中邏輯執行時序錯誤。
定位方法:利用 BackForwardList , doUpdateVisitedHistory 兩個介面在 loadUrl、onPageStart、onPageFinish 以及邏輯相關的地方調用,打log,查看歷史棧,這里注意下由於loarl是非同步的,需要考慮是否加延遲等等保證調用時機的准確。
本人曾經遇到一個問題:在WebChromeClient中的 JsPrompt回調中,直接進行WebView.goBack操作,結果發現WebView確實回退到上一個頁面,但是BackFowardList當前頁面的index未更新的問題,具體見另一個篇blog。
網上有很多關於WebView內存泄露的討論,據傳,老版本的WebView在展示大量圖片的時候,即使 WebView.destory() WebView=null ,也不會銷毀。
在新版本上,實際測試結果:compileSDKVersion 23 不會泄露。
一般,我們如何銷毀WebView比較保險?
這個問題好大。。。
暫時不介紹,另起blog進行說明。
解決方案:
實現回調 void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
首先,提幾個需要注意的點:
個人歸納總結幾點:
step1 進入開發者模式,勾選「顯示布局邊界」;
step 2,回到你想查看的界面; step 3 假如內容區只有一層基本就是H5 WebView的,多個層級,就是Native。
看到左右圖的差異了吧。
還有另一種方法,RD屌絲們看這里,特別說明,這種方法不太適合瀏覽器。 (自有內核,可能會不準確)
好了,就介紹到這里,零零散散的幾年前寫的文章,第一篇blog,如有不對的地方,還懇請大家指正。
② 如何獲取webView上的部分內容
The extensive Android SDK allows you to do many great things with particular views like the WebView for displaying webpages on Android powered devices
Android SDK 的擴展通過使用特定的view允許你做許多事情比如WebView用來在Android手機上展示網頁
As of lately while I was experimenting with the Android SDK I was using a WebView in one of my activities
最近我在體驗Android SDK的時候在一個Activity中用到了WebView
From that particular WebView I needed to know the ContentHeight but also the ContentWidth
從WebView我不但想要知道ContentHeight還想知道ContentWidth
Now getting the contentHeight is easy like so:
現在的情況是獲取contentHeight很easy如下
webviewgetContentHeight();
Unfortunately getting the contentWidth from a WebView is rather more difficult since there is not a simple method like:
不幸的是從一個WebView獲取contentWidth是相當困難因為SDK中沒有一個像這樣的方法
// THIS METHOD DOES NOT EXIST!
webviewgetContentWidth();
There are ways to get the contentWidth of the rendered HTML page and that is through Javascript If Javascript can get it for you then you can also have them in your Java code within your Android App
當然是有方法獲取contentWidth的就是通過Javascript來獲取如果你能夠支持Javascript那麼你就可以在你的Android 程序中使用java代碼來獲取寬度
By using a JavascriptInterface with your WebView you can let Javascript communicate with your Android App Java code by invoking methods on a registered object that you can embed using the JavascriptInterface
通過在你的WebView中使用JavascriptInterface通過調用你注冊的JavascriptInterface方法可以讓Javascript和你的Android程序的java代碼相互連通
So how does this work?
怎麼做呢?
For a quick example I created a simple Activity displaying a webview that loads a webpage wich displays a log message and a Toast message with the contentWidth wich was determined using Javascript Note that this happens AFTER the page was finished loading because before the page is finished loading the width might not be fully rendered Also keep in mind that if there is content loaded asynchronously that it doesnt affect widths (most likely only heights will be affected as the width is almost always fully declared in CSS files unless you have a % width webpage)
搭建一個快速的例子創建一個簡單的展示webView的Activity一個LogCat消息一個Toast消息用來顯示我們通過 Javascript獲取的寬度注意這些會在網頁完全載入之後顯示因為在網頁載入完成之前寬度可能不能夠正確的獲取到同時也要注意到如果是異 步載入這並不影響寬度(最多高度會受影響因為寬度總是在CSS文件中做了完全的定義除非在網頁中你用了%寬度)
Below is the code of the Activity Mainjava:
下面的代碼是Activity的代碼
package ;
import androidappActivity;
import androidosBundle;
import androitilLog;
import androidwebkitWebView;
import androidwebkitWebViewClient;
import androidwidgetToast;
publicclass Main extends Activity {
privatefinalstatic String LOG_TAG = "WebViewContentWidth";
privatefinal Activity activity = this;
privatestaticint webviewContentWidth = ;
privatestatic WebView webview;
/** Called when the activity is first created */
@Override
publicvoid onCreate(Bundle savedInstanceState) {
superonCreate(savedInstanceState);
setContentView(Rlayoutmain);
webview = (WebView) findViewById(Ridwebview);
webviewgetSettings()setJavaScriptEnabled(true);
webviewsetSaveEnabled(true);
webviewaddJavascriptInterface(new JavaScriptInterface() "HTMLOUT");
webviewsetWebViewClient(new WebViewClient() {
@Override
publicvoid onPageFinished(WebView view String url) {
webviewloadUrl("javascript:windowHTMLOUTgetContentWidth(documentgetElementsByTagName(html)[]scrollWidth);");
}
});
webviewloadUrl(";);
}
class JavaScriptInterface {
publicvoid getContentWidth(String value) {
if (value != null) {
webviewContentWidth = IntegerparseInt(value);
Logd(LOG_TAG "Result from javascript: " + webviewContentWidth);
ToastmakeText( activity
"ContentWidth of webpage is: " +
webviewContentWidth +
"px" ToastLENGTH_SHORT)show();
}
}
}
}
Below is the XML layout used with the Activity wich only contains a simple WebView:
下面是Activity的Layout主要就是一個簡單的WebView
<?xml version="" encoding="utf"?>
<LinearLayout
xmlns:android=";
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<WebView android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
AndroidManifestxml layout:
AndroidManifestxml代碼
<?xml version="" encoding="utf"?>
<manifest
xmlns:android=";
package=""
android:versionCode="" android:versionName="">
<application android:icon="@drawable/icon"
android:label="@string/app_name">
<activity android:name="Main"
android:label="@string/app_name">
<intentfilter>
<action android:name="androidintentactionMAIN" />
<category
android:name="androidintentcategoryLAUNCHER" />
</intentfilter>
</activity>
</application>
<usessdk android:minSdkVersion="" />
<usespermission android:name="androidpermissionINTERNET" />
</manifest>
You can also download the full source of Android Application WebViewContentWidth!
③ Android 下的 WebView 中數據如何保存
1、Android中的WebView控制項當載入html時候,會在data/應用package下生成database與cache兩個文件夾如下圖如示:
Url記錄是保存在webviewCache.db里,而url的內容是保存在webviewCache文件夾下。
WebView中存在著兩種緩存:網頁數據緩存(存儲打開過的頁面及資源)、H5緩存(即AppCache)。
2、緩存構成
/data/data/package_name/cache/
/data/data/package_name/database/webview.db
/data/data/package_name/database/webviewCache.db
webview 會將瀏覽過的網頁url以及網頁文件(css、圖片、js等)保存到資料庫表中。
緩存模式(5種)
LOAD_CACHE_ONLY: 不使用網路,只讀取本地緩存數據
LOAD_DEFAULT: 根據cache-control決定是否從網路上取數據。
LOAD_CACHE_NORMAL: API level 17中已經廢棄, 從API level 11開始作用同LOAD_DEFAULT模式
LOAD_NO_CACHE: 不使用緩存,只從網路獲取數據.
LOAD_CACHE_ELSE_NETWORK,只要本地有,無論是否過期,或者no-cache,都使用緩存中的數據。