2019-11-04 1896
01.基础使用目录介绍
02.优化汇总目录介绍
03.问题汇总目录介绍
在activity中最简单的使用
webview.loadUrl("http://www.baidu.com/"); //加载web资源 //webView.loadUrl("file:///android_asset/example.html"); //加载本地资源 //这个时候发现一个问题,启动应用后,自动的打开了系统内置的浏览器,解决这个问题需要为webview设置 WebViewClient,并重写方法: webview.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); //返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器 return true; } //还可以重写其他的方法 });
那些因素影响页面加载速度
影响页面加载速度的因素有非常多,在对 WebView 加载一个网页的过程进行调试发现
第一种方式:native 调用 js 的方法,方法为:
//java //调用无参方法 mWebView.loadUrl("javascript:callByAndroid()"); //调用有参方法 mWebView.loadUrl("javascript:showData(" + result + ")"); //javascript,下面是对应的js代码 <script type="text/javascript"> function showData(result){ alert("result"=result); return "success"; } function callByAndroid(){ console.log("callByAndroid") showElement("Js:无参方法callByAndroid被调用"); } </script>
第二种方式:
if (Build.VERSION.SDK_INT < 18) { mWebView.loadUrl(jsStr); } else { mWebView.evaluateJavascript(jsStr, new ValueCallback<String>() { @Override public void onReceiveValue(String value) { //此处为 js 返回的结果 } }); }
两种方式的对比
注意问题
第一种方式:通过 addJavascriptInterface 方法进行添加对象映射
mWebView.getSettings().setJavaScriptEnabled(true);
这个函数会有一个警告,因为在特定的版本之下会有非常危险的漏洞,设置完这个属性之后,Native需要定义一个类:
public class JSObject { private Context mContext; public JSObject(Context context) { mContext = context; } @JavascriptInterface public String showToast(String text) { Toast.show(mContext, text, Toast.LENGTH_SHORT).show(); return "success"; } /** * 前端代码嵌入js: * imageClick 名应和js函数方法名一致 * * @param src 图片的链接 */ @JavascriptInterface public void imageClick(String src) { Log.e("imageClick", "----点击了图片"); } /** * 网页使用的js,方法无参数 */ @JavascriptInterface public void startFunction() { Log.e("startFunction", "----无参"); } } //特定版本下会存在漏洞 mWebView.addJavascriptInterface(new JSObject(this), "yc逗比");
JS 代码调用
function showToast(){ var result = myObj.showToast("我是来自web的Toast"); } function showToast(){ myObj.imageClick("图片"); } function showToast(){ myObj.startFunction(); }
第二种方式:利用 WebViewClient 接口回调方法拦截 url
这种方式其实实现也很简单,使用的频次也很高,上面介绍到了 WebViewClient ,其中有个回调接口 shouldOverrideUrlLoading (WebView view, String url)) ,就是利用这个拦截 url,然后解析这个 url 的协议,如果发现是我们预先约定好的协议就开始解析参数,执行相应的逻辑。注意这个方法在 API24 版本已经废弃了,需要使用 shouldOverrideUrlLoading (WebView view, WebResourceRequest request)) 替代,使用方法很类似,我们这里就使用 shouldOverrideUrlLoading (WebView view, String url)) 方法来介绍一下:
public boolean shouldOverrideUrlLoading(WebView view, String url) { //假定传入进来的 url = "js://openActivity?arg1=111&arg2=222",代表需要打开本地页面,并且带入相应的参数 Uri uri = Uri.parse(url); String scheme = uri.getScheme(); //如果 scheme 为 js,代表为预先约定的 js 协议 if (scheme.equals("js")) { //如果 authority 为 openActivity,代表 web 需要打开一个本地的页面 if (uri.getAuthority().equals("openActivity")) { //解析 web 页面带过来的相关参数 HashMap<String, String> params = new HashMap<>(); Set<String> collection = uri.getQueryParameterNames(); for (String name : collection) { params.put(name, uri.getQueryParameter(name)); } Intent intent = new Intent(getContext(), MainActivity.class); intent.putExtra("params", params); getContext().startActivity(intent); } //代表应用内部处理完成 return true; } return super.shouldOverrideUrlLoading(view, url); }
function openActivity(){ document.location = "js://openActivity?arg1=111&arg2=222"; }
//java mWebView.loadUrl("javascript:returnResult(" + result + ")"); //javascript function returnResult(result){ alert("result is" + result); }
第三种方式:利用 WebChromeClient 回调接口的三个方法拦截消息
@Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { return super.onJsAlert(view, url, message, result); } @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { return super.onJsConfirm(view, url, message, result); } @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { //假定传入进来的 message = "js://openActivity?arg1=111&arg2=222",代表需要打开本地页面,并且带入相应的参数 Uri uri = Uri.parse(message); String scheme = uri.getScheme(); if (scheme.equals("js")) { if (uri.getAuthority().equals("openActivity")) { HashMap<String, String> params = new HashMap<>(); Set<String> collection = uri.getQueryParameterNames(); for (String name : collection) { params.put(name, uri.getQueryParameter(name)); } Intent intent = new Intent(getContext(), MainActivity.class); intent.putExtra("params", params); getContext().startActivity(intent); //代表应用内部处理完成 result.confirm("success"); } return true; } return super.onJsPrompt(view, url, message, defaultValue, result); }
和 WebViewClient 一样,这次添加的是WebChromeClient接口,可以拦截JS中的几个提示方法,也就是几种样式的对话框,在 JS 中有三个常用的对话框方法:
function clickprompt(){ var result=prompt("js://openActivity?arg1=111&arg2=222"); alert("open activity " + result); }
以上三种方案的总结和对比
WebView.loadUrl(url)加载网页做了什么?
加载页面的过程中回调哪些方法?
加载页面结束回调哪些方法
onPageFinished()或者onPageStarted()方法中注入js代码
WebViewClient.onProgressChanged()方法中注入js代码
WebViewClient.onProgressChanged()这个方法在dom树渲染的过程中会回调多次,每次都会告诉我们当前加载的进度。
那么有人会问,加载到多少才需要处理js注入逻辑呢?
清除缓存数据的方法有哪些?
//清除网页访问留下的缓存 //由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序. Webview.clearCache(true); //清除当前webview访问的历史记录//只会webview访问历史记录里的所有记录除了当前访问记录 Webview.clearHistory(); //这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据 Webview.clearFormData();
在Manifest文件中,给想要接收跳转的Activity添加配置:
<activity android:name=".X5WebViewActivity" android:configChanges="orientation|screenSize" android:hardwareAccelerated="true" android:launchMode="singleTask" android:screenOrientation="portrait" android:theme="@style/Theme.AppCompat.Light.NoActionBar"> <!--需要添加下面的intent-filter配置--> <intent-filter tools:ignore="AppLinkUrlError"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!--使用http,则只能打开http开头的网页--> <data android:scheme="https" /> </intent-filter> </activity>
然后在 X5WebViewActivity 中获取相关传递数据。具体可以看lib中的X5WebViewActivity类代码。
public class X5WebViewActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_web_view); getIntentData(); initTitle(); initWebView(); webView.loadUrl(mUrl); // 处理 作为三方浏览器打开传过来的值 getDataFromBrowser(getIntent()); } /** * 使用singleTask启动模式的Activity在系统中只会存在一个实例。 * 如果这个实例已经存在,intent就会通过onNewIntent传递到这个Activity。 * 否则新的Activity实例被创建。 */ @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); getDataFromBrowser(intent); } /** * 作为三方浏览器打开传过来的值 * Scheme: https * host: www.jianshu.com * path: /p/yc * url = scheme + "://" + host + path; */ private void getDataFromBrowser(Intent intent) { Uri data = intent.getData(); if (data != null) { try { String scheme = data.getScheme(); String host = data.getHost(); String path = data.getPath(); String text = "Scheme: " + scheme + "\n" + "host: " + host + "\n" + "path: " + path; Log.e("data", text); String url = scheme + "://" + host + path; webView.loadUrl(url); } catch (Exception e) { e.printStackTrace(); } } } }
一些重点说明
至于原因暂时没有找到,解决方案如下所示
/** * 当缩放改变的时候会调用该方法 * @param view view * @param oldScale 之前的缩放比例 * @param newScale 现在缩放比例 */ @Override public void onScaleChanged(WebView view, float oldScale, float newScale) { super.onScaleChanged(view, oldScale, newScale); //视频全屏播放按返回页面被放大的问题 if (newScale - oldScale > 7) { //异常放大,缩回去。 view.setInitialScale((int) (oldScale / newScale * 100)); } }
html代码下载到WebView后,webkit开始解析网页各个节点,发现有外部样式文件或者外部脚本文件时,会异步发起网络请求下载文件,但如果在这之前也有解析到image节点,那势必也会发起网络请求下载相应的图片。在网络情况较差的情况下,过多的网络请求就会造成带宽紧张,影响到css或js文件加载完成的时间,造成页面空白loading过久。解决的方法就是告诉WebView先不要自动加载图片,等页面finish后再发起图片加载。
//初始化的时候设置,具体代码在X5WebView类中 if(Build.VERSION.SDK_INT >= KITKAT) { //设置网页在加载的时候暂时不加载图片 ws.setLoadsImagesAutomatically(true); } else { ws.setLoadsImagesAutomatically(false); } /** * 当页面加载完成会调用该方法 * @param view view * @param url url链接 */ @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); //页面finish后再发起图片加载 if(!webView.getSettings().getLoadsImagesAutomatically()) { webView.getSettings().setLoadsImagesAutomatically(true); } }
当WebView加载页面出错时(一般为404 NOT FOUND),安卓WebView会默认显示一个出错界面。当WebView加载出错时,会在WebViewClient实例中的onReceivedError(),还有onReceivedTitle方法接收到错误
/** * 请求网络出现error * @param view view * @param errorCode 错误 * @param description description * @param failingUrl 失败链接 */ @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); if (errorCode == 404) { //用javascript隐藏系统定义的404页面信息 String data = "Page NO FOUND!"; view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\""); } else { if (webListener!=null){ webListener.showErrorView(); } } } // 向主机应用程序报告Web资源加载错误。这些错误通常表明无法连接到服务器。 // 值得注意的是,不同的是过时的版本的回调,新的版本将被称为任何资源(iframe,图像等) // 不仅为主页。因此,建议在回调过程中执行最低要求的工作。 // 6.0 之后 @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { super.onReceivedError(view, request, error); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { X5WebUtils.log("服务器异常"+error.getDescription().toString()); } //ToastUtils.showToast("服务器异常6.0之后"); //当加载错误时,就让它加载本地错误网页文件 //mWebView.loadUrl("file:///android_asset/errorpage/error.html"); if (webListener!=null){ webListener.showErrorView(); } } /** * 这个方法主要是监听标题变化操作的 * @param view view * @param title 标题 */ @Override public void onReceivedTitle(WebView view, String title) { super.onReceivedTitle(view, title); if (title.contains("404") || title.contains("网页无法打开")){ if (webListener!=null){ webListener.showErrorView(); } } else { // 设置title } }
4.0以上的系统我们开启硬件加速后,WebView渲染页面更加快速,拖动也更加顺滑。但有个副作用就是,当WebView视图被整体遮住一块,然后突然恢复时(比如使用SlideMenu将WebView从侧边滑出来时),这个过渡期会出现白块同时界面闪烁。解决这个问题的方法是在过渡期前将WebView的硬件加速临时关闭,过渡期后再开启
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null); }
webView加载一些别人的url时候,有时候会发生证书认证错误的情况,这时候我们希望能够正常的呈现页面给用户,我们需要忽略证书错误,需要调用WebViewClient类的onReceivedSslError方法,调用handler.proceed()来忽略该证书错误。
/** * 在加载资源时通知主机应用程序发生SSL错误 * 作用:处理https请求 * @param view view * @param handler handler * @param error error */ @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); if (error!=null){ String url = error.getUrl(); X5WebUtils.log("onReceivedSslError----异常url----"+url); } //https忽略证书问题 if (handler!=null){ //表示等待证书响应 handler.proceed(); // handler.cancel(); //表示挂起连接,为默认方式 // handler.handleMessage(null); //可做其他处理 } }
WebView页面中播放了音频,退出Activity后音频仍然在播放,需要在Activity的onDestory()中调用
@Override protected void onDestroy() { try { //有音频播放的web页面的销毁逻辑 //在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview //但是注意:webview调用destory时,webview仍绑定在Activity上 //这是由于自定义webview构建时传入了该Activity的context对象 //因此需要先从父容器中移除webview,然后再销毁webview: if (webView != null) { ViewGroup parent = (ViewGroup) webView.getParent(); if (parent != null) { parent.removeView(webView); } webView.removeAllViews(); webView.destroy(); webView = null; } } catch (Exception e) { Log.e("X5WebViewActivity", e.getMessage()); } super.onDestroy(); }
建立连接/服务器处理;在页面请求的数据返回之前,主要有以下过程耗费时间。
DNS connection 服务器处理
DNS采用和客户端API相同的域名
客户端内的WebView都是可以通过客户端的某个schema打开的,而要打开页面的URL很多都并不写在客户端内,而是可以由URL中的参数传递过去的。上面4.0.5 使用scheme协议打开链接风险已经说明了scheme使用的危险性,那么如何避免这个问题了,设置运行访问的白名单。或者当用户打开外部链接前给用户强烈而明显的提示。具体操作如下所示:
@Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); String host = Uri.parse(url).getHost(); LoggerUtils.i("host:" + host); if (!BuildConfig.IS_DEBUG) { if (Arrays.binarySearch(domainList, host) < 0) { //不在白名单内,非法网址,这个时候给用户强烈而明显的提示 } else { //合法网址 } } }
导致一直占有cpu 耗电特别快,所以如果遇到这种情况,处理方式如下所示。大概意思就是在后台的时候,会调用onStop方法,即此时关闭js交互,回到前台调用onResume再开启js交互。
//在onStop里面设置setJavaScriptEnabled(false); //在onResume里面设置setJavaScriptEnabled(true)。 @Override protected void onResume() { super.onResume(); if (mWebView != null) { mWebView.getSettings().setJavaScriptEnabled(true); } } @Override protected void onStop() { super.onStop(); if (mWebView != null) { mWebView.getSettings().setJavaScriptEnabled(false); } }
提前显示进度条不是提升性能 , 但是对用户体验来说也是很重要的一点 , WebView.loadUrl("url") 不会立马就回调 onPageStarted 或者 onProgressChanged 因为在这一时间段,WebView 有可能在初始化内核,也有可能在与服务器建立连接,这个时间段容易出现白屏,白屏用户体验是很糟糕的 ,所以建议
//正确 pb.setVisibility(View.VISIBLE); mWebView.loadUrl("https://github.com/yangchong211/LifeHelper"); //不太好 @Override public void onPageStarted(WebView webView, String s, Bitmap bitmap) { super.onPageStarted(webView, s, bitmap); //设定加载开始的操作 pb.setVisibility(View.VISIBLE); } //下面这个是监听进度条进度变化的逻辑 mWebView.getX5WebChromeClient().setWebListener(interWebListener); mWebView.getX5WebViewClient().setWebListener(interWebListener); private InterWebListener interWebListener = new InterWebListener() { @Override public void hindProgressBar() { pb.setVisibility(View.GONE); } @Override public void showErrorView() { } @Override public void startProgress(int newProgress) { pb.setProgress(newProgress); } @Override public void showTitle(String title) { } };
WebView 默认开启密码保存功能 mWebView.setSavePassword(true),如果该功能未关闭,在用户输入密码时,会弹出提示框,询问用户是否保存密码,如果选择”是”,密码会被明文保到 /data/data/com.package.name/databases/webview.db 中,这样就有被盗取密码的危险,所以需要通过 WebSettings.setSavePassword(false) 关闭密码保存提醒功能。
/设置是否开启密码保存功能,不建议开启,默认已经做了处理,存在盗取密码的危险 mX5WebView.setSavePassword(false);
进化史如下所示
第一次打开Web面 ,使用WebView加载页面的时候特别慢,第二次打开就能明显的感觉到速度有提升,为什么?
偶发情况,获取不到webView的内容高度
webView.loadData(htmlString, "text/html", "utf-8"); webView.setWebViewClient(new WebViewClient() { public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); Log.d("yc", view.getContentheight() + ""); } });
第一种解决办法:提供onPageFinished()一些延迟
webView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); webView.postDelayed(new Runnable() { @Override public void run() { int contentHeight = webView.getContentHeight(); int viewHeight = webView.getHeight(); } }, 500); } });
常见的用法是在APP获取到来自网页的数据后,重新生成一个intent,然后发送给别的组件使用这些数据。比如使用Webview相关的Activity来加载一个来自网页的url,如果此url来自url scheme中的参数,如:yc://ycbjie:8888/from?load_url=http://www.taobao.com。
使用建议
解决办法
对于WebView加载一个网页过程中所产生的错误回调,大致有三种
/** * 只有在主页面加载出现错误时,才会回调这个方法。这正是展示加载错误页面最合适的方法。 * 然而,如果不管三七二十一直接展示错误页面的话,那很有可能会误判,给用户造成经常加载页面失败的错觉。 * 由于不同的WebView实现可能不一样,所以我们首先需要排除几种误判的例子: * 1.加载失败的url跟WebView里的url不是同一个url,排除; * 2.errorCode=-1,表明是ERROR_UNKNOWN的错误,为了保证不误判,排除 * 3failingUrl=null&errorCode=-12,由于错误的url是空而不是ERROR_BAD_URL,排除 * @param webView webView * @param errorCode errorCode * @param description description * @param failingUrl failingUrl */ @Override public void onReceivedError(WebView webView, int errorCode, String description, String failingUrl) { super.onReceivedError(webView, errorCode, description, failingUrl); // -12 == EventHandle.ERROR_BAD_URL, a hide return code inside android.net.http package if ((failingUrl != null && !failingUrl.equals(webView.getUrl()) && !failingUrl.equals(webView.getOriginalUrl())) /* not subresource error*/ || (failingUrl == null && errorCode != -12) /*not bad url*/ || errorCode == -1) { //当 errorCode = -1 且错误信息为 net::ERR_CACHE_MISS return; } if (!TextUtils.isEmpty(failingUrl)) { if (failingUrl.equals(webView.getUrl())) { //做自己的错误操作,比如自定义错误页面 } } } /** * 只有在主页面加载出现错误时,才会回调这个方法。这正是展示加载错误页面最合适的方法。 * 然而,如果不管三七二十一直接展示错误页面的话,那很有可能会误判,给用户造成经常加载页面失败的错觉。 * 由于不同的WebView实现可能不一样,所以我们首先需要排除几种误判的例子: * 1.加载失败的url跟WebView里的url不是同一个url,排除; * 2.errorCode=-1,表明是ERROR_UNKNOWN的错误,为了保证不误判,排除 * 3failingUrl=null&errorCode=-12,由于错误的url是空而不是ERROR_BAD_URL,排除 * @param webView webView * @param webResourceRequest webResourceRequest * @param webResourceError webResourceError */ @Override public void onReceivedError(WebView webView, WebResourceRequest webResourceRequest, WebResourceError webResourceError) { super.onReceivedError(webView, webResourceRequest, webResourceError); } /** * 任何HTTP请求产生的错误都会回调这个方法,包括主页面的html文档请求,iframe、图片等资源请求。 * 在这个回调中,由于混杂了很多请求,不适合用来展示加载错误的页面,而适合做监控报警。 * 当某个URL,或者某个资源收到大量报警时,说明页面或资源可能存在问题,这时候可以让相关运营及时响应修改。 * @param webView webView * @param webResourceRequest webResourceRequest * @param webResourceResponse webResourceResponse */ @Override public void onReceivedHttpError(WebView webView, WebResourceRequest webResourceRequest, WebResourceResponse webResourceResponse) { super.onReceivedHttpError(webView, webResourceRequest, webResourceResponse); } /** * 任何HTTPS请求,遇到SSL错误时都会回调这个方法。 * 比较正确的做法是让用户选择是否信任这个网站,这时候可以弹出信任选择框供用户选择(大部分正规浏览器是这么做的)。 * 有时候,针对自己的网站,可以让一些特定的网站,不管其证书是否存在问题,都让用户信任它。 * 坑:有时候部分手机打开页面报错,绝招:让自己网站的所有二级域都是可信任的。 * @param webView webView * @param sslErrorHandler sslErrorHandler * @param sslError sslError */ @Override public void onReceivedSslError(WebView webView, SslErrorHandler sslErrorHandler, SslError sslError) { super.onReceivedSslError(webView, sslErrorHandler, sslError); //判断网站是否是可信任的,与自己网站host作比较 if (WebViewUtils.isYCHost(webView.getUrl())) { //如果是自己的网站,则继续使用SSL证书 sslErrorHandler.proceed(); } else { super.onReceivedSslError(webView, sslErrorHandler, sslError); } }
另外一种让图片不超出屏幕范围的方法,可以用的是css
<script type="text/javascript"> var tables = document.getElementsByTagName("img"); //找到table标签 for(var i = 0; i<tables.length; i++){ // 逐个改变 tables[i].style.width = "100%"; // 宽度改为100% tables[i].style.height = "auto"; } </script>
通过webView的setting属性设置
// 网页内容的宽度是否可大于WebView控件的宽度 ws.setLoadWithOverviewMode(false);
Android和js如何通信
这类应用程序一般都会有类似如下的代码:
webView.addJavascriptInterface(javaObj, "jsObj");
此段代码将javaObj对象暴露给js脚本,可以通过jsObj对象对其进行引用,调用javaObj的方法。结合Java的反射机制可以通过js脚本执行任意Java代码,相关代码如下:
<script> function execute(cmdArgs) { return jsobj.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs); } execute(someCmd); </script>
addJavascriptInterface任何命令执行漏洞
@JavascriptInterface注解做了什么操作
代码里怎么设置Cookie,如下所示
/** * 同步cookie * * @param url 地址 * @param cookieList 需要添加的Cookie值,以键值对的方式:key=value */ private void syncCookie (Context context , String url, ArrayList<String> cookieList) { //初始化 CookieSyncManager.createInstance(context); //获取对象 CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true); //移除 cookieManager.removeSessionCookie(); //添加 if (cookieList != null && cookieList.size() > 0) { for (String cookie : cookieList) { cookieManager.setCookie(url, cookie); } } String cookies = cookieManager.getCookie(url); X5LogUtils.d("cookies-------"+cookies); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { cookieManager.flush(); } else { CookieSyncManager.getInstance().sync(); } }
在android里面在调用webView.loadUrl(url)之前一句调用此方法就可以给WebView设置Cookie
webView.getSettings().setBuiltInZoomControls(true); webView.getSettings().setJavaScriptEnabled(true);
还有跨域问题: 域A: test1.yc.com 域B: test2.yc.com
Cookie的过期机制
可以设置Cookie的生效时间字段名为: expires 或 max-age。
下面列出几个有用的接口:
webView从Lollipop(5.0)开始webView默认不允许混合模式, https当中不能加载http资源, 而开发的时候可能使用的是https的链接, 但是链接中的图片可能是http的, 所以需要设置开启。
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); } mWebView.getSettings().setBlockNetworkImage(false);
webviewClient中有onReceivedError方法,当出现证书校验错误时,我们可以在该方法中使用handler.proceed()来忽略证书校验继续加载网页,或者使用默认的handler.cancel()来终端加载。
@SuppressLint("NewApi") @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { //handler.proceed();// 接受证书 super.onReceivedSslError(view, handler, error); }
如果webView.getSettings().setAllowFileAccess(boolean)设置为true,则会面临该问题;该漏洞是通过WebView对Javascript的延时执行和html文件替换产生的。
问题描述
首先载入js
//将js对象与java对象进行映射 webView.addJavascriptInterface(new ImageJavascriptInterface(context), "imagelistener");
html加载完成之后,添加监听图片的点击js函数,这个可以在onPageFinished方法中操作
@Override public void onPageFinished(WebView view, String url) { X5LogUtils.i("-------onPageFinished-------"+url); //html加载完成之后,添加监听图片的点击js函数 //addImageClickListener(); addImageArrayClickListener(webView); }
具体看addImageArrayClickListener的实现方法。
/** * android与js交互: * 首先我们拿到html中加载图片的标签img. * 然后取出其对应的src属性 * 循环遍历设置图片的点击事件 * 将src作为参数传给java代码 * 这个循环将所图片放入数组,当js调用本地方法时传入。 * 当然如果采用方式一获取图片的话,本地方法可以不需要传入这个数组 * 通过js代码找到标签为img的代码块,设置点击的监听方法与本地的openImage方法进行连接 * @param webView webview */ private void addImageArrayClickListener(WebView webView) { webView.loadUrl("javascript:(function(){" + "var objs = document.getElementsByTagName(\"img\"); " + "var array=new Array(); " + "for(var j=0;j<objs.length;j++){" + " array[j]=objs[j].src; " + "}"+ "for(var i=0;i<objs.length;i++) " + "{" + " objs[i].onclick=function() " + " { " + " window.imagelistener.openImage(this.src,array); " + " } " + "}" + "})()"); }
最后看看js的通信接口做了什么
public class ImageJavascriptInterface { private Context context; private String[] imageUrls; public ImageJavascriptInterface(Context context,String[] imageUrls) { this.context = context; this.imageUrls = imageUrls; } public ImageJavascriptInterface(Context context) { this.context = context; } /** * 接口返回的方式 */ @android.webkit.JavascriptInterface public void openImage(String img , String[] imageUrls) { Intent intent = new Intent(); intent.putExtra("imageUrls", imageUrls); intent.putExtra("curImageUrl", img); // intent.setClass(context, PhotoBrowserActivity.class); context.startActivity(intent); for (int i = 0; i < imageUrls.length; i++) { Log.e("图片地址"+i,imageUrls[i].toString()); } } }
在有些需求中会有一些吸顶的元素,例如导航条,购买按钮等;当页面滚动超出元素高度后,元素吸附在屏幕顶部。在WebView中成了难题:在页面滚动期间,Scroll Event不触发。不仅如此,WebView在滚动期间还有各种限定:
这些限制让WebView在滚动期间很难有较好的体验。这些限制大部分是不可突破的,但至少对于吸顶功能还是可以做一些支持,解决方法:
由于WebView加载的页面代码是从服务器动态获取的,这些代码将会很容易被中间环节所窃取或者修改,其中最主要的问题出自地方运营商和一些WiFi。监测到的问题包括:
针对页面注入的行为,有一些解决方案:
2.HTTPS。
3.App使用Socket代理请求
在资源预加载方面,其实也有很多种方式,下面主要列举了一些:
getContentHeight 返回的是整个html的高度,但并不等同于当前整个页面的高度,因为WebView有缩放功能,所以当前整个页面的高度实际上应该是原始html 的高度再乘上缩放比例. 因此,更正后的结果,准确的判断方法应该是:
if(WebView.getContentHeight*WebView.getScale() == (webview.getHeight()+WebView.getScrollY())){ //已经处于底端 }
可以通过使用 WebView.loadData(String data, String mimeType, String encoding)) 方法来加载一整个 HTML 页面的一小段内容,第一个就是我们需要 WebView 展示的内容,第二个是我们告诉 WebView 我们展示内容的类型,一般,第三个是字节码,但是使用的时候,这里会有一些坑
String html = new String("<h3>我是loadData() 的标题</h3><p>  我是他的内容</p>"); webView.loadData(html, "text/html", "UTF-8");
使用loadData()或 loadDataWithBaseURL()加载一段HTML代码片段
data:是要加载的数据类型,但在数据里面不能出现英文字符:'#', '%', '' , '?' 这四个字符,如果有的话可以用 %23, %25, %27, %3f,这些字符来替换,在平时测试时,你的数据时,你的数据里含有这些字符,但不会出问题,当出问题时,你可以替换下。
String html = new String("<h3>我是loadData() 的标题</h3><p>  我是他的内容</p>"); webView.loadData(html, "text/html;charset=UTF-8", "null");
专业叙述
网络解释
举个例子
实现WebView的滑动监听和优雅处理回退栈问题
感谢开源库
参考博客
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。