前言
在 Android
開發(fā)過程中古话,我們偶爾會涉及到WebView
的url 攔截問題少态。那么今天就讓我們來詳細講講WebView
的攔截與返回。
今天涉及的內(nèi)容:
- WebView 加載原理
- 攔截之
shouldOverrideUrlLoading
的使用 - 攔截之
shouldInterceptRequest
的使用 - 需要注意的點
- 返回鍵攔截
- 項目結(jié)構(gòu)圖和效果圖
-
WebViewClientInterceptor
源碼
先來波效果圖
一.WebView加載原理
WebView
加載原理從 WebView
設(shè)置url
開始,然后請求響應(yīng)實體孝治,最后將結(jié)果顯示到ui屏幕上。
知道了大致原理活玲,然后在攔截的時候瞒津,可以從兩個方面著手:
- 第一個是在設(shè)置
url
時修改url - 第二個是在響應(yīng)實體替換實體
這樣就可以達到攔截webview加載的功能了。要在url
加載期攔截来氧,則可以重寫WebViewClient
的shouldOverrideUrlLoading
方法诫给。如果要在響應(yīng)實體階段攔截,可以重寫WebViewClient
的shouldInterceptRequest
方法饲漾。接下來讓我們具體講講這兩個方法之于攔截的使用吧蝙搔。
二.攔截之shouldOverrideUrlLoading的使用
shouldOverrideUrlLoading
是在webView
加載url
階段執(zhí)行攔截的。我繼承WebViewClient
封裝了一個WebViewClientInterceptor
類考传,用于在webview使用過程中執(zhí)行攔截功能〕孕停現(xiàn)在看看WebViewClientInterceptor
攔截url
在Activity
中的使用。
下面貼出MainActivity
代碼:
package com.otherdemo;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.util.LogUtil;
import com.util.ToastUtil;
public class MainActivity extends AppCompatActivity {
private TextView mTv;
private Button mBtn;
private WebView mWebView;
private WebViewClientInterceptor mWebViewClientInterceptor;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LogUtil.setDebug(true);
initView();
initData();
setListener();
}
private void initView() {
mTv = findViewById(R.id.tv);
mBtn = findViewById(R.id.btn);
mWebView = findViewById(R.id.web_view);
}
private void initData() {
mWebViewClientInterceptor = new WebViewClientInterceptor();
//webview基礎(chǔ)設(shè)置
mWebViewClientInterceptor.setWebViewConfig(mWebView, MainActivity.this);
mWebViewClientInterceptor.setOnOverrideUrlListener(new WebViewClientInterceptor.OnOverrideUrlListener() {
@Override
public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest request) {
ToastUtil.shortShow("這是攔截url的操作,原url="+webView.getUrl());
return true;
}
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
ToastUtil.shortShow("這是攔截url的操作,原url="+webView.getUrl());
return true;
}
});
//設(shè)置WebViewClient向一個網(wǎng)頁發(fā)送請求僚楞,可以返回文本勤晚,文件等
mWebView.setWebViewClient(mWebViewClientInterceptor);
//設(shè)置可讓界面彈出alert等提示框
mWebView.setWebChromeClient(new WebChromeClient());
mWebView.loadUrl("file:///android_asset/test.html");
}
private void setListener() {
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
@Override
protected void onDestroy() {
//清理webview相關(guān)配置
mWebViewClientInterceptor.destoryWebViewConfig(mWebView, MainActivity.this);
super.onDestroy();
}
}
接著貼出test.html
代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html" charset="UTF-8">
<!--手機高寬度撐滿屏幕-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS交互</title>
</head>
<body>
<p><input type="button" id="enter" value="js調(diào)用Android(調(diào)用默認方法)" onclick="alert('hello')" /></p>
<p><input type="button" id="enter1" value="js調(diào)用Android(調(diào)用自定義tag的方法)" onclick="testBaidu()" /></p>
<p id="show"></p>
</body>
<script type="text/javascript">
function testBaidu(){
window.location.
}
</script>
</html>
下面貼出效果圖:
三.攔截之shouldInterceptRequest的使用
還是以封裝類WebViewClientInterceptor
為例,shouldInterceptRequest
是在響應(yīng)實體階段攔截webview
加載的實例泉褐。
其攔截原理是在響應(yīng)階段攔截下html
數(shù)據(jù)赐写,然后用本地Html
或網(wǎng)絡(luò)獲取的html
進行替換,重新加載膜赃。
3.1 本地Html替換加載
下面貼出在MainActivity
中加載本地Html
的代碼:
package com.otherdemo;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.util.LogUtil;
import java.io.InputStream;
public class MainActivity extends AppCompatActivity {
private TextView mTv;
private Button mBtn;
private WebView mWebView;
private WebViewClientInterceptor mWebViewClientInterceptor;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LogUtil.setDebug(true);
initView();
initData();
setListener();
}
private void initView() {
mTv = findViewById(R.id.tv);
mBtn = findViewById(R.id.btn);
mWebView = findViewById(R.id.web_view);
}
private void initData() {
LogUtil.i("=========開始加載接收打擊========");
mWebViewClientInterceptor = new WebViewClientInterceptor();
//webview基礎(chǔ)設(shè)置
mWebViewClientInterceptor.setWebViewConfig(mWebView, MainActivity.this);
mWebViewClientInterceptor.setOnInterceptorListener(new WebViewClientInterceptor.OnInterceptorListener() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
String htmlPage = "<html>\n" +
"<title>千度</title>\n" +
"<body>\n" +
"<a href=\"www.taobao.com\">千度</a>,比百度知道的多10倍\n" +
"</body>\n" +
"<html>";
InputStream inputStream = mWebViewClientInterceptor.getLocalHtmlPageStream(htmlPage, null);
WebResourceResponse response = mWebViewClientInterceptor.getWebResourceResponse(inputStream, WebViewClientInterceptor.UTF_8);
return response;
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
String htmlPage = "<html>\n" +
"<title>千度</title>\n" +
"<body>\n" +
"<a href=\"www.taobao.com\">千度</a>,比百度知道的多10倍\n" +
"</body>\n" +
"<html>";
InputStream inputStream = mWebViewClientInterceptor.getLocalHtmlPageStream(htmlPage, null);
WebResourceResponse response = mWebViewClientInterceptor.getWebResourceResponse(inputStream, WebViewClientInterceptor.UTF_8);
return response;
}
});
//設(shè)置WebViewClient向一個網(wǎng)頁發(fā)送請求挺邀,可以返回文本,文件等
mWebView.setWebViewClient(mWebViewClientInterceptor);
//設(shè)置可讓界面彈出alert等提示框
mWebView.setWebChromeClient(new WebChromeClient());
mWebView.loadUrl("https://www.baidu.com/");
}
private void setListener() {
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
@Override
protected void onDestroy() {
//清理webview相關(guān)配置
mWebViewClientInterceptor.destoryWebViewConfig(mWebView, MainActivity.this);
super.onDestroy();
}
}
下面貼出效果圖
3.2 網(wǎng)絡(luò)Html替換加載
這個主要是之前的
String htmlPage = "<html>\n" +
"<title>千度</title>\n" +
"<body>\n" +
"<a href=\"www.taobao.com\">千度</a>,比百度知道的多10倍\n" +
"</body>\n" +
"<html>";
代碼用網(wǎng)絡(luò)請求的結(jié)果跳座,然后利用WebResourceResponse
替換加載即可端铛。
現(xiàn)在給出網(wǎng)絡(luò)加載替換在MainActivity
中的使用:
package com.otherdemo;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.util.LogUtil;
import java.io.InputStream;
public class MainActivity extends AppCompatActivity {
private TextView mTv;
private Button mBtn;
private WebView mWebView;
private WebViewClientInterceptor mWebViewClientInterceptor;
private String mTempResult;
private long mLastTime;
public String getmTempResult() {
return mTempResult;
}
public void setmTempResult(String mTempResult) {
this.mTempResult = mTempResult;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LogUtil.setDebug(true);
initView();
initData();
setListener();
}
private void initView() {
mTv = findViewById(R.id.tv);
mBtn = findViewById(R.id.btn);
mWebView = findViewById(R.id.web_view);
}
private void initData() {
mWebViewClientInterceptor = new WebViewClientInterceptor();
//webview基礎(chǔ)設(shè)置
mWebViewClientInterceptor.setWebViewConfig(mWebView, MainActivity.this);
new AsyncTask<Void, Void, String>() {
@Override
protected String doInBackground(Void... voids) {
return mWebViewClientInterceptor.getDataByUrl("https://github.com/");
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
setmTempResult(s);
}
}.execute();
mWebViewClientInterceptor.setOnInterceptorListener(new WebViewClientInterceptor.OnInterceptorListener() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
mLastTime=System.currentTimeMillis();
while (true) {
LogUtil.i("============等待=========");
if (getmTempResult() != null||(System.currentTimeMillis()-mLastTime)>1000*10) {
break;
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(getmTempResult()!=null) {
String temp=getmTempResult();
if(temp.contains("utf-8")){
temp=temp.replaceAll("utf-8","");
}
InputStream inputStream = mWebViewClientInterceptor.getLocalHtmlPageStream(temp, null);
WebResourceResponse response = mWebViewClientInterceptor.getWebResourceResponse(inputStream, WebViewClientInterceptor.UTF_8);
return response;
}
return null;
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
mLastTime=System.currentTimeMillis();
while (true) {
LogUtil.i("============等待=========");
if (getmTempResult() != null||(System.currentTimeMillis()-mLastTime)>1000*10) {
break;
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(getmTempResult()!=null) {
String temp=getmTempResult();
if(temp.contains("utf-8")){
temp=temp.replaceAll("utf-8","");
}
InputStream inputStream = mWebViewClientInterceptor.getLocalHtmlPageStream(temp, null);
WebResourceResponse response = mWebViewClientInterceptor.getWebResourceResponse(inputStream, WebViewClientInterceptor.UTF_8);
return response;
}
return null;
}
});
//設(shè)置WebViewClient向一個網(wǎng)頁發(fā)送請求,可以返回文本疲眷,文件等
mWebView.setWebViewClient(mWebViewClientInterceptor);
//設(shè)置可讓界面彈出alert等提示框
mWebView.setWebChromeClient(new WebChromeClient());
mWebView.loadUrl("https://www.baidu.com/");
}
private void setListener() {
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
@Override
protected void onDestroy() {
//清理webview相關(guān)配置
mWebViewClientInterceptor.destoryWebViewConfig(mWebView, MainActivity.this);
super.onDestroy();
}
}
下面給出效果圖
這里我這白你顯示成了
html
源碼了禾蚕,也不知道啥原因,有知道的大大可以指點一二狂丝。由于這個不是重點换淆,本文就暫不討論。
四.需要注意的點
shouldOverrideUrlLoading
和shouldInterceptRequest
都可以做webView
加載網(wǎng)頁的攔截几颜,但是他們攔截的主體不一樣:
-
shouldOverrideUrlLoading
攔截的是url
加載階段 -
shouldInterceptRequest
加載的是響應(yīng)主體階段
攔截的內(nèi)容不一樣:
-
shouldOverrideUrlLoading
主要攔截url
-
shouldInterceptRequest
可攔截url
,js
倍试,css
等
也就是說整體而言shouldInterceptRequest
攔截的范圍比shouldOverrideUrlLoading
廣。但是shouldOverrideUrlLoading
能響應(yīng)本地html
文件加載菠剩,如assets
文件夾下的html
加載易猫,而shouldInterceptRequest
只能響應(yīng)url
之類的,而不響應(yīng)本地文件加載具壮。
還有一個需要注意的是准颓,shouldOverrideUrlLoading
的攔截處在shouldInterceptRequest
上游(由webView
加載原理決定)哈蝇,所以在shouldInterceptRequest
攔截的時候,我們一般不重寫shouldOverrideUrlLoading
攘已,這是為了保證shouldOverrideUrlLoading
方法返回為false
炮赦,若shouldOverrideUrlLoading
方法返回為true
,則表示"上游"已經(jīng)攔截了样勃,那這時再在shouldInterceptRequest
進行攔截已經(jīng)不起作用了吠勘。
五.返回鍵攔截
WebView
關(guān)于返回鍵的攔截也是比較常用的操作,一般是重寫Activity
的onKeyDown(int keyCode, KeyEvent event)
方法峡眶,下面貼出主要代碼:
public class MainActivity extends AppCompatActivity {
//其他代碼省略
//......
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
//按返回鍵操作并且能回退網(wǎng)頁
if (keyCode == KeyEvent.KEYCODE_BACK && !mWebView.canGoBack()) {
//關(guān)閉界面
ToastUtil.shortShow("關(guān)閉界面");
return true;
}
}
return super.onKeyDown(keyCode,event);
}
}
六. 項目結(jié)構(gòu)圖和效果圖
七. WebViewClientInterceptor源碼
下面貼出WebViewClientInterceptor
源碼: