原生webview的引擎是webkit,Android 4.4後直接使用了Chrome,那麼是否還有其他引擎呢?有的,比如騰訊的X5內核和crosswalk,crosswalk我不太清楚,有興趣的可以用用看,反正我沒興趣,目前騰訊的X5內核在國內已經很熱門了,這也不排除騰訊的宣傳效果,官方公佈的X5內核的優勢如下:
1) 速度快:相比系統webview的網頁打開速度有30+%的提升;
2) 省流量:使用雲端優化技術使流量節省20+%;
3) 更安全:安全問題可以在24小時內修復;
4) 更穩定:經過億級用戶的使用考驗,CRASH率低於0.15%;
5) 兼容好:無系統內核的碎片化問題,更少的兼容性問題;
6) 體驗優:支持夜間模式、適屏排版、字體設置等瀏覽增強功能;
7) 功能全:在Html5、ES6上有更完整支持;
8) 更強大:集成強大的視頻播放器,支持視頻格式遠多於系統webview;
9) 視頻和文件格式的支持x5內核多於系統內核;
10) 防劫持是x5內核的一大亮點。
在這裏我可以大膽的猜測,X5內核其實就是基於webkit上進一步優化,騰訊採用填坑的方式將webkit封裝成了X5。
由於原生webview的坑比較多,不知道TX要填到什麼時候才能將X5完善,一個完善的X5內核還是令人比較期待了,畢竟本人已經被原生webview坑了好久了。
文章中提到了WebView的狀態,我把它理解爲生命週期:
//激活WebView爲活躍狀態,能正常執行網頁的響應
webView.onResume() ;
//當頁面被失去焦點被切換到後臺不可見狀態,需要執行onPause
//通過onPause動作通知內核暫停所有的動作,比如DOM的解析、plugin的執行、JavaScript執行。
webView.onPause();
//當應用程序(存在webview)被切換到後臺時,這個方法不僅僅針對當前的webview而是全局的全應用程序的webview
//它會暫停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
webView.pauseTimers()
//恢復pauseTimers狀態
webView.resumeTimers();
//銷燬Webview
//在關閉了Activity時,如果Webview的音樂或視頻,還在播放。就必須銷燬Webview
//但是注意:webview調用destory時,webview仍綁定在Activity上
//這是由於自定義webview構建時傳入了該Activity的context對象
//因此需要先從父容器中移除webview,然後再銷燬webview:
rootLayout.removeView(webView);
webView.destroy();
當了解生命週期的前提下,我們不得不考慮webview內存泄漏的問題,網上也有相關的總結:
WebView內存泄漏--解決方法小結
從根源解決WebView內存泄漏、
前進和後退
//是否可以後退
Webview.canGoBack()
//後退網頁
Webview.goBack()
//是否可以前進
Webview.canGoForward()
//前進網頁
Webview.goForward()
//以當前的index爲起始點前進或者後退到歷史記錄中指定的steps
//如果steps爲負數則爲後退,正數則爲前進
Webview.goBackOrForward(intsteps)
Android客戶端的返回鍵分爲:硬件返回和軟件返回,我們可以監聽這兩種返回,然後執行Webview.canGoBack()和Webview.goBack()。
實際上,如果第一次加載的網頁有重定向鏈接時,可能會發生無法返回退出當前頁面的效果,那麼因爲當返回到第一個歷史網頁時,就又被重定向了,我能想到的操作方案如下:
連續點擊兩次返回鍵,強制退出當前頁面;(顯然這樣做不友好)
在導航欄增加一個關閉當前頁面的按鈕;(這是很多APP常用的方式)
-
捕獲歷史記錄,對有重定向的鏈接做特定的處理
//獲取歷史 WebBackForwardList mWebBackForwardList = webvie.copyBackForwardList();
監聽物理返回有兩種方式:
mywebview.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK && mywebview.canGoBack()) { //表示按返回鍵
mywebview.goBack();
return true;
}
}
return false;
}
});
或者
@Override
public void onBackPressed() {
if(mywebview != null && mywebview.canGoBack()){
mywebview.goBack();
}else{
finish();
}
}
清除緩存數據
//清除網頁訪問留下的緩存
//由於內核緩存是全局的因此這個方法不僅僅針對webview而是針對整個應用程序
Webview.clearCache(true);
//清除當前webview訪問的歷史記錄
//只會webview訪問歷史記錄裏的所有記錄除了當前訪問記錄
Webview.clearHistory();
//這個api僅僅清除自動完成填充的表單數據,並不會清除WebView存儲到本地的數據
Webview.clearFormData();
這裏需要注意的是,如果你使用了cookie,按照需求決定是否需要清除cookie
下面是完整的onDestroy代碼
@Override
protected void onDestroy() {
//清除Cookie
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
CookieSyncManager.getInstance().sync();
if( mywebview!=null) {
mywebview.setWebChromeClient(null);
mywebview.setWebViewClient(null);
//清除網頁訪問留下的緩存,由於內核緩存是全局的因此這個方法不僅僅針對webview而是針對整個應用程序
mywebview.clearCache(true);
//只會webview訪問歷史記錄裏的所有記錄除了當前訪問記錄
mywebview.clearHistory();
//這個api僅僅清除自動完成填充的表單數據,並不會清除WebView存儲到本地的數據
mywebview.clearFormData();
// 如果先調用destroy()方法,則會命中if (isDestroyed()) return;這一行代碼,需要先onDetachedFromWindow(),再
// destory()
ViewParent parent = mywebview.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(mywebview);
}
mywebview.stopLoading();
// 退出時調用此方法,移除綁定的服務,否則某些特定系統會報錯
mywebview.getSettings().setJavaScriptEnabled(false);
mywebview.clearView();
mywebview.removeAllViews();
mywebview.destroy();
}
super.onDestroy();
}
下面發一下Cookie的同步代碼
public class SyncWebCookies {
private SyncWebCookies(){}
static class SingleHolder{
public static SyncWebCookies instance = new SyncWebCookies();
}
public static SyncWebCookies getInstance(){
return SingleHolder.instance;
}
/**
* 獲取新cookie
* @param
*/
private static String getNewCookie(String cookie) {
if (cookie != null && cookie.contains(";")) {
cookie = cookie.substring(cookie.lastIndexOf(";")+1, cookie.length());
}
return cookie;
}
public static void synchronousWebCookies(Context mContext, String url) {
CookieManager cookieManager = CookieManager.getInstance();
String cookies = cookieManager.getCookie(url);
cookies = getNewCookie(cookies);//獲取最終cookie
if (!TextUtils.isEmpty(cookies)) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
CookieSyncManager.createInstance(mContext);
}
cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.removeSessionCookie();// 移除
cookieManager.removeAllCookie();
StringBuilder sbCookie = new StringBuilder();
sbCookie.append(cookies);
String cookieValue = sbCookie.toString();
//爲url設置cookie
cookieManager.setCookie(url, cookieValue);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//同步cookie
CookieManager.getInstance().flush();
}else{
//同步cookie
CookieSyncManager.getInstance().sync();
}
}
}
}
代碼的邏輯是這樣的,獲取原有cookie,然後取cookie最後一個“;”的cookie,再同步。原因有以下幾點:
(1)當有新的cookie生成時,新的cookie會添加到原有cookie之後,cookie和cookie之間用分號隔開,也就是說,最後一個cookie纔是最新的cookie;
(2)cookieManager.setCookie(url, cookieValue)當設置cookie時,如果cookieValue有分號,只能截取到第一個分號之前的數據,也就是說,如果cookieValue=“1234;5678”,那麼調用該方法之後cookie是“1234”,而不是“1234;5678”;
--以上設置cookie的結論是結合以前項目總結的,當前並沒有經過系統的測試。--
然而,怎麼設置cookie是根據需求而定的。
如果涉及到網絡,需要註冊網絡權限
<uses-permission android:name="android.permission.INTERNET"/>
網頁加載
1.loadData(String data, String mimeType, String encoding)
String data= "<html>###標題###<body><p>我是段落</p></body></html>";
mywebview.loadData(data, "text/html", "UTF8");
輸出結果如下:
感覺奇怪的是,命名encoding指明的是“utf-8”,但是還是亂碼了,查詢了一些資料才發現,這裏encoding其實是不生效的,這是原生的一個bug。
解決方案是改成如下代碼:
String data= "<html>###標題###<body><p>我是段落</p></body></html>";
mywebview.loadData(data, "text/html;charset=UTF-8", null);
輸出結果是:
2.loadDataWithBaseURL(String baseUrl, String data,String mimeType, String encoding, String failUrl)
String data= "<html>###標題###<body><p>我是段落</p></body></html>";
mywebview.loadDataWithBaseURL(null, data, "text/html", "UTF-8", null);
輸出效果如下:
該方法的encoding是生效的,這個方法是官方比較推薦的方法。官方之所以推薦使用這個方法不僅僅是因爲encoding,官方文檔中有“同源策略”的概念,這個也許需要結合代碼才能理解:
在網上隨便找了兩個同源圖片:
http://img.daimg.com/uploads/allimg/110825/3-110R5133545427.jpg
http://img.daimg.com/uploads/allimg/120302/3-1203021T03E04.jpg
String data = "<img src='/uploads/allimg/110825/3-110R5133545427.jpg' /><p/><img src='/uploads/allimg/120302/3-1203021T03E04.jpg'/>";
String baseUrl = "http://img.daimg.com";
mywebview.loadDataWithBaseURL(baseUrl, data, "text/html", "utf-8", null);
data裏面的圖片路勁是相對路勁,在加載數據時,指定baseUrl之後纔可以正確在webview中加載圖片,效果如下:
3.loadUrl(String url)、loadUrl(String url, Map<String, String> additionalHttpHeaders)
加載網絡圖片
//加載網絡圖片
mywebview.loadUrl("http://img.daimg.com/uploads/allimg/110825/3-110R5133545427.jpg");
加載一個網址
//加載一個網址
mywebview.loadUrl("https://www.baidu.com");
Map<String, String> map = new HashMap<>();
map.put("key", "value");
mywebview.loadUrl(url, map);
加載本地一個文件(android_asset或android_res)
//該文件的格式是html,webview支持加載html格式的文件,在webview中已網頁的形式展示
mywebview.loadUrl("file:///android_asset/htmldemo3.html");
//該文件的格式是ui,這個後綴是我隨便命名的,webview不能識別ui格式的文件,當我將這個文件拷貝到assets文件夾中時,必須選擇一種AS支持的一種類型
mywebview.loadUrl("file:///android_asset/htmldemo3.ui");
//該文件的格式是txt,webview可以識別txt格式的文件
mywebview.loadUrl("file:///android_asset/htmldemo3.txt");
//該文件的格式是mp3,webview可以識別mp3格式的文件可以查看下方截圖:
mywebview.loadUrl("file:///android_asset/htmldemo3.mp3");
webview可以嘗試加載任意資源文件,我加載了excel文件,發現不能顯示內容,總之,文件的格式有很多,webview到底支持哪些格式的文件需要嘗試後才知道,最後再擴展一下,文件url的協議不一定是file, 也有可能是content,支持ContentProvider的協議。
//加載某一資源庫文件
mywebview.loadUrl("content://authorities/person/xxxx");
4.postUrl(String url, byte[] postData)
一般我們加載網頁是這樣寫mywebview.loadUrl("http://www.xxx.xxx/demo?username='zhangsan'&age=12"),這是一個典型的get請求,但是如果需要post請求呢?這時就會需要postUrl(String url, byte[] postData)的支持。
具體用法如下:
public class User {
String username;
int age;
}
User user = new User();
user.username = "za";
user.age = 12;
//使用POST請求加載指定的網頁
try {
mywebview.postUrl(url, concatParams(getAllFields(user)).getBytes());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NullPointerException e){
e.printStackTrace();
}
/**
*
* @param t
* @param <T>
* @return
*/
private <T> Map<String,String> getAllFields(T t){
Field[] field = t.getClass().getDeclaredFields();
Map<String,String> fieldsAndValues = new HashMap<>();
try {
for (int i = 0; i < field.length; i++) {
String name = field[i].getName();
if(field[i].getGenericType().toString().equals("class java.lang.String")){
String value = (String) field[i].get(t);
fieldsAndValues.put(name,value);
}
}
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
System.out.print(""+fieldsAndValues.size());
return fieldsAndValues;
}
private String concatParams(Map<String,String> params) throws UnsupportedEncodingException {
if(params.size() ==0){
return null;
}
StringBuilder builder = new StringBuilder();
Set<String> keys = params.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()){
String key = iterator.next();
String value = URLEncoder.encode(params.get(key), "UTF-8");
builder.append(String.format("%s=%s&",key, value));
}
builder.deleteCharAt(builder.lastIndexOf("&"));
return builder.toString();
}
5.重新加載當前網頁reload()
mywebview.reload();
webview常用配置
webview一般需要支持JS,如果不支持,所有的JS功能將失效
//是否支持JS,如果訪問的頁面中要與Javascript交互,則webview必須設置支持Javascript
mywebview.getSettings().setJavaScriptEnabled(true);
窗口相關的配置一般用的比較少,瞭解就行
//讓JavaScript自動打開窗口,默認false。適用於JavaScript方法window.open()。
mywebview.getSettings().setJavaScriptCanOpenWindowsAutomatically(false);
//設置WebView是否支持多窗口。如果設置爲true,主程序要實現onCreateWindow(WebView, boolean, boolean, Message),默認false。
mywebview.getSettings().setSupportMultipleWindows(false);
//當WebView切換到後臺但仍然與窗口關聯時是否raster tiles,打開它可以避免在WebView從後臺切換到前臺時重新繪製,默認值false。在這種模式下後臺的WebView佔用更多的內存。請按如下準則顯示內存的使用:
//WebView的尺寸不能比設備的屏幕尺寸更大;
//限制在少數WebView上使用該模式;
//在可見的WebView和即將顯現的WebView上使用;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mywebview.getSettings().setOffscreenPreRaster(true);
}
縮放功能
mywebview.getSettings().setSupportZoom(true);//是否支持縮放功能(mywebview.getSettings().supportZoom()可以判斷當前是否支持縮放)
mywebview.getSettings().setBuiltInZoomControls(true);//設置內置的縮放控件。若爲false,則該WebView不可縮放
mywebview.getSettings().setDisplayZoomControls(false);//顯示或隱藏原生的縮放控件
//mywebview.getSettings().setDefaultZoom();//已廢棄
//mywebview.getSettings().setEnableSmoothTransition()//已廢棄
需要注意的是:
(1)網頁端可以將縮放功能屏蔽掉,如果加上以上代碼依然不能縮放的話,則說明網頁上已經將縮放功能屏蔽。
(2)當原生縮放控件漸變動畫未結束前就銷燬當前activity,會造成內存泄漏,再次打開activity時還有可能造成以下錯誤:
java.lang.IllegalArgumentException: Receiver not registered: android.widget.ZoomButtonsController$1@14ff16a
at android.app.LoadedApk.forgetReceiverDispatcher(LoadedApk.java:856)
at android.app.ContextImpl.unregisterReceiver(ContextImpl.java:1352)
at android.content.ContextWrapper.unregisterReceiver(ContextWrapper.java:576)
at android.widget.ZoomButtonsController.setVisible(ZoomButtonsController.java:404)
at android.widget.ZoomButtonsController$2.handleMessage(ZoomButtonsController.java:178)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5546)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)
設置字體和字體大小
//API14版本以上已廢棄。請取代使用setTextZoom(int)。設置頁面文本的尺寸,默認NORMAL。
//mywebview.getSettings().setTextSize();
//設置頁面上的文本縮放百分比,默認100
mywebview.getSettings().setTextZoom(100);
//設置WebView字體庫字體,默認“cursive”
mywebview.getSettings().setCursiveFontFamily("cursive");
//設置fantasy字體集(font family)的名字默認爲“fantasy”
mywebview.getSettings().setFantasyFontFamily("fantasy");
//設置固定的字體集的名字,默認爲”monospace”。
mywebview.getSettings().setFixedFontFamily("monospace");
//設置默認固定的字體大小,默認爲16,可取值1到72
mywebview.getSettings().setDefaultFixedFontSize(16);
//設置默認的字體大小,默認16,可取值1到72
mywebview.getSettings().setDefaultFontSize(16);
//設置最小的字號,默認爲8
mywebview.getSettings().setMinimumFontSize(8);
//設置最小的本地字號,默認爲8。
mywebview.getSettings().setMinimumLogicalFontSize(8);
//設置標準字體集的名字,默認值“sans-serif”。
mywebview.getSettings().setStandardFontFamily("sans-serif");
//設置襯線字體集(serif font family)的名字,默認“sans-serif”。
mywebview.getSettings().setSerifFontFamily("sans-serif");
//設置無襯線字體集(sans-serif font family)的名字。默認值”sans-serif”.
mywebview.getSettings().setSansSerifFontFamily("sans-serif");
這裏設置字體和設置字體大小基本不用,默認就行,這裏需要關心的是setTextZoom這個方法,默認值是100,作用有兩個:
(1)可以設置webview的字體大小;
(2)當設置手機自帶的字體大小時,webview的字體大小會隨之變化,setTextZoom可以保證webview字體大小不隨手機自帶字體大小的變化而變化。
webview編碼問題
//設置默認的字符編碼集,默認”UTF-8”
mywebview.getSettings().setDefaultTextEncodingName("utf-8");//設置編碼格式
webview通過JS進行文件訪問
//是否允許在WebView中訪問內容URL(Content Url),默認允許。內容Url訪問允許WebView從安裝在系統中的內容提供者載入內容。
mywebview.getSettings().setAllowContentAccess(false);
//設置是否允許 WebView 使用 File 協議,默認設置爲true,即允許在 File 域下執行任意 JavaScript 代碼
//使用 file 域加載的 js代碼能夠使用進行同源策略跨域訪問,從而導致隱私信息泄露
//1.源策略跨域訪問:對私有目錄文件進行訪問
//2.針對 IM 類產品,泄露的是聊天信息、聯繫人等等
//2.針對瀏覽器類軟件,泄露的是cookie 信息泄露
mywebview.getSettings().setAllowFileAccess(false);
//設置是否允許通過 file url 加載的 Js代碼讀取其他的本地文件
//在Android 4.1前默認允許,在Android 4.1後默認禁止
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mywebview.getSettings().setAllowFileAccessFromFileURLs(false);
// 設置是否允許通過 file url 加載的 Javascript 可以訪問其他的源(包括http、https等源)
//在Android 4.1前默認允許、在Android 4.1後默認禁止
mywebview.getSettings().setAllowUniversalAccessFromFileURLs(false);
}
由於安全性的問題,如果以上方法設置成true,攻擊者可以通過JS操作手機本地文件,包括app內核app外,這是一個非常嚴重的安全問題,在以後的配置中,直接把以上設置成false即可。
webview緩存
//應用緩存API是否可用,默認值false, 結合setAppCachePath(String)使用。
mywebview.getSettings().setAppCacheEnabled(true);
String storePath = Environment.getExternalStorageDirectory().getPath() + "/webcache/";
//storePath路徑是外部路徑,非APP路勁,所以需要配置文件存儲和讀取文件的權限
File file = new File(storePath);
file.mkdirs();
//設置應用緩存文件的路徑。爲了讓應用緩存API可用,此方法必須傳入一個應用可寫的路徑。該方法只會執行一次,重複調用會被忽略。
mywebview.getSettings().setAppCachePath(storePath);
//mywebview.getSettings().setAppCacheMaxSize(1000);//設置緩存最大值,現在已被棄用,緩存的管理變爲自動管理
//重寫使用緩存的方式,默認值LOAD_DEFAULT。緩存的使用方式基於導航類型,正常的頁面加載,檢測緩存,需要時緩存內容復現。導航返回時,內容不會復現,只有內容會從緩存盤中恢復。該方法允許客戶端通過指定LOAD_DEFAULT, LOAD_CACHE_ELSE_NETWORK, LOAD_NO_CACHE or LOAD_CACHE_ONLY的其中一項來重寫其行爲。
//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,都使用緩存中的數據
if(isNetworkConnected(MainActivity.this)){
mywebview.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
}else{
mywebview.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
//DOM存儲API是否可用,默認false。
mywebview.getSettings().setDomStorageEnabled(true);
//數據庫存儲API是否可用,默認值false。如何正確設置數據存儲API參見setDatabasePath(String)。該設置對同一進程中的所有WebView實例均有效。注意,只能在當前進程的任意WebView加載頁面之前修改此項,因爲此節點之後WebView的實現類可能會忽略該項設置的改變。
mywebview.getSettings().setDatabaseEnabled(true);
//mywebview.getSettings().setDatabasePath();//已廢棄
//已廢棄,WebView是否保存表單數據,默認值true。
//mywebview.getSettings().setSaveFormData(true);
//API18以上版本已廢棄。未來版本將不支持保存WebView中的密碼。設置WebView是否保存密碼,默認true。
//mywebview.getSettings().setSavePassword(true);
這裏着重理解一下文本view的緩存模式,代碼中給出的策略是,如果網絡可用模式爲LOAD_DEFAULT,否則模式爲LOAD_CACHE_ELSE_NETWORK。
圖片和數據的加載
//是否禁止從網絡(通過http和https URI schemes訪問的資源)下載圖片資源,默認值爲false。注意,除非getLoadsImagesAutomatically()返回true,否則該方法無效。還請注意,即使此項設置爲false,使用setBlockNetworkLoads(boolean)禁止所有網絡加載也會阻止網絡圖片的加載。當此項設置的值從true變爲false,WebView當前顯示的內容所引用的網絡圖片資源會自動獲取。
mywebview.getSettings().setBlockNetworkImage(false);
//WebView是否下載圖片資源,默認爲true。注意,該方法控制所有圖片的下載,包括使用URI嵌入的圖片(使用setBlockNetworkImage(boolean) 只控制使用網絡URI的圖片的下載)。如果該設置項的值由false變爲true,WebView展示的內容所引用的所有的圖片資源將自動下載。
mywebview.getSettings().setLoadsImagesAutomatically(true);
//是否禁止從網絡下載數據,如果app有INTERNET權限,默認值爲false,否則默認爲true。使用setBlockNetworkImage(boolean) 只會禁止圖片資源的加載。注意此值由true變爲false,當前WebView展示的內容所引用的網絡資源不會自動加載,直到調用了重載。如果APP沒有INTERNET權限,設置此值爲false會拋出SecurityException。
mywebview.getSettings().setBlockNetworkLoads(false);
setBlockNetworkImage:允許或禁止網絡圖片加載(注意:重點是“網絡”)
setLoadsImagesAutomatically:允許或禁止網絡和本地圖片的加載(注意:重點是“網絡”和“本地”)
setBlockNetworkLoads:允許或禁止網絡下載數據
webview定位
//定位是否可用,默認爲true。請注意,爲了確保定位API在WebView的頁面中可用,必須遵守如下約定:
//(1) app必須有定位的權限,參見ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION;
//(2) app必須提供onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)回調方法的實現,在頁面通過JavaScript定位API請求定位時接收通知。
//作爲可選項,可以在數據庫中存儲歷史位置和Web初始權限,參見setGeolocationDatabasePath(String).
mywebview.getSettings().setGeolocationEnabled(true);
//mywebview.getSettings().setGeolocationDatabasePath();//已廢棄
設置UserAgent
//設置WebView的用戶代理字符串。如果字符串爲null或者empty,將使用系統默認值。注意從KITKAT版本開始,加載網頁時改變用戶代理會讓WebView再次初始化加載。
mywebview.getSettings().setUserAgentString(mywebview.getSettings().getUserAgentString());
如果是加載第三方網站,請不要隨意更改別人的UserAgent,避免加載錯誤,因爲你不清楚人家的加載邏輯中有沒有涉及到對UserAgent的判斷,UserAgent的一般用法是爲了區別是哪一個客戶端(比如:Android端可以設置爲:android,IOS端可以設置爲:ios,PC端可以設置爲:pc)
允許使用輕觸摸做出選擇和光標懸停(已廢棄)
//已廢棄。從 JELLY_BEAN 開始,該設置無效。允許使用輕觸摸做出選擇和光標懸停。
//mywebview.getSettings().setLightTouchEnabled(false);
禁用菜單項模式行爲包括menuItems標籤(複製、粘貼、選擇)
//禁用菜單項模式行爲包括menuItems標籤
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mywebview.getSettings().setDisabledActionModeMenuItems(WebSettings.MENU_ITEM_NONE);
//mywebview.getSettings().setDisabledActionModeMenuItems(WebSettings.MENU_ITEM_PROCESS_TEXT);
//mywebview.getSettings().setDisabledActionModeMenuItems(WebSettings.MENU_ITEM_SHARE);
//mywebview.getSettings().setDisabledActionModeMenuItems(WebSettings.MENU_ITEM_WEB_SEARCH);
}
}catch (NoSuchMethodError noSuchMethodError){
}
這個就不寫註釋了,因爲我在運行的時候崩潰了,報了NoSuchMethodError錯誤,總之如果以後有一個需求設計到這個再回來看吧。
webview適配
//設置佈局,會引起WebView的重新佈局(relayout),默認值NARROW_COLUMNS
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mywebview.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING);
}else{
mywebview.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
}
//是否允許WebView度超出以概覽的方式載入頁面,默認false。即縮小內容以適應屏幕寬度。該項設置在內容寬度超出WebView控件的寬度時生效,例如當getUseWideViewPort() 返回true時。
mywebview.getSettings().setLoadWithOverviewMode(true);
//WebView是否支持HTML的“viewport”標籤或者使用wide viewport。設置值爲true時,佈局的寬度總是與WebView控件上的設備無關像素(device-dependent pixels)寬度一致。當值爲true且頁面包含viewport標記,將使用標籤指定的寬度。如果頁面不包含標籤或者標籤沒有提供寬度,那就使用wide viewport。
mywebview.getSettings().setUseWideViewPort(true);
setLayoutAlgorithm目前只剩下兩種常量了:TEXT_AUTOSIZING和NORMAL,其他的都已經棄用了,目前不太清楚它們的界面效果。
但是將setLoadWithOverviewMode和setUseWideViewPort設置成true完全可以看出效果,沒有適配的網頁真的適配了。當然,網頁本身也可以做到自適應。
播放視頻相關
//WebView是否需要用戶的手勢進行媒體播放,默認值爲true。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mywebview.getSettings().setMediaPlaybackRequiresUserGesture(false);
}
默認情況下setMediaPlaybackRequiresUserGesture的值是true,需要手動點擊視頻才能播放,如果改成false,那麼在網頁滑動的時候就可以實現自動播放。
Android5.0上WebView中Http和Https混合問題
/**
* Android5.0上 WebView中Http和Https混合問題
* MIXED_CONTENT_ALWAYS_ALLOW:允許從任何來源加載內容,即使起源是不安全的;
* MIXED_CONTENT_NEVER_ALLOW:不允許Https加載Http的內容,即不允許從安全的起源去加載一個不安全的資源;(默認)
* MIXED_CONTENT_COMPATIBILITY_MODE:當涉及到混合式內容時,WebView 會嘗試去兼容最新Web瀏覽器的風格。
**/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mywebview.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
在Android5.0上,爲了提高安全性,https的頁面不能直接訪問http,導致一些功能不可用,以上代碼可以解決這個問題。
請求焦點時是否需要設置節點獲取焦點
//調用requestFocus(int, android.graphics.Rect)時是否需要設置節點獲取焦點,默認值爲true。
mywebview.getSettings().setNeedInitialFocus(true);
這個默認就是ture,不用管了。
插件相關
//mywebview.getSettings().setPluginsEnabled();//現在這個已經不再支持,被setPluginState替代
//在API18以上已廢棄。未來將不支持插件,不要使用。告訴WebView啓用、禁用或者有即用(on demand)的插件,即用模式是指如果存在一個可以處理嵌入內容的插件,會顯示一個佔位圖標,點擊時開啓。默認值OFF。
//mywebview.getSettings().setPluginState(WebSettings.PluginState.ON);
以後的webview版本中都不會使用到插件,不用管,知道就行了。
調整線程優先級
//在API18以上已廢棄。不建議調整線程優先級,未來版本不會支持這樣做。設置繪製線程的優先級。不像其他設置,同一進程中只需調用一次,默認值NORMAL
//mywebview.getSettings().setRenderPriority(WebSettings.RenderPriority priority.);
官方都不建議調整優先級了,不用管。
webview安全瀏覽
//設置是否啓用安全瀏覽。安全瀏覽功能允許WebView通過驗證鏈接來防範惡意軟件和網絡釣魚攻擊。使用清單標籤可以禁用所有WebView的安全瀏覽功能。清單標記的優先級低於此API。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mywebview.getSettings().setSafeBrowsingEnabled(true);
}
在Android8.0之後新增的特性,可以用代碼控制安全瀏覽,其標籤就是
<meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
android:value="true" />
由於文章太長,官方不讓發佈,所以文章就一分爲二了