Android WebView通過動態(tài)的修改js去攔截post請求參數(shù)實例
需求背景:
需要在用戶點擊提交按鈕的時候攔截用戶提交的數(shù)據(jù)。
遇到的問題:
1.頁面不是自家前端做的,不能修改網(wǎng)頁中的代碼
2.要攔截的請求不是get請求,而是一個post請求 (難點在于:如果攔截的請求是get請求的話,我只需要拿到url,將后面拼接的參數(shù)鍵值對取出來就好了,但是post請求的參數(shù)鍵值對我們是看不到的。。。)
解決重點:
重寫webViewClient的shouldInterceptRequest這個方法
1.這個方法是API21以后才出現(xiàn)的,還有一個過時的方法也要重寫,不要忘了!
2.在加載網(wǎng)頁時,所有的資源都會經(jīng)過shouldInterceptRequest這個方法,我們可以通過shouldInterceptRequest和抓包工具(Fidder,Charles)去獲取你想要獲取信息的網(wǎng)址和資源文件
3.這個方法是執(zhí)行在子線程的,如果你想要更新UI的話,記得切換線程
解決方案:
我這里找到了兩種解決方案(總有一款適合你)
方案A : 適合 精通js 的大大們
1.攔截頁面上按鈕的點擊事件,將點擊事件的操作進行替換
$('#J_submit').off('click'); //1.將id為J_submit的按鈕點擊事件關(guān)閉
$('#J_submit').on('click',function(){ //2.將id為J_submit的按鈕點擊事件重新打開,并執(zhí)行function里的內(nèi)容
if ($(this).hasClass("btn-disabled")) { // ----- 此處為原頁面代碼,不做解釋 -----
return;
}
try {
trackDealerEvent('dlr_order_page_form_submit_click', {
'esfrom': _mediaId,
'business': 'songshu',
'series': _seriesId,
'city': _cityId
});
} catch (e) {
console.log(e);
} // ----- 此處為原頁面代碼,不做解釋 -----
var pageFormData = validateAllField(alertDiv);
if (pageFormData) { //3.獲取到頁面內(nèi)的數(shù)據(jù)
$.ajax({ //4.ajax方式上傳到服務(wù)器中
url: 'https://gouche.jxedt.com/gouche/clue/submit',
data: {
cityid: _cityId,
brandid: _brandId,
seriesid: _seriesId,
classesid: _specId,
name: $("[name='userName']").val(),
phone: $('#phoneNumber').val(),
type: 4
}
});
postOrder(pageFormData);
}
})
2.動態(tài)的加載一段js代碼
mCommonWebView.setCommonWebViewClient(new CommonWebViewClient() { //添加自定義的 WebViewClient
@Override
public void onPageFinished(WebView view, String url) { //重寫onPageFinished方法
super.onPageFinished(view, url);
//請求js的網(wǎng)址
runRemoteJs(Constant.QueryCarPrice.loadJsUrl_CarHome);
}
private void runJs(String remoteJs){ //把獲取到的js代碼添加到當前網(wǎng)頁
if(TextUtils.isEmpty(remoteJs)) {
return;
}
String js = "javascript:"; //作用:指明字符串后面的都是js代碼
js+= "var script = document.createElement('script');"; // 作用:創(chuàng)建script節(jié)點
js+= "script.type = 'text/javascript';";
js+=remoteJs;
mCommonWebView.callJsFunction(js); //加載js代碼
}
private void runRemoteJs(String url) {//前端大大提供的一個網(wǎng)址,網(wǎng)址里面就是上面的js代碼,將網(wǎng)頁中的代碼獲取下來
RxRequest<String> request = new RxRequest<String>()
.setUrl(url)
.setMethod(Request.Method.GET);
RxHttpEngineWrapper.commonExec(request)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new UtilsRx.DefaultSubscriber<String>(){
@Override
public void onNext(String s) {
runJs(s);
}
});
}
});
3.到時候只要前端的大大修改頁面中的js就可以了
此方案的坑:
1.要加載的js代碼中不能包含script節(jié)點
2.要加載的js代碼中不能有注釋
3.要加載的js代碼一定要加上分號
*如果不滿足上面的三點要求,要加載的js都不能正確的執(zhí)行
方案B : 原生的Android方式,相對于上一種方案,這種方案比較麻煩
1.重寫shouldInterceptRequest去攔截資源
2.將第三方網(wǎng)頁上進行網(wǎng)絡(luò)請求的js頁面下載下來(就是把網(wǎng)頁的所有下載下來,找到進行網(wǎng)絡(luò)請求的js頁),對js頁進行修改
3.將處理好的js頁加載到本地,以后加載時就利用本地的js替換第三方的js(我會在本地的js頁面中添加與webview溝通的橋梁)
//以下為具體操作,我把具體的方法貼了上去,如果不太懂的可以看看代碼,我寫了注釋
//初始化WebView
private void initWebView() {
mWebView.getSettings().setDomStorageEnabled(true);
mWebView.getSettings().setDefaultTextEncodingName("utf-8");
if(Build.VERSION.SDK_INT >=21){//Added in API level 21
mWebView.getSettings().setMixedContentMode(android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setUseWideViewPort(true); //設(shè)置webview推薦使用的窗口,使html界面自適應屏幕
mWebView.getSettings().setLoadWithOverviewMode(true);
mWebView.getSettings().setGeolocationEnabled(true);
mWebView.getSettings().setAllowFileAccess(true);
if (Build.VERSION.SDK_INT >= 16) {
//屏蔽Webview的跨域漏洞
mWebView.getSettings().setAllowFileAccessFromFileURLs(false);
mWebView.getSettings().setAllowUniversalAccessFromFileURLs(false);
}
mWebView.getSettings().setPluginState(WebSettings.PluginState.ON);
if (Build.VERSION.SDK_INT >= 11) {
mWebView.getSettings().setAllowContentAccess(true);
}
mWebView.loadUrl(currUrl);
mWebView.setWebViewClient(new MyWebViewClient());
//與js通訊的橋梁
mWebView.addJavascriptInterface(new StubClass(),"stub");
}
public class MyWebViewClient extends WebViewClient {
/*兩個shouldInterceptRequest方法體中的內(nèi)容大致相同,因為是demo,我也沒有抽取方法*/
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
//獲取的請求參數(shù)的 Map 集合
HashMap<String,String> params;
Uri uri=Uri.parse(url); //獲取網(wǎng)址對應的Uri
if (rightUrl(uri.toString())) {
/*get請求獲取參數(shù)*/
params=paramForGET(uri);
/*重頭戲,post請求獲取參數(shù)*/
/*
* 獲取post請求參數(shù)的思路就是:
* 找到其網(wǎng)址中進行網(wǎng)絡(luò)請求的js代碼,對這段js代碼進行替換
* 我采取的是攔截第三方網(wǎng)址上請求數(shù)據(jù)的js資源,將本地的資源提交上去替換原資源
*/
if (uri.toString().contains("index.js")) { //攔截該網(wǎng)頁下對應的js資源并進行替換
try {
//WebResourceResponse的構(gòu)造器三個參數(shù)作用 String mimeType:指定替換資源的類型 String encoding:字符集 InputStream input:輸入流
return new WebResourceResponse("application/x-javascript","UTF-8",getAssets().open("index.js"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
return super.shouldInterceptRequest(view, url);
}
//API21及21以后才支持此方法
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
//獲取的請求參數(shù)的 Map 集合
HashMap<String,String> params;
String method=request.getMethod(); //當前網(wǎng)址的提交方式
Map<String, String> requestHeaders = request.getRequestHeaders(); //獲取請求頭
Uri uri=request.getUrl(); //獲取網(wǎng)址對應的Uri
if (rightUrl(uri.toString())) {
/*get請求獲取參數(shù)*/
params=paramForGET(uri);
/*重頭戲,post請求獲取參數(shù)*/
/*
* 獲取post請求參數(shù)的思路就是:
* 找到其網(wǎng)址中進行網(wǎng)絡(luò)請求的js代碼,對這段js代碼進行替換
* 我采取的是攔截第三方網(wǎng)址上請求數(shù)據(jù)的js資源,將本地的資源提交上去替換原資源
*/
if (uri.toString().contains("index.js")) { //攔截該網(wǎng)頁下對應的js資源并進行替換
try {
//WebResourceResponse的構(gòu)造器三個參數(shù)作用 String mimeType:指定替換資源的類型 String encoding:字符集 InputStream input:輸入流
return new WebResourceResponse("application/x-javascript","UTF-8",getAssets().open("index.js"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
return super.shouldInterceptRequest(view, request);
}
private boolean rightUrl(String url){
if (url.contains(COLLECT_URL)) //判斷資源網(wǎng)址是否是我需要的
return true;
return false;
}
private HashMap<String,String> paramForGET(Uri uri){
HashMap<String,String> params=new HashMap<>();
Set<String> paramNames = uri.getQueryParameterNames(); //獲取此get請求中所有的參數(shù)名
/*我這里是將所有的參數(shù)都填了進去,大家在獲取的時候可以進行篩選和過濾*/
for (String param : paramNames) {
params.put(param,uri.getQueryParameter(param)); //存儲鍵值對
}
return params;
}
}
public class StubClass{
@JavascriptInterface
public void getData(String json){
Log.i("xxx","json -> "+json);
}
}
這是我本地的js,對原來的js進行了修改,添加了與Android通訊的橋梁,來截取數(shù)據(jù)。
補充知識:android WebView使用Post請求和設(shè)置瀏覽器彈框
這里要注意:post請求參數(shù)只能傳byte數(shù)組,而且必須是鍵值對字符串形式的byte數(shù)組,其中的key是后臺服務(wù)器接收key,后臺規(guī)定key是什么值就是什么值,不能隨意更改,沒有key=value格式或者key不正確,都會請求不到數(shù)據(jù)網(wǎng)頁打不開。
下面代碼直接看initWebView()方法就好
package com.xxxxx.xxx.activity.banksign;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.xinzong.etc.R;
import com.xinzong.xx.base.BaseGestureActivty;
import com.xinzong.xxx.utils.ShowReloadUtil;
/**
*
* @author
*
*/
public class WebViewActivity extends BaseGestureActivty implements OnClickListener{
private ShowReloadUtil reloadUtil;
private String url = "http://120.1.1.1/xx/xxxx";
private WebView webView;
private String urlParameter = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_webview);
findViewById(R.id.ibBack).setOnClickListener(this);
//獲取傳過來的支付參數(shù)
urlParameter = getIntent().getStringExtra("urlParameter");
Log.i("TAG", urlParameter);
//初始化重新加載框
reloadUtil = new ShowReloadUtil(this);
reloadUtil.setReloadView(this, R.id.ll_show_data_mc,
R.id.rl_reload_parent_mc);
//刷新界面,加載webview
refresh();
}
private void refresh() {
if(isNetworkConnected()){
findView(R.id.webview1).setVisibility(View.VISIBLE);
reloadUtil.showDataView();
initWebView();
}else{
findView(R.id.webview1).setVisibility(View.GONE);
reloadUtil.showReload();
}
}
private void initWebView() {
webView = (WebView) findViewById(R.id.webview1);
//初始化webview
//啟用支持javascript
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);//支持javaScript
settings.setDefaultTextEncodingName("utf-8");//設(shè)置網(wǎng)頁默認編碼
settings.setJavaScriptCanOpenWindowsAutomatically(true);
Log.d("TAG", "url:"+url);
//post請求(使用鍵值對形式,格式與get請求一樣,key=value,多個用&連接)
urlParameter = "JSONpriKey=" +urlParameter;
webView.postUrl(url, urlParameter.getBytes());
// webView.loadUrl(url);//get
webView.setWebChromeClient(new MyWebChromeClient());// 設(shè)置瀏覽器可彈窗
//覆蓋WebView默認使用第三方或系統(tǒng)默認瀏覽器打開網(wǎng)頁的行為,使網(wǎng)頁用WebView打開
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//返回值是true的時候控制去WebView打開,為false調(diào)用系統(tǒng)瀏覽器或第三方瀏覽器
Log.d("TAG", "url:"+url);
view.loadUrl(url);
return true;
}
@Override
public void onPageStarted(WebView view, String url,
Bitmap favicon) {
Log.d("TAG", "onPageStarted--url:"+url);
//支付完成后,點返回關(guān)閉界面
if(url.endsWith("http://120.1.1.1/xxx/xx/xxx")){
finish();
}
super.onPageStarted(view, url, favicon);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
});
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.btnReload) {// 點擊 ‘重新加載'
reloadUtil.showClickloadingView();
Log.d("TAG", "RELOAD");
if (this.isNetworkConnected()) {
webView.loadUrl(url);
} else {
reloadUtil.showReload();
}
}else if(v.getId() == R.id.ibBack){
if(webView !=null && webView.canGoBack()){
webView.goBack();
}else{
finish();
}
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && webView !=null && webView.canGoBack()){
webView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
/**
* 瀏覽器可彈窗
*
* @author Administrator
*
*/
final class MyWebChromeClient extends WebChromeClient {
@Override
public boolean onJsConfirm(WebView view, String url, String message,
final JsResult result) {
new AlertDialog.Builder(CTX)
.setTitle("App Titler")
.setMessage(message)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
result.confirm();
}
})
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
result.cancel();
}
}).create().show();
return true;
}
}
}
以上這篇Android WebView通過動態(tài)的修改js去攔截post請求參數(shù)實例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解androidstudio項目上傳到github方法以及步驟
在使用studio開發(fā)的項目過程中有時候我們想將項目發(fā)布到github上,studio其實是自帶這種功能的,那么如何使用呢,下面我們就一起來了解一下2019-01-01
輕松實現(xiàn)Android仿淘寶地區(qū)選擇功能
這篇文章主要介紹了輕松實現(xiàn)Android仿淘寶地區(qū)選擇功能的相關(guān)資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-06-06
百度語音識別(Baidu Voice) Android studio版本詳解
這篇文章主要介紹了百度語音識別(Baidu Voice) Android studio版本詳解的相關(guān)資料,需要的朋友可以參考下2016-09-09
Android RecyclerView四級緩存源碼層詳細分析
RecyclerView是Android一個更強大的控件,其不僅可以實現(xiàn)和ListView同樣的效果,還有優(yōu)化了ListView中的各種不足。其可以實現(xiàn)數(shù)據(jù)縱向滾動,也可以實現(xiàn)橫向滾動(ListView做不到橫向滾動)。接下來講解RecyclerView的用法2022-11-11

