webview添加參數(shù)與修改請求頭的user-agent實例
前言
最近公司項目需求,在項目中嵌入h5頁面,一般原生,看著感覺跟往常一樣,一個地址就完全ok了,如果是這樣那就沒有這個博文的必要了!
項目的登錄使用的token登錄,在移動端的登錄是原生的,但是h5也是有登錄頁面,這就需要控制token的過期時間了,但是想達(dá)到的網(wǎng)頁訪問使用網(wǎng)頁的cookie,app登錄使用的是app原生的登錄token,在網(wǎng)頁的cookie登錄過期的時候,網(wǎng)頁是可以正常退回登錄頁面,而在app嵌入的h5也需要根據(jù)token是否過期,決定是否返回登錄頁。
那么,問題就是在此產(chǎn)生了,token過期的時候app端是如何知道呢?這篇博文記錄解決方案:
正常使用
按照正常程序走的話,我們使用
webView.loadUrl(url);
這樣就可以解決平時之間訪問一個網(wǎng)頁的需求。或者在url的后面拼接上我們需要的token去驗證登錄狀態(tài),也可以拼接其他參數(shù)做一些特殊的處理。
但是如果僅僅是拼接token過去,在當(dāng)前頁面(一級頁面)是可以正常判斷過期時間,但是如果在此頁面(一級頁面)有按鈕點(diǎn)擊進(jìn)入二級頁面,這樣在二級頁面是沒有token的拼接的,如果在一級頁面token過期了,還是能正常進(jìn)入二級頁面,所以在二級頁面還是處于登錄的狀態(tài),但是實際在一級頁面的時候token已經(jīng)是過期的了,所以正常來說二級頁面也是處于退出登錄狀態(tài)才對,但是由于token是在一級頁面的時候進(jìn)行拼接的,所以能正常判斷,但是二級頁面我們沒有拼接,所以無法判斷。
總結(jié):此方法不適合所有登錄頁面都需要判斷登錄狀態(tài)的h5
添加請求頭
在加載h5的時候添加請求頭
在度娘上有很多都是直接使用webView.loadUrl(url,header);直接添加請求頭(header),但是這種方法與step1中的沒有多大的區(qū)別,也只是在一級頁面的時候生效,二級頁面的header還是變成了之前的默認(rèn)的了。
同步cookie
使用cookie,同步網(wǎng)頁的cookie,這種方法可以是可以,但是并不能使用很多情況,如果你是半原生,半h5,而你原生的登錄的登錄使用的是token進(jìn)行登錄的驗證的話,cookie的這種方法也不是一個很好的方法。cookie的同步就不介紹了,在度娘上可以找到。
修改user-agent、訪問url都拼接token
因為我們的需求的進(jìn)行多端登錄,進(jìn)行不同設(shè)備的判斷,所以我們移動端的h5頁面要告訴服務(wù)端,這個是訪問是我們app訪問的,所以我們需要解決的是讓服務(wù)端如何知道這個網(wǎng)頁的訪問是移動端訪問的。
在http請求頭中,有user-agent,服務(wù)端可以根據(jù)請求頭的user-agent來判斷當(dāng)前是什么設(shè)備訪問的,所以我們要跟服務(wù)端規(guī)定好給服務(wù)端傳一個特殊的字符串,默認(rèn)這是我們app訪問的,這樣服務(wù)端就知道是誰訪問的了,然后進(jìn)行相關(guān)的操作。
而webview中也剛好提供了方法進(jìn)行user-agent的設(shè)置,具體使用下面再進(jìn)行介紹。在請求頭里的作用可自行百度。
解決了身份的判斷了,就剩下解決退出登錄的通知了。
上面也說了,如果是直接使用拼接參數(shù)的方法是可以驗證是否登錄過期的,但是對二級頁面是不生效的,因為二級頁面是沒有進(jìn)行拼接的,但是如果我們的二級頁面也進(jìn)行了拼接呢?是否可以正常驗證呢?
下面我們使用的就是在所有的二級或者三級等頁面上都拼接上我們的token:
在介紹解決方法之前先介紹幾個方法:
@Nullable @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { return super.shouldInterceptRequest(view, request); } @Nullable @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { return super.shouldInterceptRequest(view, url); }
方法一shouldInterceptRequest(WebView view, WebResourceRequest request)和方法二shouldInterceptRequest(WebView view, String url)其實左右是一樣的,但是方法二在在API級別21中已棄用。但是我們未來兼容版本,所以兩方法還是都要使用。
上圖是Google的官方文檔,我們可以知道,在h5中發(fā)起的所有的加載資源的url,都會再此方法中攔截,然后我們攔截到所以得url進(jìn)行我們需要的操作。所以在所有的二級、三級等頁面拼接參數(shù)就在此方法中拼接。
同時再看兩個方法:
@Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { return super.shouldOverrideUrlLoading(view, request); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return super.shouldOverrideUrlLoading(view, url); }
在這里,我們可以處理重定向,變成我們需要顯示的效果,具體的使用可就不詳細(xì)介紹了。主要是shouldOverrideUrlLoading()方法的使用介紹。
解決方案:
1、修改user-agent
在webview中,提供了一個方法setUserAgentString(),可以修改user-agent,使用方法如下:
mWebView.getSettings().setUserAgentString(mWebView.getSettings().getUserAgentString().concat("Android-APP"));
Android-APP就是配置特殊字符串,服務(wù)端可以根據(jù)你的user-agent包含了Android-APP來判斷你是使用app進(jìn)行訪問的。
但是在設(shè)置user-agent的時候,使用一下設(shè)置方法;
mWebView.getSettings().setUserAgentString(mWebView.getSettings().getUserAgentString()+"Android-APP");
這樣設(shè)置的話服務(wù)端怎么都拿不到Android-APP,這個問題我還沒搞明白是這么回事。有知道的朋友可以幫忙解惑一下,非常感謝?。?!
第一種設(shè)置方法就是在user-agent的基礎(chǔ)上在后面拼接上Android-APP,這樣app訪問的話,服務(wù)端就會知道是app訪問的,就會想要對應(yīng)的操作。
2、拼接參數(shù)
/** * * @param view * @param request * @return */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public WebResourceResponse shouldInterceptRequest(WebView view, final WebResourceRequest request) { if (request != null && request.getUrl() != null && request.getMethod().equalsIgnoreCase("get")) { String scheme = request.getUrl().getScheme().trim(); if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) { if (request.getUrl().toString().contains(".html")) { try { LogUtil.e("------->" + request.getUrl().toString()); URL url = new URL(injectIsParams(request.getUrl().toString())); URLConnection connection = url.openConnection(); if (connection.getInputStream().toString().length() < 120) { String value = IOUtils.toString(connection.getInputStream(), "UTF-8"); LogUtil.e(value); if (value.startsWith("{")) { HttpResult httpResult = JSONObject.parseObject(value, HttpResult.class); if (httpResult != null) { LogUtil.e(httpResult.toString()); if (Constants.LOGIN_OUT_CODE.equals(httpResult.getCode())) { LoginActivity.startAction(getActivity()); SPUtils.put(getActivity(), Constants.ACCESS_TOKEN, ""); getActivity().finish(); } } } } else { connection.getInputStream().close(); } return null; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } } return null; } /** * * @param view * @param url * @return */ @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { if (url.contains(".html")) { try { URL url1 = new URL(injectIsParams(url)); URLConnection connection = url1.openConnection(); if (connection.getInputStream().toString().length() < 120) { String value = IOUtils.toString(connection.getInputStream(), "UTF-8"); LogUtil.e(value); if (value.startsWith("{")) { HttpResult httpResult = JSONObject.parseObject(value, HttpResult.class); if (httpResult != null) { LogUtil.e(httpResult.toString()); if (Constants.LOGIN_OUT_CODE.equals(httpResult.getCode())) { LoginActivity.startAction(getActivity()); SPUtils.put(getActivity(), Constants.ACCESS_TOKEN, ""); getActivity().finish(); } } } } else { connection.getInputStream().close(); } return null; } catch (IOException e) { e.printStackTrace(); } } return null; }
/** * 注入?yún)?shù) * * @param url * @return */ public static String injectIsParams(String url) { if (url != null && !url.contains("app=true&access_token=")) { if (url.contains("?")) { return url + "&app=true&access_token=" + SPUtils.get(App.getInstance().getApplicationContext(), Constants.ACCESS_TOKEN, ""); } else { return url + "?app=true&access_token=" + SPUtils.get(App.getInstance().getApplicationContext(), Constants.ACCESS_TOKEN, ""); } } else { return url; } }
因為在shouldInterceptRequest()中攔截的是所有的加載資源文件的url,想加載圖片,加載視頻,加載js等url都會再此攔截,所以我們拼接參數(shù)的話,是要過濾掉這些的,我這里只要是包含了.html的url都進(jìn)行參數(shù)拼接。
URL url = new URL(injectIsParams(request.getUrl().toString()));
URLConnection connection = url.openConnection();
這里是對進(jìn)行了拼接參數(shù)的url進(jìn)行連接,連接完成之后,我們要獲取頁面的時間,使用一下方法去獲取:
connection.getInputStream()
以上的方法獲取的是整個頁面的數(shù)據(jù),如果是網(wǎng)頁的話,會把整個網(wǎng)頁的代碼獲取下來,但是我們需要的并不是要網(wǎng)頁的代碼,我們是要獲取登錄失敗的消息。
在此,我們已服務(wù)端協(xié)商,在token失效后,給我們返回跟接口一樣的json字符串,如果是處于登錄失效的時候,我們使用connection.getInputStream()獲取的就是一串json字符串,因為token并不是都是過期的,所以connection.getInputStream()獲取的可以是一個網(wǎng)頁的代碼,這就需要我們進(jìn)行過濾了。
json字符串是以大括號開始的,并且以大括號結(jié)束,這樣我們就可以區(qū)分是代碼還是json字符串了。
拿到j(luò)son字符串之后就進(jìn)行解析,執(zhí)行我們的退出登錄操作。至此,完美解決token失效的問題?。?!
下面就貼出整個頁面的代碼,如果有需要的可以去除錯誤部分,畢竟是從項目中抽出來的,只是做一個參考作用:
/** * @author Administrator */ public class WebViewActivity extends BaseAbstractSimpleActivity implements IActivityStatusBar { private String url = ""; private AgentWeb mAgentWeb; private LinearLayout mLinearLayoutWebView; private DownloadingService mDownloadingService; private WebView mWebView; private long mTime; private LinearLayout mLinearLayoutSplash; private ValueCallback<Uri[]> mUploadCallbackAboveFive; private ValueCallback<Uri> mUploadMessage; private final int RESULT_CODE_IMAGE = 1005; private File userImgFile; /** * 請求開啟定位 */ public static int REQUEST_CODE_ENABLE_LOCATION = 100; public static int REQUEST_CODE_ACCESS_LOCATION_PERMISSION = 101; private LocationWebChromeClient mWebChromeClient; public static void startAction(Activity context, @NonNull String url) { Intent intent = new Intent(context, WebViewActivity.class); intent.putExtra("url", url); context.startActivityForResult(intent, Constants.LOGIN_OUT_REQUEST); } @Override protected int getLayout() { return R.layout.activity_web_view; } @Override protected void initEventAndData() { mWebChromeClient = new LocationWebChromeClient(); mAgentWeb = AgentWeb.with(this) .setAgentWebParent(mLinearLayoutWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)) .useDefaultIndicator(-1, 3) .setAgentWebWebSettings(getSettings()) .setWebViewClient(mWebViewClient) .setWebChromeClient(mWebChromeClient) .setSecurityType(AgentWeb.SecurityType.STRICT_CHECK) .setMainFrameErrorView(R.layout.agentweb_error_page, -1) .setOpenOtherPageWays(DefaultWebClient.OpenOtherPageWays.DISALLOW) .interceptUnkownUrl() .createAgentWeb() .ready() .go(url); mWebView = mAgentWeb.getWebCreator().getWebView(); mAgentWeb.getJsInterfaceHolder().addJavaObject("aaa", new JavaScriptInterface(this)); if (url.contains("<html>")) { mWebView.loadDataWithBaseURL(null, url.toString(), "text/html", "utf-8", null); } mAgentWeb.getWebCreator().getWebView().setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { Uri uri = Uri.parse(url); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } }); } @Override protected void initView() { url = getIntent().getStringExtra("url"); mLinearLayoutWebView = findViewById(R.id.linear_layout_webview); new RxPermissions(this) .requestEach(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE) .subscribe(new SubscriberCallBack<>(new ApiCallback<Permission>() { @Override public void onSuccess(Permission model) { if (model.granted) { } else { Toast.makeText(WebViewActivity.this, "請允許打開需要的權(quán)限", Toast.LENGTH_SHORT).show(); finish(); } } @Override public void onFailure(String msg) { } })); } /** * @return IAgentWebSettings */ public IAgentWebSettings getSettings() { return new AbsAgentWebSettings() { private AgentWeb mAgentWeb; @Override protected void bindAgentWebSupport(AgentWeb agentWeb) { this.mAgentWeb = agentWeb; } /** * AgentWeb 4.0.0 內(nèi)部刪除了 DownloadListener 監(jiān)聽 ,以及相關(guān)API ,將 Download 部分完全抽離出來獨(dú)立一個庫, * 如果你需要使用 AgentWeb Download 部分 , 請依賴上 compile 'com.just.agentweb:download:4.0.0 , * 如果你需要監(jiān)聽下載結(jié)果,請自定義 AgentWebSetting , New 出 DefaultDownloadImpl,傳入DownloadListenerAdapter * 實現(xiàn)進(jìn)度或者結(jié)果監(jiān)聽,例如下面這個例子,如果你不需要監(jiān)聽進(jìn)度,或者下載結(jié)果,下面 setDownloader 的例子可以忽略。 * @param webView * @param downloadListener * @return WebListenerManager */ @Override public WebListenerManager setDownloader(WebView webView, android.webkit.DownloadListener downloadListener) { return super.setDownloader(webView, DefaultDownloadImpl .create((Activity) webView.getContext(), webView, mDownloadListenerAdapter, mDownloadListenerAdapter, this.mAgentWeb.getPermissionInterceptor())); } }; } protected DownloadListenerAdapter mDownloadListenerAdapter = new DownloadListenerAdapter() { @Override public boolean onStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength, AgentWebDownloader.Extra extra) { extra.setOpenBreakPointDownload(true) .setIcon(R.drawable.ic_file_download_black_24dp) .setConnectTimeOut(6000) .setBlockMaxTime(2000) .setDownloadTimeOut(60L * 5L * 1000L) .setAutoOpen(true) .setForceDownload(false); return false; } @Override public void onBindService(String url, DownloadingService downloadingService) { super.onBindService(url, downloadingService); mDownloadingService = downloadingService; LogUtils.i("TAG", "onBindService:" + url + " DownloadingService:" + downloadingService); } @Override public void onUnbindService(String url, DownloadingService downloadingService) { super.onUnbindService(url, downloadingService); mDownloadingService = null; LogUtils.i("TAG", "onUnbindService:" + url); } @Override public void onProgress(String url, long loaded, long length, long usedTime) { int mProgress = (int) ((loaded) / Float.valueOf(length) * 100); LogUtils.i("TAG", "onProgress:" + mProgress); super.onProgress(url, loaded, length, usedTime); } @Override public boolean onResult(String path, String url, Throwable throwable) { if (null == throwable) { //do you work } else { } return false; } }; private WebViewClient mWebViewClient = new WebViewClient() { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { LogUtil.e("加載地址--》" + url); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); } @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); } /** * * @param view * @param request * @return */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public WebResourceResponse shouldInterceptRequest(WebView view, final WebResourceRequest request) { if (request != null && request.getUrl() != null && request.getMethod().equalsIgnoreCase("get")) { String scheme = request.getUrl().getScheme().trim(); if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) { if (request.getUrl().toString().contains(".html")) { try { LogUtil.e("------->" + request.getUrl().toString()); URL url = new URL(injectIsParams(request.getUrl().toString())); URLConnection connection = url.openConnection(); if (connection.getInputStream().toString().length() < 120) { String value = IOUtils.toString(connection.getInputStream(), "UTF-8"); LogUtil.e(value); if (value.startsWith("{")) { HttpResult httpResult = JSONObject.parseObject(value, HttpResult.class); if (httpResult != null) { LogUtil.e(httpResult.toString()); if (Constants.LOGIN_OUT_CODE.equals(httpResult.getCode())) { setResult(LOGIN_OUT_RESULT); finish(); } } } } else { connection.getInputStream().close(); } return null; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } } return null; } /** * * @param view * @param url * @return */ @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { if (url.contains(".html")) { try { URL url1 = new URL(injectIsParams(url)); URLConnection connection = url1.openConnection(); if (connection.getInputStream().toString().length() < 120) { String value = IOUtils.toString(connection.getInputStream(), "UTF-8"); LogUtil.e(value); if (value.startsWith("{")) { HttpResult httpResult = JSONObject.parseObject(value, HttpResult.class); if (httpResult != null) { LogUtil.e(httpResult.toString()); if (Constants.LOGIN_OUT_CODE.equals(httpResult.getCode())) { setResult(LOGIN_OUT_RESULT); finish(); } } } } else { connection.getInputStream().close(); } return null; } catch (IOException e) { e.printStackTrace(); } } return null; } }; /** * 注入?yún)?shù) * * @param url * @return */ public static String injectIsParams(String url) { if (url != null && !url.contains("app=true&access_token=")) { if (url.contains("?")) { return url + "&app=true&access_token=" + SPUtils.get(App.getInstance().getApplicationContext(), Constants.ACCESS_TOKEN, ""); } else { return url + "?app=true&access_token=" + SPUtils.get(App.getInstance().getApplicationContext(), Constants.ACCESS_TOKEN, ""); } } else { return url; } } @Override public int getStatusBarColor() { return ContextCompat.getColor(this, R.color.colorPrimary); } @Override protected void onResume() { mAgentWeb.getWebLifeCycle().onResume(); super.onResume(); } @Override protected void onPause() { mAgentWeb.getWebLifeCycle().onPause(); super.onPause(); } @Override protected void onDestroy() { AgentWebConfig.clearDiskCache(this); mWebView.clearCache(true); mAgentWeb.getWebLifeCycle().onDestroy(); super.onDestroy(); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); Log.e("TAG", "結(jié)果碼-->" + resultCode + "\n請求碼--》" + requestCode); if (resultCode == 0) { if (mUploadCallbackAboveFive != null) { mUploadCallbackAboveFive.onReceiveValue(null); mUploadCallbackAboveFive = null; } if (mUploadMessage != null) { mUploadMessage.onReceiveValue(null); mUploadMessage = null; } return; } switch (requestCode) { //拍照 case GET_PICTURE_TAKE_PHOTO: userImgFile = ImageSelector.cutPicture(this, userImgFile); break; //選擇照片 case GET_PICTURE_SELECT_PHOTO: userImgFile = ImageSelector.getPhotoFromIntent(data, this); userImgFile = ImageSelector.cutPicture(this, userImgFile); break; //裁剪照片 case CUT_PHOTO: if (resultCode == Activity.RESULT_OK) { compressImage(userImgFile); } break; default: break; } } /** * 壓縮圖片 * * @param file */ public void compressImage(File file) { List<File> list = new ArrayList<>(); list.add(file); BitmapUtil.compressFiles(list, new ICompressImageResponse() { @Override public void onSuccess(List<File> images) { File imgFile = images.get(0); Uri result = ImageSelector.toURI(WebViewActivity.this, imgFile); Log.e("TAG", "文件URI-->" + result); if (null != mUploadMessage && null == mUploadCallbackAboveFive) { mUploadMessage.onReceiveValue(result); mUploadMessage = null; } if (null == mUploadMessage && null != mUploadCallbackAboveFive) { mUploadCallbackAboveFive.onReceiveValue(new Uri[]{result}); mUploadCallbackAboveFive = null; } } @Override public void onMarch() { } @Override public void onFail() { } @Override public void onFinish() { } }); } /** * 顯示圖片選擇器 * * @param context * @param view */ public void showSelector(final Activity context, View view) { PopupGetPictureView popupGetPictureView = new PopupGetPictureView(context, new PopupGetPictureView.GetPicture() { @Override public void takePhoto(View v) { if (PermissionUtils.checkTakePhotoPermission(context)) { userImgFile = ImageSelector.takePicture(context, GET_PICTURE_TAKE_PHOTO); } } @Override public void selectPhoto(View v) { if (PermissionUtils.checkAlbumStroagePermission(context)) { ImageSelector.photoPick(context, GET_PICTURE_SELECT_PHOTO); } } @Override public void cancel(PopupWindow popupWindow) { if (popupWindow.isShowing()) { if (mUploadCallbackAboveFive != null) { mUploadCallbackAboveFive.onReceiveValue(null); mUploadCallbackAboveFive = null; } if (mUploadMessage != null) { mUploadMessage.onReceiveValue(null); mUploadMessage = null; } popupWindow.dismiss(); } } }); popupGetPictureView.showPop(view); } class LocationWebChromeClient extends WebChromeClient { private LocationWebChromeClientListener mLocationWebChromeClientListener; private GeolocationPermissions.Callback mGeolocationPermissionsCallback; private String mOrigin; private boolean mShowRequestPermissionRationale = false; public LocationWebChromeClient() { mLocationWebChromeClientListener = new LocationWebChromeClientListener() { @Override public boolean onReturnFromLocationSetting(int requestCode) { if (requestCode == REQUEST_CODE_ENABLE_LOCATION) { if (mGeolocationPermissionsCallback != null) { if (isEnabledLocationFunction()) { mGeolocationPermissionsCallback.invoke(mOrigin, true, true); } else { //顯然,從設(shè)置界面回來還沒有開啟定位服務(wù),肯定是要拒絕定位了 Toast.makeText(WebViewActivity.this, "您拒絕了定位請求", Toast.LENGTH_SHORT).show(); mGeolocationPermissionsCallback.invoke(mOrigin, false, false); } } return true; } return false; } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { boolean pass = true; for (Integer result : grantResults) { if (result == PackageManager.PERMISSION_DENIED) { pass = false; break; } } if (pass) { onAccessLocationPermissionGranted(); } else { onAccessLocationPermissionRejected(); } } public void onAccessLocationPermissionGranted() { doJudgeLocationServiceEnabled(); } public void onAccessLocationPermissionRejected() { if (mShowRequestPermissionRationale) { Toast.makeText(WebViewActivity.this, "您拒絕了定位請求", Toast.LENGTH_SHORT).show(); mGeolocationPermissionsCallback.invoke(mOrigin, false, false); } else { doRequestAppSetting(); } } }; } @Override public void onProgressChanged(WebView view, int newProgress) { //do you work } @SuppressWarnings("unused") public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType, String capture) { this.openFileChooser(uploadMsg); } @SuppressWarnings("unused") public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType) { this.openFileChooser(uploadMsg); } public void openFileChooser(ValueCallback<Uri> uploadMsg) { mUploadMessage = uploadMsg; showSelector(WebViewActivity.this, mWebView); } @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { mUploadCallbackAboveFive = filePathCallback; showSelector(WebViewActivity.this, mWebView); return true; } private void doRequestAppSetting() { AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this); builder.setTitle("溫馨提示"); builder.setMessage(String.format("您禁止了應(yīng)用獲取當(dāng)前位置的權(quán)限,是否前往開啟?", mOrigin)); builder.setPositiveButton("是", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { Intent mIntent = new Intent(); mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= 9) { mIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); mIntent.setData(Uri.fromParts("package", getPackageName(), null)); } else if (Build.VERSION.SDK_INT <= 8) { mIntent.setAction(Intent.ACTION_VIEW); mIntent.setClassName("com.android.settings", "com.android.setting.InstalledAppDetails"); mIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName()); } startActivity(mIntent); } }); builder.setNegativeButton("否", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { mGeolocationPermissionsCallback.invoke(mOrigin, false, false); } }); builder.create().show(); } public LocationWebChromeClientListener getLocationWebChromeClientListener() { return mLocationWebChromeClientListener; } @Override public void onGeolocationPermissionsHidePrompt() { super.onGeolocationPermissionsHidePrompt(); } @Override public void onGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback geolocationPermissionsCallback) { this.mOrigin = origin; this.mGeolocationPermissionsCallback = geolocationPermissionsCallback; //是否擁有定位權(quán)限 if (hasAccessLocationPermission()) { doJudgeLocationServiceEnabled(); } else { //請求定位 requestAccessLocationPermission(); } } private void doJudgeLocationServiceEnabled() { //是否開啟定位 if (isEnabledLocationFunction()) { AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this); builder.setTitle("溫馨提示"); builder.setMessage(String.format("網(wǎng)站%s,正在請求使用您當(dāng)前的位置,是否許可?", mOrigin)); builder.setPositiveButton("許可", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { mGeolocationPermissionsCallback.invoke(mOrigin, true, true); } }); builder.setNegativeButton("不許可", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { mGeolocationPermissionsCallback.invoke(mOrigin, false, false); } }); builder.create().show(); } else { //請求開啟定位功能 requestEnableLocationFunction(mOrigin, mGeolocationPermissionsCallback); } } /** * 請求開啟定位服務(wù) */ private void requestEnableLocationFunction(final String origin, final GeolocationPermissions.Callback geolocationPermissionsCallback) { AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this); builder.setTitle("溫馨提示"); builder.setMessage(String.format("網(wǎng)站%s,正在請求使用您當(dāng)前的位置,是否前往開啟定位服務(wù)?", origin)); builder.setPositiveButton("前往開啟", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivityForResult(intent, REQUEST_CODE_ENABLE_LOCATION); } }); builder.setNegativeButton("拒絕", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { geolocationPermissionsCallback.invoke(origin, false, false); } }); builder.create().show(); } private boolean isEnabledLocationFunction() { int locationMode = 0; String locationProviders; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { try { locationMode = Settings.Secure.getInt(WebViewActivity.this.getContentResolver(), Settings.Secure.LOCATION_MODE); } catch (Settings.SettingNotFoundException e) { e.printStackTrace(); return false; } return locationMode != Settings.Secure.LOCATION_MODE_OFF; } else { locationProviders = Settings.Secure.getString(WebViewActivity.this.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED); return !TextUtils.isEmpty(locationProviders); } } private boolean hasAccessLocationPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; } else { return ContextCompat.checkSelfPermission(WebViewActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; } } private void requestAccessLocationPermission() { // 是否要顯示問什么要獲取權(quán)限的解釋界面 /** * 什么情況下 shouldShowRequestPermissionRationale會返回true? * - 首次請求權(quán)限,但是用戶禁止了,但是沒有勾選“禁止后不再詢問”,這樣,之后的請求都會返回true * 什么情況下,shouldShowRequestPermissionRationale會返回false? * - 首次請求權(quán)限或者請求權(quán)限時,用戶勾選了“禁止后不再詢問”,之后的請求都會返回false */ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) { //請求過定位權(quán)限,但是被用戶拒絕了(但是沒有勾選“禁止后不再詢問”) // 顯示解釋權(quán)限用途的界面,然后再繼續(xù)請求權(quán)限 mShowRequestPermissionRationale = true; } else { mShowRequestPermissionRationale = false; } } else { mShowRequestPermissionRationale = false; } AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this); builder.setTitle("溫馨提示"); builder.setMessage(String.format("網(wǎng)站%s,正在請求使用您當(dāng)前的位置,是否許可應(yīng)用獲取當(dāng)前位置權(quán)限?", mOrigin)); builder.setPositiveButton(" 是 ", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_ACCESS_LOCATION_PERMISSION); } else { //額,版本低,正常情況下,安裝默認(rèn)許可,然鵝,國產(chǎn)ROM各種魔改,有闊輪提前實現(xiàn)了單獨(dú)授權(quán) doRequestAppSetting(); } } }); builder.setNegativeButton(" 否 ", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { mGeolocationPermissionsCallback.invoke(mOrigin, false, false); } }); builder.create().show(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (mWebChromeClient != null && mWebChromeClient.getLocationWebChromeClientListener() != null) { mWebChromeClient.getLocationWebChromeClientListener().onRequestPermissionsResult(requestCode, permissions, grantResults); } } interface LocationWebChromeClientListener { /** * 用戶從開啟定位頁面回來了 */ boolean onReturnFromLocationSetting(int requestCode); /** * 請求權(quán)限結(jié)果 */ void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults); } private final class JavaScriptInterface { public Activity activity; public JavaScriptInterface(Activity activity) { this.activity = activity; } @JavascriptInterface public void scanCode() { } @JavascriptInterface public void androidGoBack() { activity.finish(); } @JavascriptInterface public void call(String phone) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_CALL); intent.setData(Uri.parse("tel:" + phone)); //開啟系統(tǒng)撥號器 activity.startActivity(intent); } } }
特別說明:使用shouldInterceptRequest()方法拼接參數(shù),并不是修改了訪問的網(wǎng)頁url,只不過是在加載資源的時候?qū)撁孢M(jìn)行了一層是否登陸的驗證,驗證通過則進(jìn)行加載頁面,不通過執(zhí)行相對于的操作。如果頁面有重定向的,則需要在處理重定向的方法中進(jìn)行驗證,驗證成功,則加載重定向,驗證不成功,不加載,然后執(zhí)行我們的操作即可。
以上使用的webview是AgentWeb,導(dǎo)入依賴方法:
//agentweb 下載文件、選擇文件 implementation 'com.just.agentweb:download:4.0.2' // (可選) implementation 'com.just.agentweb:agentweb:4.0.2' implementation 'com.just.agentweb:filechooser:4.0.2'
使用的解析庫是阿里fastJson解析庫:
//阿里fastJson解析庫
implementation 'com.alibaba:fastjson:1.2.55'
使用的io裝換庫:
// https://mvnrepository.com/artifact/commons-io/commons-io
implementation group: 'commons-io', name: 'commons-io', version: '2.6'
以上這篇webview添加參數(shù)與修改請求頭的user-agent實例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Android使用HorizontalScrollView實現(xiàn)水平滾動
這篇文章主要為大家詳細(xì)介紹了Android使用HorizontalScrollView實現(xiàn)水平滾動,并點(diǎn)擊有相應(yīng)的反應(yīng)效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-11-11Android利用animation-list實現(xiàn)幀動畫
這篇文章主要為大家詳細(xì)介紹了Android利用animation-list實現(xiàn)幀動畫,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12Android6.0來電號碼與電話薄聯(lián)系人進(jìn)行匹配
這篇文章主要為大家詳細(xì)介紹了Android6.0來電號碼與電話薄聯(lián)系人進(jìn)行匹配的方法,感興趣的小伙伴們可以參考一下2016-07-07Android 6.0 無法在SD卡創(chuàng)建目錄的方法
今天小編就為大家分享一篇Android 6.0 無法在SD卡創(chuàng)建目錄的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08Android?Choreographer源碼詳細(xì)分析
Choreographer的作用主要是配合Vsync,給上層App的渲染提供一個穩(wěn)定的Message處理的時機(jī),也就是Vsync到來的時候,系統(tǒng)通過對Vsync信號周期的調(diào)整,來控制每一幀繪制操作的時機(jī)2022-08-08RecyclerView優(yōu)雅實現(xiàn)復(fù)雜列表布局
這篇文章主要為大家詳細(xì)介紹了RecyclerView優(yōu)雅實現(xiàn)復(fù)雜列表布局,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-11-11Android自定義View實現(xiàn)進(jìn)度條動畫
這篇文章主要為大家詳細(xì)介紹了Android自定義View實現(xiàn)進(jìn)度條動畫,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-08-08Android Camera2實現(xiàn)最簡單的預(yù)覽框顯示
這篇文章主要為大家詳細(xì)介紹了Android Camera2實現(xiàn)最簡單的預(yù)覽框顯示,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05