005.在WebView中獲取網頁輸入框中的數據

前言:

之前開發所有版本的APP中,WebView使用的很少,一般都是詳情頁里面沒有交互作用的頁面展示,或者是商家頁面中類似問答(同樣沒有交互)頁面的展示。現如今碰到一種需求,需要獲取WebView加載的非本地網頁中,用戶在輸入框中添加的內容。這可就fucking egg……

在百度中搜索了一堆資料,絕大部分都是本地交互的,先走一遍本地交互(html文件是本地的,或者是自己公司開發的可以商量修改的)
添加assets目錄

本地交互

  • 申請網絡權限
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
  • 造html代碼
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>本地html文件與Java代碼的互相調用</title>
    <script language="javascript">
            window.onload = function(){//html文檔結構加載完畢之后才會執行
                var submit = document.getElementById('submit');
                submit.onclick  = function(){//添加提交按鈕的監聽事件
                    var name = document.getElementById('name').value;
                    var password = document.getElementById('password').value;
                    //我想調用Android里面的代碼
                    window.androidObj.getInputValue(name,password);

                }
            }
            //我想被Android代碼調用
            function callByAndroidCode(){
                alert("callByAndroidCode");
                return 'hello js';
            }
        </script>
</head>
<body >
<form>
    <table bgColor='#ececec' cellspacing=1>
        <tr>
            <td align='center'>用戶名:</td>
            <td><input type='text' name='name' id='name' maxlength=20></td>
        </tr>
        <tr>
            <td align='center'>密碼:</td>
            <td><input type='password' name='password' id='password' ></td>
        </tr>

        <tr>
            <td><input type='button' name='submit' id='submit' value='提交'></td>
        </tr>

    </table>
</form>
</body>
</html>
  • 造布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.gome.retrofit.retrofitdemo.MainActivity">

    <Button
        android:id="@+id/callJsCodeBT"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="調用Js的代碼" />

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="50dp"></WebView>
</RelativeLayout>
  • 造Activity
    1.主要步驟是先打開setJavaScriptEnabled(true)
    2.創建一個非空類,方法名稱上要有@JavascriptInterface這樣的注解
    3.創建對象,并且往頁面中添加java對象“androidObj”,這個名稱隨意
    4.自定義WebViewClient
    5.加載資源
package com.gome.retrofit.retrofitdemo;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.JavascriptInterface;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;

public class SecondActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "SecondActivity";
    private WebView webView;
    private Button callJsCodeBT;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        callJsCodeBT = (Button) findViewById(R.id.callJsCodeBT);
        callJsCodeBT.setOnClickListener(this);
        webView = (WebView) findViewById(R.id.webView);
        webView.getSettings().setJavaScriptEnabled(true);//打開JS開關
        webView.addJavascriptInterface(new AndroidObj(), "androidObj");//往要訪問的文檔里面塞一個window下的androidObj對象
        webView.setWebViewClient(new CustomWebViewClient());//自定義一個WebViewClient,可以控制當前訪問頁面的進度,加載情況等
        webView.loadUrl("file:///android_asset/demo.html");
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.callJsCodeBT:
                webView.loadUrl("javascript:callByAndroidCode()");
                break;
        }
    }

    class AndroidObj{
        /**
         * 在js中調用該方法,放回輸入框中的數據
         * @param username
         * @param password
         */
        @JavascriptInterface//這個注解必須要聲明,不然編譯會報錯
        public void getInputValue(String username,String password){
            Log.i(TAG,"username:"+username+" password:"+password);
        }
    }
    final class CustomWebViewClient extends WebViewClient {
        @Override
        public void onPageCommitVisible(WebView view, String url) {
            Log.i("onPageCommitVisible",url);
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            return super.shouldOverrideUrlLoading(view, request);
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
        }
        //我比較喜歡在頁面加載完畢的時候搞事兒
        @Override
        public void onPageFinished(WebView view, String url) {
            //document.body.innerHTML 獲取body體里面的內容
//            view.loadUrl("javascript:window.local_obj.showSource(document.body.innerHTML);");

        }
    }

}

運行以上代碼,基本上沒啥大問題

Android4.2js調用的方法上面@JavascriptInterface 的問題

4.2之前存在重大安全隱患,因為js可以通過反射訪問注入Webview中Java對象的public fields,在一個包含不信任內容的WebView中使用這個方法,會允許攻擊者去篡改應用程序,使用宿主應用程序的權限執行Java代碼,因此4.2之后,任何JS調用的Java對象接口,都需要添加@JavascriptInterface注解,這樣的結果是
Java對象的fields屬性將不允許js來訪問
(之前還幻想著寫個Java屬性,直接從js中獲取數據賦值給Java屬性多好,哎想多了)

參考鏈接

獲取web加載頁面的HTML內容信息

在AndroidObj中添加獲取源碼的方法

