前言
在 Android
开发过程中,我们偶尔会涉及到WebView
的url 拦截问题。那么今天就让我们来详细讲讲WebView
的拦截与返回。
今天涉及的内容:
- WebView 加载原理
- 拦截之
shouldOverrideUrlLoading
的使用 - 拦截之
shouldInterceptRequest
的使用 - 需要注意的点
- 返回键拦截
- 项目结构图和效果图
-
WebViewClientInterceptor
源码
先来波效果图
一.WebView加载原理
WebView
加载原理从 WebView
设置url
开始,然后请求响应实体,最后将结果显示到ui屏幕上。
知道了大致原理,然后在拦截的时候,可以从两个方面着手:
- 第一个是在设置
url
时修改url - 第二个是在响应实体替换实体
这样就可以达到拦截webview加载的功能了。要在url
加载期拦截,则可以重写WebViewClient
的shouldOverrideUrlLoading
方法。如果要在响应实体阶段拦截,可以重写WebViewClient
的shouldInterceptRequest
方法。接下来让我们具体讲讲这两个方法之于拦截的使用吧。
二.拦截之shouldOverrideUrlLoading的使用
shouldOverrideUrlLoading
是在webView
加载url
阶段执行拦截的。我继承WebViewClient
封装了一个WebViewClientInterceptor
类,用于在webview使用过程中执行拦截功能。现在看看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基础设置
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;
}
});
//设置WebViewClient向一个网页发送请求,可以返回文本,文件等
mWebView.setWebViewClient(mWebViewClientInterceptor);
//设置可让界面弹出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相关配置
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调用Android(调用默认方法)" onclick="alert('hello')" /></p>
<p><input type="button" id="enter1" value="js调用Android(调用自定义tag的方法)" onclick="testBaidu()" /></p>
<p id="show"></p>
</body>
<script type="text/javascript">
function testBaidu(){
window.location.
}
</script>
</html>
下面贴出效果图:
三.拦截之shouldInterceptRequest的使用
还是以封装类WebViewClientInterceptor
为例,shouldInterceptRequest
是在响应实体阶段拦截webview
加载的实例。
其拦截原理是在响应阶段拦截下html
数据,然后用本地Html
或网络获取的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基础设置
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;
}
});
//设置WebViewClient向一个网页发送请求,可以返回文本,文件等
mWebView.setWebViewClient(mWebViewClientInterceptor);
//设置可让界面弹出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相关配置
mWebViewClientInterceptor.destoryWebViewConfig(mWebView, MainActivity.this);
super.onDestroy();
}
}
下面贴出效果图
3.2 网络Html替换加载
这个主要是之前的
String htmlPage = "<html>\n" +
"<title>千度</title>\n" +
"<body>\n" +
"<a href=\"www.taobao.com\">千度</a>,比百度知道的多10倍\n" +
"</body>\n" +
"<html>";
代码用网络请求的结果,然后利用WebResourceResponse
替换加载即可。
现在给出网络加载替换在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基础设置
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;
}
});
//设置WebViewClient向一个网页发送请求,可以返回文本,文件等
mWebView.setWebViewClient(mWebViewClientInterceptor);
//设置可让界面弹出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相关配置
mWebViewClientInterceptor.destoryWebViewConfig(mWebView, MainActivity.this);
super.onDestroy();
}
}
下面给出效果图
这里我这白你显示成了
html
源码了,也不知道啥原因,有知道的大大可以指点一二。由于这个不是重点,本文就暂不讨论。
四.需要注意的点
shouldOverrideUrlLoading
和shouldInterceptRequest
都可以做webView
加载网页的拦截,但是他们拦截的主体不一样:
-
shouldOverrideUrlLoading
拦截的是url
加载阶段 -
shouldInterceptRequest
加载的是响应主体阶段
拦截的内容不一样:
-
shouldOverrideUrlLoading
主要拦截url
-
shouldInterceptRequest
可拦截url
,js
,css
等
也就是说整体而言shouldInterceptRequest
拦截的范围比shouldOverrideUrlLoading
广。但是shouldOverrideUrlLoading
能响应本地html
文件加载,如assets
文件夹下的html
加载,而shouldInterceptRequest
只能响应url
之类的,而不响应本地文件加载。
还有一个需要注意的是,shouldOverrideUrlLoading
的拦截处在shouldInterceptRequest
上游(由webView
加载原理决定),所以在shouldInterceptRequest
拦截的时候,我们一般不重写shouldOverrideUrlLoading
,这是为了保证shouldOverrideUrlLoading
方法返回为false
,若shouldOverrideUrlLoading
方法返回为true
,则表示"上游"已经拦截了,那这时再在shouldInterceptRequest
进行拦截已经不起作用了。
五.返回键拦截
WebView
关于返回键的拦截也是比较常用的操作,一般是重写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) {
//按返回键操作并且能回退网页
if (keyCode == KeyEvent.KEYCODE_BACK && !mWebView.canGoBack()) {
//关闭界面
ToastUtil.shortShow("关闭界面");
return true;
}
}
return super.onKeyDown(keyCode,event);
}
}
六. 项目结构图和效果图
七. WebViewClientInterceptor源码
下面贴出WebViewClientInterceptor
源码: