WebView拦截url

前言

Android开发过程中,我们偶尔会涉及到WebView的url 拦截问题。那么今天就让我们来详细讲讲WebView的拦截与返回。

今天涉及的内容:

  1. WebView 加载原理
  2. 拦截之shouldOverrideUrlLoading的使用
  3. 拦截之shouldInterceptRequest的使用
  4. 需要注意的点
  5. 返回键拦截
  6. 项目结构图和效果图
  7. WebViewClientInterceptor源码

先来波效果图


拦截url效果图.gif

本地html替换效果图.png

网络html替换效果图.png

一.WebView加载原理

WebView加载原理从 WebView设置url开始,然后请求响应实体,最后将结果显示到ui屏幕上。
知道了大致原理,然后在拦截的时候,可以从两个方面着手:

  • 第一个是在设置url时修改url
  • 第二个是在响应实体替换实体

这样就可以达到拦截webview加载的功能了。要在url加载期拦截,则可以重写WebViewClientshouldOverrideUrlLoading方法。如果要在响应实体阶段拦截,可以重写WebViewClientshouldInterceptRequest方法。接下来让我们具体讲讲这两个方法之于拦截的使用吧。

二.拦截之shouldOverrideUrlLoading的使用

shouldOverrideUrlLoading是在webView加载url阶段执行拦截的。我继承WebViewClient封装了一个WebViewClientInterceptor类,用于在webview使用过程中执行拦截功能。现在看看WebViewClientInterceptor拦截urlActivity中的使用。
下面贴出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>

下面贴出效果图:


1.gif

三.拦截之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();
    }
}

下面贴出效果图


image.png

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();
    }
}

下面给出效果图

image.png

这里我这白你显示成了html源码了,也不知道啥原因,有知道的大大可以指点一二。由于这个不是重点,本文就暂不讨论。

四.需要注意的点

shouldOverrideUrlLoadingshouldInterceptRequest都可以做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关于返回键的拦截也是比较常用的操作,一般是重写ActivityonKeyDown(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);
    }
}

六. 项目结构图和效果图

项目结构图.png

拦截url效果图.gif

本地html替换效果图.png

网络html替换效果图.png

七. WebViewClientInterceptor源码

下面贴出WebViewClientInterceptor源码:

还有 24% 的精彩内容
?著作权归作者所有,转载或内容合作请联系作者
支付 ¥3.00 继续阅读
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351