//獲取整個頁面元文件
@JavascriptInterface
public void getHtmlSource(String htmlSource){
    Log.i(TAG,htmlSource);
}

然后重寫自定義的WebViewClient中的onPageFinished方法,添加如下代碼:

public void onPageFinished(WebView view, String url) {
    //document.body.innerHTML 獲取<body></body>體里面的內容
    //document.documentElement.outerHTML 獲取整個頁面的元素包括<html></html>,但是<html>標簽上面關于機構,DTD信息的注釋就沒有了
    view.loadUrl("javascript:window.androidObj.getHtmlSource(document.documentElement.outerHTML)");

}

此時隨便加載一個頁面都可以獲取該頁面的源代碼。

獲取網頁中輸入框中輸入的數據

tip:這個網頁不是本地的,也不是自己公司前端寫的
如果需求是這樣,用戶點擊登錄按鈕的時候,獲取用戶輸入的登錄信息。
這個時候就需要用電腦瀏覽器打開這個頁面,查看用戶在登錄的時候,調用了什么js方法。

  • 看看能不能自己重寫一下這個js方法,如果可以,直接重寫。
  • 看看能不能在中間的某個js方法中添加我需要獲取數據的代碼
  • ……各種方法都不行
  • 最后拿出3板斧,重寫整個頁面。
    舉個例子:
    目前我碰到的問題就是當提交用戶數據的時候,執行到最后,調用了“before_submit”和do_encrypt方法,他們的執行順序是這樣的
    before_submit();do_encrypt();
    當時在源文件里面是有do_encrypt這個方法的(一看方法名稱就知道是對用戶傳遞的數據進行加密操作),過兩天后發現do_encrypt方法已經不存在了,最后我只能在before_submit方法中獲取最原始的用戶輸入操作。
    我的操作步驟如下
    1.在onPageFinished中獲取網頁源代碼

@Override
public void onPageFinished(WebView view, String url) {
    if(!isDown){
        isDown = false;
        view.loadUrl("javascript:window.androidObj.showSource(document.documentElement.outerHTML);");
    }
    //獲取當前WebView保存的cookie信息
    CookieManager cookieManager = CookieManager.getInstance();
    String cookie = cookieManager.getCookie("http://www.bjgjj.gov.cn");
    Log.i("onPageFinished",cookie);

}

2.在注入的java對象的類中添加接收網頁源碼的方法,修改源碼,然后進行重新加載

@JavascriptInterface
public void showSource(String html) {
    StringBuilder sb = new StringBuilder(html);
    char zuo = 0x7B;//{花括號
    String replaceString = "before_submit()"+zuo ;
    int index = sb.indexOf(replaceString);
    if (index>-1){
        //下面是要新增的js代碼,用來獲取網頁中用戶在id=lb id=bh1 bh2 bh3 bh4 bh5 mm1 mm2 mm3……中輸入的數據
        String newString = "var type = $(\"#lb\").val();var bh1 = $(\"#bh1\").val();var mm1 = $(\"#mm1\").val();var bh2 = $(\"#bh2\").val();var mm2 = $(\"#mm2\").val();var bh3 = $(\"#bh3\").val();var mm3 = $(\"#mm3\").val();var bh4 = $(\"#bh4\").val();var mm4 = $(\"#mm4\").val();var bh5 = $(\"#bh5\").val();var mm5 = $(\"#mm5\").val();window.androidObj.getInputValue(type,bh1,mm1,bh2,mm2,bh3,mm3,bh4,mm4,bh5,mm5);";
        sb.insert(index+replaceString.length(),newString);
        System.out.println(sb.toString());
        //在handler中重新加載sb中的數據
        Message message = Message.obtain();
        message.what = 1;
        message.obj = sb.toString();
        handler.sendMessage(message);
    }
}
@JavascriptInterface
public void getInputValue(String type,String bh1,String mm1,String bh2,String mm2,String bh3,String mm3,String bh4,String mm4,String bh5,String mm5){
    Log.i("type:",type);
    Log.i("bh5:",bh5);
    Log.i("bh4:",bh4);
    Log.i("bh3:",bh3);
    Log.i("bh2:",bh2);
    Log.i("bh1:",bh1);
    Log.i("mm1:",mm1);
    Log.i("mm2:",mm2);
    Log.i("mm3:",mm3);
    Log.i("mm4:",mm4);
    Log.i("mm5:",mm5);
}

  1. 在Handler中重新加載
private final Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what){
            case 1:
                webView.loadDataWithBaseURL("baseURL當前頁面的上級目錄",msg.obj.toString(),"text/html; charset=utf-8",null,null);
                break;
        }
    }
};

第三個參數和第四個參數最好連寫在一塊,單獨寫會出現亂碼問題

用戶只要一點擊登錄按鈕,就會觸發我注入androidObj對象中的getInputValue方法,從此方法中獲取我想要獲取的數據

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容