WebView攔截url

前言

Android開發(fā)過程中古话,我們偶爾會涉及到WebView的url 攔截問題少态。那么今天就讓我們來詳細講講WebView的攔截與返回。

今天涉及的內(nèi)容:

  1. WebView 加載原理
  2. 攔截之shouldOverrideUrlLoading的使用
  3. 攔截之shouldInterceptRequest的使用
  4. 需要注意的點
  5. 返回鍵攔截
  6. 項目結(jié)構(gòu)圖和效果圖
  7. WebViewClientInterceptor源碼

先來波效果圖


攔截url效果圖.gif

本地html替換效果圖.png

網(wǎng)絡(luò)html替換效果圖.png

一.WebView加載原理

WebView加載原理從 WebView設(shè)置url開始,然后請求響應(yīng)實體孝治,最后將結(jié)果顯示到ui屏幕上。
知道了大致原理活玲,然后在攔截的時候瞒津,可以從兩個方面著手:

  • 第一個是在設(shè)置url時修改url
  • 第二個是在響應(yīng)實體替換實體

這樣就可以達到攔截webview加載的功能了。要在url加載期攔截来氧,則可以重寫WebViewClientshouldOverrideUrlLoading方法诫给。如果要在響應(yīng)實體階段攔截,可以重寫WebViewClientshouldInterceptRequest方法饲漾。接下來讓我們具體講講這兩個方法之于攔截的使用吧蝙搔。

二.攔截之shouldOverrideUrlLoading的使用

shouldOverrideUrlLoading是在webView加載url階段執(zhí)行攔截的。我繼承WebViewClient封裝了一個WebViewClientInterceptor類考传,用于在webview使用過程中執(zhí)行攔截功能〕孕停現(xiàn)在看看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基礎(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>

下面貼出效果圖:


1.gif

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

下面貼出效果圖


image.png

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

下面給出效果圖

image.png

這里我這白你顯示成了html源碼了禾蚕,也不知道啥原因,有知道的大大可以指點一二狂丝。由于這個不是重點换淆,本文就暫不討論。

四.需要注意的點

shouldOverrideUrlLoadingshouldInterceptRequest都可以做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)于返回鍵的攔截也是比較常用的操作,一般是重寫ActivityonKeyDown(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)圖和效果圖

項目結(jié)構(gòu)圖.png

攔截url效果圖.gif

本地html替換效果圖.png

網(wǎng)絡(luò)html替換效果圖.png

七. WebViewClientInterceptor源碼

下面貼出WebViewClientInterceptor源碼:

還有 24% 的精彩內(nèi)容
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
支付 ¥3.00 繼續(xù)閱讀
  • 序言:七十年代末剧防,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子辫樱,更是在濱河造成了極大的恐慌峭拘,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狮暑,死亡現(xiàn)場離奇詭異鸡挠,居然都是意外死亡,警方通過查閱死者的電腦和手機搬男,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門拣展,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缔逛,你說我怎么就攤上這事备埃。” “怎么了褐奴?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵瓜喇,是天一觀的道長。 經(jīng)常有香客問我歉糜,道長,這世上最難降的妖魔是什么望众? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任匪补,我火速辦了婚禮,結(jié)果婚禮上烂翰,老公的妹妹穿的比我還像新娘夯缺。我一直安慰自己,他們只是感情好甘耿,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布踊兜。 她就那樣靜靜地躺著,像睡著了一般佳恬。 火紅的嫁衣襯著肌膚如雪捏境。 梳的紋絲不亂的頭發(fā)上于游,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機與錄音垫言,去河邊找鬼贰剥。 笑死,一個胖子當(dāng)著我的面吹牛筷频,可吹牛的內(nèi)容都是我干的蚌成。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼凛捏,長吁一口氣:“原來是場噩夢啊……” “哼担忧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起坯癣,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤瓶盛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后坡锡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蓬网,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年鹉勒,在試婚紗的時候發(fā)現(xiàn)自己被綠了帆锋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡禽额,死狀恐怖锯厢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情脯倒,我是刑警寧澤实辑,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站藻丢,受9級特大地震影響剪撬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜悠反,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一残黑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斋否,春花似錦梨水、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春奇徒,著一層夾襖步出監(jiān)牢的瞬間雏亚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工逼龟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留评凝,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓腺律,卻偏偏與公主長得像奕短,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子匀钧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355