前言
最近公司项目需求,在项目中嵌入h5页面,一般原生,看着感觉跟往常一样,一个地址就完全ok了,如果是这样那就没有这个博文的必要了!
项目的登录使用的token登录,在移动端的登录是原生的,但是h5也是有登录页面,这就需要控制token的过期时间了,但是想达到的网页访问使用网页的cookie,app登录使用的是app原生的登录token,在网页的cookie登录过期的时候,网页是可以正常退回登录页面,而在app嵌入的h5也需要根据token是否过期,决定是否返回登录页。
那么,问题就是在此产生了,token过期的时候app端是如何知道呢?这篇博文记录解决方案:
正常使用
按照正常程序走的话,我们使用
webView.loadUrl(url);
这样就可以解决平时之间访问一个网页的需求。或者在url的后面拼接上我们需要的token去验证登录状态,也可以拼接其他参数做一些特殊的处理。
但是如果仅仅是拼接token过去,在当前页面(一级页面)是可以正常判断过期时间,但是如果在此页面(一级页面)有按钮点击进入二级页面,这样在二级页面是没有token的拼接的,如果在一级页面token过期了,还是能正常进入二级页面,所以在二级页面还是处于登录的状态,但是实际在一级页面的时候token已经是过期的了,所以正常来说二级页面也是处于退出登录状态才对,但是由于token是在一级页面的时候进行拼接的,所以能正常判断,但是二级页面我们没有拼接,所以无法判断。
总结:此方法不适合所有登录页面都需要判断登录状态的h5
添加请求头
在加载h5的时候添加请求头
在度娘上有很多都是直接使用webView.loadUrl(url,header);直接添加请求头(header),但是这种方法与step1中的没有多大的区别,也只是在一级页面的时候生效,二级页面的header还是变成了之前的默认的了。
同步cookie
使用cookie,同步网页的cookie,这种方法可以是可以,但是并不能使用很多情况,如果你是半原生,半h5,而你原生的登录的登录使用的是token进行登录的验证的话,cookie的这种方法也不是一个很好的方法。cookie的同步就不介绍了,在度娘上可以找到。
修改user-agent、访问url都拼接token
因为我们的需求的进行多端登录,进行不同设备的判断,所以我们移动端的h5页面要告诉服务端,这个是访问是我们app访问的,所以我们需要解决的是让服务端如何知道这个网页的访问是移动端访问的。
在http请求头中,有user-agent,服务端可以根据请求头的user-agent来判断当前是什么设备访问的,所以我们要跟服务端规定好给服务端传一个特殊的字符串,默认这是我们app访问的,这样服务端就知道是谁访问的了,然后进行相关的操作。
而webview中也刚好提供了方法进行user-agent的设置,具体使用下面再进行介绍。在请求头里的作用可自行百度。
解决了身份的判断了,就剩下解决退出登录的通知了。
上面也说了,如果是直接使用拼接参数的方法是可以验证是否登录过期的,但是对二级页面是不生效的,因为二级页面是没有进行拼接的,但是如果我们的二级页面也进行了拼接呢?是否可以正常验证呢?
下面我们使用的就是在所有的二级或者三级等页面上都拼接上我们的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中发起的所有的加载资源的url,都会再此方法中拦截,然后我们拦截到所以得url进行我们需要的操作。所以在所有的二级、三级等页面拼接参数就在此方法中拼接。
同时再看两个方法:
@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);
}
在这里,我们可以处理重定向,变成我们需要显示的效果,具体的使用可就不详细介绍了。主要是shouldOverrideUrlLoading()方法的使用介绍。
解决方案:
1、修改user-agent
在webview中,提供了一个方法setUserAgentString(),可以修改user-agent,使用方法如下:
mWebView.getSettings().setUserAgentString(mWebView.getSettings().getUserAgentString().concat("Android-APP"));
Android-APP就是配置特殊字符串,服务端可以根据你的user-agent包含了Android-APP来判断你是使用app进行访问的。
但是在设置user-agent的时候,使用一下设置方法;
mWebView.getSettings().setUserAgentString(mWebView.getSettings().getUserAgentString()+"Android-APP");
这样设置的话服务端怎么都拿不到Android-APP,这个问题我还没搞明白是这么回事。有知道的朋友可以帮忙解惑一下,非常感谢!!!
第一种设置方法就是在user-agent的基础上在后面拼接上Android-APP,这样app访问的话,服务端就会知道是app访问的,就会想要对应的操作。
2、拼接参数
/*** * @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;
}
/*** 注入参数** @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都会再此拦截,所以我们拼接参数的话,是要过滤掉这些的,我这里只要是包含了.html的url都进行参数拼接。
URL url = new URL(injectIsParams(request.getUrl().toString()));
URLConnection connection = url.openConnection();
这里是对进行了拼接参数的url进行连接,连接完成之后,我们要获取页面的时间,使用一下方法去获取:
connection.getInputStream()
以上的方法获取的是整个页面的数据,如果是网页的话,会把整个网页的代码获取下来,但是我们需要的并不是要网页的代码,我们是要获取登录失败的消息。
在此,我们已服务端协商,在token失效后,给我们返回跟接口一样的json字符串,如果是处于登录失效的时候,我们使用connection.getInputStream()获取的就是一串json字符串,因为token并不是都是过期的,所以connection.getInputStream()获取的可以是一个网页的代码,这就需要我们进行过滤了。
json字符串是以大括号开始的,并且以大括号结束,这样我们就可以区分是代码还是json字符串了。
拿到json字符串之后就进行解析,执行我们的退出登录操作。至此,完美解决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);}@Overrideprotected int getLayout() {return R.layout.activity_web_view;}@Overrideprotected 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() {@Overridepublic 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);}});}@Overrideprotected 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>() {@Overridepublic void onSuccess(Permission model) {if (model.granted) {} else {Toast.makeText(WebViewActivity.this, "请允许打开需要的权限", Toast.LENGTH_SHORT).show();finish();}}@Overridepublic void onFailure(String msg) {}}));}/*** @return IAgentWebSettings*/public IAgentWebSettings getSettings() {return new AbsAgentWebSettings() {private AgentWeb mAgentWeb;@Overrideprotected void bindAgentWebSupport(AgentWeb agentWeb) {this.mAgentWeb = agentWeb;}/*** AgentWeb 4.0.0 内部删除了 DownloadListener 监听 ,以及相关API ,将 Download 部分完全抽离出来独立一个库,* 如果你需要使用 AgentWeb Download 部分 , 请依赖上 compile 'com.just.agentweb:download:4.0.0 ,* 如果你需要监听下载结果,请自定义 AgentWebSetting , New 出 DefaultDownloadImpl,传入DownloadListenerAdapter* 实现进度或者结果监听,例如下面这个例子,如果你不需要监听进度,或者下载结果,下面 setDownloader 的例子可以忽略。* @param webView* @param downloadListener* @return WebListenerManager*/@Overridepublic 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() {@Overridepublic 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;}@Overridepublic void onBindService(String url, DownloadingService downloadingService) {super.onBindService(url, downloadingService);mDownloadingService = downloadingService;LogUtils.i("TAG", "onBindService:" + url + " DownloadingService:" + downloadingService);}@Overridepublic void onUnbindService(String url, DownloadingService downloadingService) {super.onUnbindService(url, downloadingService);mDownloadingService = null;LogUtils.i("TAG", "onUnbindService:" + url);}@Overridepublic 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);}@Overridepublic boolean onResult(String path, String url, Throwable throwable) {if (null == throwable) {//do you work} else {}return false;}};private WebViewClient mWebViewClient = new WebViewClient() {@Overridepublic void onPageStarted(WebView view, String url, Bitmap favicon) {LogUtil.e("加载地址--》" + url);}@Overridepublic void onPageFinished(WebView view, String url) {super.onPageFinished(view, url);}@Overridepublic void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {handler.proceed();}/**** @param view* @param request* @return*/@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)@Overridepublic 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*/@Overridepublic 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;}};/*** 注入参数** @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;}}@Overridepublic int getStatusBarColor() {return ContextCompat.getColor(this, R.color.colorPrimary);}@Overrideprotected void onResume() {mAgentWeb.getWebLifeCycle().onResume();super.onResume();}@Overrideprotected void onPause() {mAgentWeb.getWebLifeCycle().onPause();super.onPause();}@Overrideprotected void onDestroy() {AgentWebConfig.clearDiskCache(this);mWebView.clearCache(true);mAgentWeb.getWebLifeCycle().onDestroy();super.onDestroy();}@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);Log.e("TAG", "结果码-->" + 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() {@Overridepublic 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;}}@Overridepublic void onMarch() {}@Overridepublic void onFail() {}@Overridepublic void onFinish() {}});}/*** 显示图片选择器** @param context* @param view*/public void showSelector(final Activity context, View view) {PopupGetPictureView popupGetPictureView = new PopupGetPictureView(context, newPopupGetPictureView.GetPicture() {@Overridepublic void takePhoto(View v) {if (PermissionUtils.checkTakePhotoPermission(context)) {userImgFile = ImageSelector.takePicture(context, GET_PICTURE_TAKE_PHOTO);}}@Overridepublic void selectPhoto(View v) {if (PermissionUtils.checkAlbumStroagePermission(context)) {ImageSelector.photoPick(context, GET_PICTURE_SELECT_PHOTO);}}@Overridepublic 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() {@Overridepublic boolean onReturnFromLocationSetting(int requestCode) {if (requestCode == REQUEST_CODE_ENABLE_LOCATION) {if (mGeolocationPermissionsCallback != null) {if (isEnabledLocationFunction()) {mGeolocationPermissionsCallback.invoke(mOrigin, true, true);} else {//显然,从设置界面回来还没有开启定位服务,肯定是要拒绝定位了Toast.makeText(WebViewActivity.this, "您拒绝了定位请求", Toast.LENGTH_SHORT).show();mGeolocationPermissionsCallback.invoke(mOrigin, false, false);}}return true;}return false;}@Overridepublic 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();}}};}@Overridepublic 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);}@Overridepublic 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("您禁止了应用获取当前位置的权限,是否前往开启?", mOrigin));builder.setPositiveButton("是", new DialogInterface.OnClickListener() {@Overridepublic 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() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {mGeolocationPermissionsCallback.invoke(mOrigin, false, false);}});builder.create().show();}public LocationWebChromeClientListener getLocationWebChromeClientListener() {return mLocationWebChromeClientListener;}@Overridepublic void onGeolocationPermissionsHidePrompt() {super.onGeolocationPermissionsHidePrompt();}@Overridepublic voidonGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback geolocationPermissionsCallback) {this.mOrigin = origin;this.mGeolocationPermissionsCallback = geolocationPermissionsCallback;//是否拥有定位权限if (hasAccessLocationPermission()) {doJudgeLocationServiceEnabled();} else {//请求定位requestAccessLocationPermission();}}private void doJudgeLocationServiceEnabled() {//是否开启定位if (isEnabledLocationFunction()) {AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this);builder.setTitle("温馨提示");builder.setMessage(String.format("网站%s,正在请求使用您当前的位置,是否许可?", mOrigin));builder.setPositiveButton("许可", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {mGeolocationPermissionsCallback.invoke(mOrigin, true, true);}});builder.setNegativeButton("不许可", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {mGeolocationPermissionsCallback.invoke(mOrigin, false, false);}});builder.create().show();} else {//请求开启定位功能requestEnableLocationFunction(mOrigin, mGeolocationPermissionsCallback);}}/*** 请求开启定位服务*/private void requestEnableLocationFunction(final String origin, final GeolocationPermissions.Callback geolocationPermissionsCallback) {AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this);builder.setTitle("温馨提示");builder.setMessage(String.format("网站%s,正在请求使用您当前的位置,是否前往开启定位服务?", origin));builder.setPositiveButton("前往开启", new DialogInterface.OnClickListener() {@Overridepublic 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() {@Overridepublic 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() {// 是否要显示问什么要获取权限的解释界面/*** 什么情况下 shouldShowRequestPermissionRationale会返回true?* - 首次请求权限,但是用户禁止了,但是没有勾选“禁止后不再询问”,这样,之后的请求都会返回true* 什么情况下,shouldShowRequestPermissionRationale会返回false?* - 首次请求权限或者请求权限时,用户勾选了“禁止后不再询问”,之后的请求都会返回false*/if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {//请求过定位权限,但是被用户拒绝了(但是没有勾选“禁止后不再询问”)// 显示解释权限用途的界面,然后再继续请求权限mShowRequestPermissionRationale = true;} else {mShowRequestPermissionRationale = false;}} else {mShowRequestPermissionRationale = false;}AlertDialog.Builder builder = new AlertDialog.Builder(WebViewActivity.this);builder.setTitle("温馨提示");builder.setMessage(String.format("网站%s,正在请求使用您当前的位置,是否许可应用获取当前位置权限?", mOrigin));builder.setPositiveButton(" 是 ", new DialogInterface.OnClickListener() {@Overridepublic 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 {//额,版本低,正常情况下,安装默认许可,然鹅,国产ROM各种魔改,有阔轮提前实现了单独授权doRequestAppSetting();}}});builder.setNegativeButton(" 否 ", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialogInterface, int i) {mGeolocationPermissionsCallback.invoke(mOrigin, false, false);}});builder.create().show();}}@Overridepublic 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);/*** 请求权限结果*/void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults);}private final class JavaScriptInterface {public Activity activity;public JavaScriptInterface(Activity activity) {this.activity = activity;}@JavascriptInterfacepublic void scanCode() {}@JavascriptInterfacepublic void androidGoBack() {activity.finish();}@JavascriptInterfacepublic void call(String phone) {Intent intent = new Intent();intent.setAction(Intent.ACTION_CALL);intent.setData(Uri.parse("tel:" + phone));//开启系统拨号器activity.startActivity(intent);}}
}
特别说明:使用shouldInterceptRequest()方法拼接参数,并不是修改了访问的网页url,只不过是在加载资源的时候对页面进行了一层是否登陆的验证,验证通过则进行加载页面,不通过执行相对于的操作。如果页面有重定向的,则需要在处理重定向的方法中进行验证,验证成功,则加载重定向,验证不成功,不加载,然后执行我们的操作即可。
以上使用的webview是AgentWeb,导入依赖方法:
//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'