WebView的核心用法與最佳實踐,避免常見陷阱和優化技巧
WebView介紹
WebView是Android平臺中用于顯示網頁內容的控件,基于Chromium項目(并非完整版的Chrome瀏覽器,不包括Chrome中的所有功能)。WebView使用WebKit引擎來渲染網頁,可以很好地兼容Web標準,可以顯示HTML、CSS和JavaScript等內容,還可以用于動態加載網頁內容,并與網頁進行交互,如點擊鏈接、輸入文本等。
WebView在Android應用開發中非常有用,在需要展示網頁內容或者與網頁交互的場景中。例如,在微信或微博等應用程序中,WebView常用于打開應用程序內的共享超鏈接。通過WebView在應用中直接展示網頁內容,提供了更為豐富的用戶體驗。
WebView的生命周期:
onResume()
:當WebView處于活躍狀態時,會回調此方法。WebView可以正常執行網頁的響應,包括加載網頁內容、執行JavaScript等。onPause()
:當WebView被切換到后臺或失去焦點時,會回調此方法。WebView會暫停所有進行中的動作,如DOM的解析、CSS和JavaScript的執行等,以降低CPU功耗。destroy()
:當WebView需要被銷毀以釋放資源時,會調用此方法。在這個階段,應確保所有與WebView相關的資源都被正確清理,以避免內存泄漏。
為了正確管理WebView的生命周期,應跟隨Activity的生命周期方法來調用WebView的生命周期方法。例如,當Activity進入onResume狀態時,應調用WebView的onResume方法;當Activity進入onPause狀態時,應調用WebView的onPause方法;當Activity被銷毀時,應確保WebView也被正確銷毀。
@Override
protected void onResume() {
super.onResume();
//恢復webview的狀態(不靠譜)
webView.resumeTimers();
//激活webView的狀態,能正常加載網頁
webView.onResume();
}
@Override
protected void onPause() {
super.onPause();
//當頁面被失去焦點被切換到后臺不可見狀態,需要執行onPause
//通過onPause動作通知內核暫停所有的動作,比如DOM的解析、plugin的執行、JavaScript執行。
webView.onPause();
//當應用程序(存在webview)被切換到后臺時,這個方法不僅僅針對當前的webview而是全局的全應用程序的webview
//它會暫停所有webview的layout,parsing,javascripttimer。降低CPU功耗。(不靠譜)
webView.pauseTimers();
}
@Override
protected void onDestroy() {
super.onDestroy();
//在關閉了Activity時,如果Webview的音樂或視頻,還在播放。就必須銷毀Webview
//但是注意:webview調用destory時,webview仍綁定在Activity上
//這是由于自定義webview構建時傳入了該Activity的context對象
//因此需要先從父容器中移除webview,然后再銷毀webview:
ViewGroup parent = findViewById(R.id.container);
parent.removeView(webView);
webView.destroy();
}
WebView使用
添加網絡權限
<uses-permission android:name="android.permission.INTERNET" />
- 布局文件添加WebView控件
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- 初始化WebView
WebView webView = (WebView) findViewById(R.id.webview);
- 設置WebSettings 通過WebSettings類來配置WebView的一些設置項,比如是否支持JavaScript,是否允許縮放等。
//聲明WebSettings子類
WebSettings webSettings = webView.getSettings();
//如果訪問的頁面中要與Javascript交互,則webview必須設置支持Javascript
webSettings.setJavaScriptEnabled(true);
//支持插件
webSettings.setPluginsEnabled(true);
//設置自適應屏幕,兩者合用
webSettings.setUseWideViewPort(true); //將圖片調整到適合webview的大小
webSettings.setLoadWithOverviewMode(true); // 縮放至屏幕的大小
//縮放操作
webSettings.setSupportZoom(true); //支持縮放,默認為true。是下面那個的前提。
webSettings.setBuiltInZoomControls(true); //設置內置的縮放控件。若為false,則該WebView不可縮放
webSettings.setDisplayZoomControls(false); //隱藏原生的縮放控件
//其他細節操作
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //關閉webview中緩存
webSettings.setAllowFileAccess(true); //設置可以訪問文件
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通過JS打開新窗口
webSettings.setLoadsImagesAutomatically(true); //支持自動加載圖片
webSettings.setDefaultTextEncodingName("utf-8");//設置編碼格式
- 加載網頁內容 WebView可以加載遠程網頁或本地HTML資源。使用
loadUrl
方法加載一個網頁的URL,或者使用loadData
方法加載一段HTML數據。
webView.loadUrl("https://www.baidu.com"); // 加載遠程網頁
加載本地的HTML文件:
webView.loadUrl("file:///android_asset/index.html"); // 加載本地HTML文件
加載HTML數據:
String goods_content="<p>我的第一個段落。</p>";
webView.loadDataWithBaseURL(null, WebUtil.getHtmlData(goods_content), "text/html", "utf-8", null);
public static String getHtmlData(String bodyHTML) {
String head = "<head>" +
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\"> " +
"<style>div,p,img{max-width: 100%; width: 100% !important; height: auto !important;}" +
"body {" +
"margin-right:8px;" +//限定網頁中的文字右邊距為15px(可根據實際需要進行行管屏幕適配操作)
"margin-left:8px;" +//限定網頁中的文字左邊距為15px(可根據實際需要進行行管屏幕適配操作)
"margin-top:8px;" +//限定網頁中的文字上邊距為15px(可根據實際需要進行行管屏幕適配操作)
"font-size:16px;" +//限定網頁中文字的大小為40px,請務必根據各種屏幕分辨率進行適配更改
"word-wrap:break-word;" +//允許自動換行(漢字網頁應該不需要這一屬性,這個用來強制英文單詞換行,類似于word/wps中的西文換行)
"}" +
"p { margin: 0; }" +
"</style>" +
"</head>";
return "<html>" + head + "<body>" + bodyHTML + "</body><ml>";
}
- 處理網頁加載事件 常規用法,復寫shouldOverrideUrlLoading()方法,使打開網頁時不調用系統瀏覽器, 而是在WebView中顯示。
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
通過WebViewClient或WebChromeClient類來處理網頁加載過程中的一些事件,比如頁面開始加載、頁面加載完成、出現錯誤等。
WebViewClient webViewClient = new WebViewClient() {
/**
* shouldOverrideUrlLoading
* <p>
* 當加載的網頁需要重定向的時候就會回調這個函數告知我們應用程序是否需要接管控制網頁加載,如果應用程序接管,
*并且return true意味著主程序接管網頁加載,如果返回false讓webview自己處理。
* </p>
* 參數說明:
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param url
* 即將要被加載的url
* @return true 當前應用程序要自己處理這個url, 返回false則不處理。 注:"post"請求方式不會調用這個回調函數
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("www.baidu.com")) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
return true;
}
return false;
}
/**
* onPageStarted 當內核開始加載訪問的url時,會通知應用程序,對每個main frame
* 這個函數只會被調用一次,頁面包含iframe或者framesets 不會另外調用一次onPageStarted,
* 當網頁內內嵌的frame 發生改變時也不會調用onPageStarted。
*
* 參數說明:
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param url
* 即將要被加載的url
* @param favicon
* 如果這個favicon已經存儲在本地數據庫中,則會返回這個網頁的favicon,否則返回為null。
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
// TODO Auto-generated method stub
super.onPageStarted(view, url, favicon);
Log.i(TAG, "onPageStarted:頁面開始加載");
}
/**
* onPageFinished 當內核加載完當前頁面時會通知我們的應用程序,這個函數只有在main
* frame情況下才會被調用,當調用這個函數之后,渲染的圖片不會被更新,如果需要獲得新圖片的通知可以使用@link
* WebView.PictureListener#onNewPicture。 參數說明:
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param url
* 即將要被加載的url
*/
@Override
public void onPageFinished(WebView view, String url) {
// TODO Auto-generated method stub
super.onPageFinished(view, url);
Log.i(TAG, "onPageStarted:頁面加載結束");
}
/**
* onLoadResource 通知應用程序WebView即將加載url 制定的資源
*
* 參數說明:
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param url
* 即將加載的url 資源
*/
@Override
public void onLoadResource(WebView view, String url) {
// TODO Auto-generated method stub
super.onLoadResource(view, url);
Log.i(TAG, "onLoadResource:加載資源指定的網址");
}
/**
* shouldInterceptRequest
* 通知應用程序內核即將加載url制定的資源,應用程序可以返回本地的資源提供給內核,若本地處理返回數據,內核不從網絡上獲取數據。
*
* 參數說明:
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param url
* raw url 制定的資源
* @return 返回WebResourceResponse包含數據對象,或者返回null
*/
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
// TODO Auto-generated method stub
Log.i(TAG, "shouldInterceptRequest");
return super.shouldInterceptRequest(view, url);
}
/**
* onReceivedError
* <p>
* 當瀏覽器訪問制定的網址發生錯誤時會通知我們應用程序 參數說明:
* </p>
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param errorCode
* 錯誤號可以在WebViewClient.ERROR_* 里面找到對應的錯誤名稱。
* @param description
* 描述錯誤的信息
* @param failingUrl
* 當前訪問失敗的url,注意并不一定是我們主url
*/
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
// TODO Auto-generated method stub
super.onReceivedError(view, errorCode, description, failingUrl);
view.loadUrl("file:///android_asset/error.html");
Log.i(TAG, "onReceivedError");
}
/**
* 如果瀏覽器需要重新發送POST請求,可以通過這個時機來處理。默認是不重新發送數據。 參數說明
*
* @param view
* 接收WebViewClient的webview
* @param dontResend
* 瀏覽器不需要重新發送的參數
* @param resend
* 瀏覽器需要重新發送的參數
*/
@Override
public void onFormResubmission(WebView view, Message dontResend,
Message resend) {
// TODO Auto-generated method stub
super.onFormResubmission(view, dontResend, resend);
Log.i(TAG, "onFormResubmission");
}
/**
* doUpdateVisitedHistory
* 通知應用程序可以將當前的url存儲在數據庫中,意味著當前的訪問url已經生效并被記錄在內核當中。這個函數在網頁加載過程中只會被調用一次。
* 注意網頁前進后退并不會回調這個函數。
*
* 參數說明:
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param url
* 當前正在訪問的url
* @param isReload
* 如果是true 這個是正在被reload的url
*/
@Override
public void doUpdateVisitedHistory(WebView view, String url,
boolean isReload) {
// TODO Auto-generated method stub
super.doUpdateVisitedHistory(view, url, isReload);
Log.i(TAG, "doUpdateVisitedHistory");
}
/**
* 當網頁加載資源過程中發現SSL錯誤會調用此方法。我們應用程序必須做出響應,是取消請求handler.cancel(),還是繼續請求handler.
* proceed();內核的默認行為是handler.cancel();
*
* 參數說明:
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param handler
* 處理用戶請求的對象。
* @param error
* SSL錯誤對象
*
*/
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler,
SslError error) {
// view.loadUrl("file:///android_asset/error.html");
// TODO Auto-generated method stub
super.onReceivedSslError(view, handler, error);
Log.i(TAG, "onReceivedSslError");
}
/**
* onReceivedHttpAuthRequest 通知應用程序WebView接收到了一個Http
* auth的請求,應用程序可以使用supplied 設置webview的響應請求。默認行為是cancel 本次請求。
*
*
* 參數說明:
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param handler
* 用來響應WebView請求的對象
* @param host
* 請求認證的host
* @param realm
* 認真請求所在的域
*/
@Override
public void onReceivedHttpAuthRequest(WebView view,
HttpAuthHandler handler, String host, String realm) {
// TODO Auto-generated method stub
super.onReceivedHttpAuthRequest(view, handler, host, realm);
Log.i(TAG, "onReceivedHttpAuthRequest");
}
/**
* shouldOverrideKeyEvent
* 提供應用程序同步一個處理按鍵事件的機會,菜單快捷鍵需要被過濾掉。如果返回true,webview不處理該事件,如果返回false,
* webview會一直處理這個事件,因此在view 鏈上沒有一個父類可以響應到這個事件。默認行為是return false;
*
*
* 參數說明:
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param event
* 鍵盤事件名
* @return 如果返回true,應用程序處理該時間,返回false 交由webview處理。
*/
@Override
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
Log.i(TAG, "shouldOverrideKeyEvent");
// TODO Auto-generated method stub
return super.shouldOverrideKeyEvent(view, event);
}
/**
* 通知應用程序webview 要被scale。應用程序可以處理改事件,比如調整適配屏幕。
*/
@Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
// TODO Auto-generated method stub
super.onScaleChanged(view, oldScale, newScale);
Log.i(TAG, "onScaleChanged");
}
/**
* onReceivedLoginRequest 通知應用程序有個自動登錄的帳號過程
*
* 參數說明:
*
* @param view
* 請求登陸的webview
* @param realm
* 賬戶的域名,用來查找賬戶。
* @param account
* 一個可選的賬戶,如果是null 需要和本地的賬戶進行check, 如果是一個可用的賬戶,則提供登錄。
* @param args
* 驗證制定參數的登錄用戶
*/
@Override
public void onReceivedLoginRequest(WebView view, String realm,
String account, String args) {
// TODO Auto-generated method stub
super.onReceivedLoginRequest(view, realm, account, args);
Log.i(TAG, "onReceivedLoginRequest");
}
});
WebChromeClient輔助WebVlew處理Javascrlpt的對話框,網站圖標,網站tltle,加載進度等。
webView.setWebChromeClient(new WebChromeClient() {
/**
* onProgressChanged 通知應用程序當前網頁加載的進度。
*
* 參數說明:
*
* @param view
* 接收WebChromeClient的的webview實例
* @param newProgress
* webview接受的進度
*/
@Override
public void onProgressChanged(WebView view, int newProgress) {
// TODO Auto-generated method stub
super.onProgressChanged(view, newProgress);
if (newProgress <= 100) {
Log.i(TAG, newProgress + "===onProgressChanged===");
}
}
/**
* 當document 的title變化時,會通知應用程序
*
*
* 參數說明:
*
* @param view
* 接收WebViewClient的webview實例
* @param title
* document新的title
*/
@Override
public void onReceivedTitle(WebView view, String title) {
// TODO Auto-generated method stub
super.onReceivedTitle(view, title);
Message message = new Message();
message.what = 100;
message.obj = title;
handler.sendMessage(message);
}
/**
* 當前頁面有個新的favicon時候,會回調這個函數。 參數說明:
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param icon
* 當前頁面的favicon 注:很多時間不會跳轉到此回調函數,因為很多網站設置了icon,沒有設置favicon,
*/
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
// TODO Auto-generated method stub
super.onReceivedIcon(view, icon);
Message message = new Message();
message.what = 200;
message.obj = icon;
handler.sendMessage(message);
}
/**
* 通知應用程序 apple-touch-icon的 url
*
* 參數說明:
*
* @param view
* 接收WebViewClient的那個實例,前面看到webView.setWebViewClient(new
* MyAndroidWebViewClient()),即是這個webview。
* @param url
* apple-touch-icon 的服務端地址
* @param precomposed
* 如果precomposed 是true 則touch-icon是預先創建的
*
* Tips
*
* 如果應用程序需要這個icon的話, 可以通過這個url獲取得到 icon。
*/
@Override
public void onReceivedTouchIconUrl(WebView view, String url,
boolean precomposed) {
// TODO Auto-generated method stub
super.onReceivedTouchIconUrl(view, url, precomposed);
Log.i(TAG, "====onReceivedTouchIconUrl====");
}
/**
* webview請求得到focus,發生這個主要是當前webview不是前臺狀態,是后臺webview。
*/
@Override
public void onRequestFocus(WebView view) {
// TODO Auto-generated method stub
super.onRequestFocus(view);
Log.i(TAG, "====onRequestFocus====");
}
/**
* 覆蓋默認的window.alert展示界面,
*/
@Override
public boolean onJsAlert(final WebView view, String url, String message,
JsResult result) {
final AlertDialog.Builder builder = new AlertDialog.Builder(
view.getContext());
builder.setTitle("對話框").setMessage(message)
.setPositiveButton("確定", null);
builder.setOnKeyListener(new OnKeyListener() {
public boolean onKey(DialogInterface dialog, int keyCode,
KeyEvent event) {
Log.v("onJsAlert", "keyCode==" + keyCode + "event=" + event);
return true;
}
});
// 禁止響應按back鍵的事件
builder.setCancelable(false);
AlertDialog dialog = builder.create();
dialog.show();
result.confirm();// 因為沒有綁定事件,需要強行confirm,否則頁面會變黑顯示不了內容。
return true;
// return super.onJsAlert(view, url, message, result);
}
/**
* 覆蓋默認的window.confirm展示界面,
*/
@Override
public boolean onJsConfirm(final WebView view, String url, String message,
final JsResult result) {
final AlertDialog.Builder builder = new AlertDialog.Builder(
view.getContext());
builder.setTitle("對話框").setMessage(message)
.setPositiveButton("確定", new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
}).setNeutralButton("取消", new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
});
builder.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
result.cancel();
}
});
// 屏蔽keycode等于84之類的按鍵,避免按鍵后導致對話框消息而頁面無法再彈出對話框的問題
builder.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode,
KeyEvent event) {
Log.v("onJsConfirm", "keyCode==" + keyCode + "event=" + event);
return true;
}
});
// 禁止響應按back鍵的事件
// builder.setCancelable(false);
AlertDialog dialog = builder.create();
dialog.show();
return true;
}
/**
* 覆蓋默認的window.prompt展示界面,
*/
@Override
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, final JsPromptResult result) {
final AlertDialog.Builder builder = new AlertDialog.Builder(
view.getContext());
builder.setTitle("對話框").setMessage(message);
final EditText et = new EditText(view.getContext());
et.setSingleLine();
et.setText(defaultValue);
builder.setView(et).setPositiveButton("確定", new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.confirm(et.getText().toString());
}
}).setNeutralButton("取消", new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
});
// 屏蔽keycode等于84之類的按鍵,避免按鍵后導致對話框消息而頁面無法再彈出對話框的問題
builder.setOnKeyListener(new OnKeyListener() {
public boolean onKey(DialogInterface dialog, int keyCode,
KeyEvent event) {
Log.v("onJsPrompt", "keyCode==" + keyCode + "event=" + event);
return true;
}
});
// 禁止響應按back鍵的事件
// builder.setCancelable(false);
AlertDialog dialog = builder.create();
dialog.show();
return true;
// return super.onJsPrompt(view, url, message, defaultValue,
// result);
}
});
- 處理JavaScript與Android代碼的交互 如果網頁中包含JavaScript,并且需要與Android代碼進行交互,可以使用WebView的addJavascriptInterface方法來實現。在Android代碼中定義一個對象,并在JavaScript中調用這個對象的方法。
編寫html文件,放到assets文件里面:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div>
function say(value){</br>
callJS(value);</br>
}
</div>
</body>
<script>
function callJS(value){
alert(value);
return value;
}
</script>
</html>
Android調用js:
public class MainActivity extends AppCompatActivity {
private WebView webview;
private TextView tvAndroid;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webview = (WebView) findViewById(R.id.webview);
tvAndroid = (TextView) findViewById(R.id.tv_android);
tvAndroid.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//Android調用js方法
//Android 4.4以下使用loadUrl,Android 4.4以上evaluateJavascript
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
webview.loadUrl("javascript:callJS('aaa')");
} else {
webview.evaluateJavascript("javascript:callJS('aaa')", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此處為 js 返回的結果
Toast.makeText(MainActivity.this,value,Toast.LENGTH_SHORT).show();
}
});
}
}
});
initWebView();
}
public void initWebView() {
//啟用JS腳本
webview.getSettings().setJavaScriptEnabled(true);
// 設置允許JS彈窗
webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
//加載網頁
webview.loadUrl("file:///android_asset/index.html");
// 由于設置了彈窗檢驗調用結果,所以需要支持js對話框
// webview只是載體,內容的渲染需要使用webviewChromClient類去實現
// 通過設置WebChromeClient對象處理JavaScript的對話框
//設置響應js 的Alert()函數
webview.setWebChromeClient(new WebChromeClient(){
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult jsResult) {
new AlertDialog.Builder(view.getContext()).setMessage(message).setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
jsResult.confirm();
}
}).setCancelable(false).create().show();
return true;
}
});
//覆蓋WebView默認使用第三方或系統默認瀏覽器打開網頁的行為,使網頁用WebView打開
webview.setWebViewClient(new WebViewClient() {
//override
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
handler.proceed("admin", "sunlight");
int d = Log.d("MyWebViewClient", "onReceivedHttpAuthRequest");
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String uri) {
// TODO Auto-generated method stub
//返回值是true的時候控制去WebView打開,為false調用系統瀏覽器或第三方瀏覽器
view.loadUrl(uri);
return true;
}
});
}
}
「注意」js代碼調用一定要在onPageFinished() 回調之后才能調用,否則不會調用。
js調用Android方法: 通過WebView的addJavascriptInterface()進行對象映射
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<button style="width:100%;height:50px; margin-top: 100px;" onclick="aa.showToast('哈哈哈')">js調用Android方法</button>
</body>
</html>
public class MainActivity2 extends AppCompatActivity {
private WebView webview;
private TextView tvAndroid;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
webview = (WebView) findViewById(R.id.webview);
tvAndroid = (TextView) findViewById(R.id.tv_android);
tvAndroid.setText("http://繼承自Object類,別名是aa,即在html可以直接用aa.showToast(\"哈哈哈\")來調用android方法\n" +
"public class MyObject extends Object {\n" +
" @JavascriptInterface\n" +
" public void showToast(String name){\n" +
" Toast.makeText(MainActivity2.this, \"您好!\"+name, Toast.LENGTH_SHORT).show();\n" +
" }\n" +
"}");
initWebView();
}
public void initWebView() {
// 設置與Js交互的權限
webview.getSettings().setJavaScriptEnabled(true);
//將java對象暴露給JavaScript腳本
//參數1:java對象,里面定義了java方法
//參數2:Java對象在js里的對象名,可以看作第一個參數的別名,可以隨便取,即在html可以直接用aa.showToast("哈哈哈")來調用android方法
webview.addJavascriptInterface(new MyObject(), "aa");//AndroidtoJS類對象映射到js的test對象
//加載網頁
webview.loadUrl("file:///android_asset/index2.html");
}
//繼承自Object類,別名是aa,即在html可以直接用aa.showToast("哈哈哈")來調用android方法
public class MyObject extends Object {
// 定義JS需要調用的方法
// 被JS調用的方法必須加入@JavascriptInterface注解
@JavascriptInterface
public void showToast(String name){
Toast.makeText(MainActivity2.this, "您好!"+name, Toast.LENGTH_SHORT).show();
}
}
}
- 頁面返回
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
webView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
- 緩存配置
WebSettings webSettings = webView.getSettings();
//優先使用緩存
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
//只在緩存中讀取
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ONLY);
/不使用緩存
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
- 清除緩存
//清除網頁訪問留下的緩存,由于內核緩存是全局的因此這個方法不僅僅針對webview而是針對整個應用程序.
webview.clearCache(true);
//清除當前webview訪問的歷史記錄,只會webview訪問歷史記錄里的所有記錄除了當前訪問記錄.
webview.clearHistory ();
//這個api僅僅清除自動完成填充的表單數據,并不會清除WebView存儲到本地的數據。
webview.clearFormData ();
注意
WebView在使用過程中存在一些常見問題「性能問題」WebView加載H5頁面時,由于JS解析過程復雜、前端頁面涉及較多的JS代碼文件,以及Android機型碎片化導致的手機硬件性能差異,可能會導致頁面加載速度較慢。每次加載H5頁面都會產生較多的網絡請求,包括HTML的主URL請求以及HTML引用的外部JS、CSS、字體文件、圖片文件等,會耗費一定的流量和時間。
「內存管理問題」WebView是依附于Activity的,而Activity的生命周期和WebView啟動的線程的生命周期可能不一致,可能導致WebView一直持有對Activity的引用而無法釋放,從而引發內存泄漏問題。WebView使用不當,可能會導致應用程序在運行過程中占用大量內存,甚至引發應用崩潰。
「安全漏洞」WebView中可能存在一些安全漏洞,如遠程代碼執行漏洞、密碼明文存儲漏洞和域控制不嚴格漏洞等。可能導致攻擊者利用WebView執行任意Java對象的方法,竊取用戶信息,甚至控制用戶設備。
「兼容性問題」不同版本的Android系統或不同品牌的手機可能存在WebView兼容性問題。例如,一些機型可能不支持WebGL,導致部分網頁內容無法正常顯示。WebView在加載某些特定格式的網頁或執行某些特定操作時也可能出現兼容性問題。