package com.shuyu; import android.annotation.TargetApi; import android.graphics.Bitmap; import android.net.Uri; import android.net.http.SslError; import android.os.Build; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.webkit.DownloadListener; import android.webkit.JavascriptInterface; import android.webkit.JsPromptResult; import android.webkit.JsResult; import android.webkit.SslErrorHandler; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.FrameLayout; import android.widget.Toast; import java.util.Set; /** * 演示WebView中的Api说明、js交互的方法,还有注意事项 * <p> * 1、内存泄漏防备 * 2、配置webView * 3、页面加载开始,错误,拦截请求,接受Error等 * 4、页面加载进度,title,图标,js弹框等 * 5、js交互与安全 */ public class APIWebViewActivity extends AppCompatActivity implements View.OnClickListener { FrameLayout mRootLayout; WebView mWebView; Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_api_webview); findViewById(R.id.call_js_function).setOnClickListener(this); //添加webView到布局中 addWebViewToLayout(); //set webView Setting setWebView(); //set webView Client setWebClient(); //set webView chrome setWebViewChromeClient(); //load web loadUrl(); } /** * 主动清空销毁来避免内存泄漏 */ @Override protected void onDestroy() { if (mWebView != null) { mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null); mWebView.clearHistory(); ((ViewGroup) mWebView.getParent()).removeView(mWebView); mWebView.destroy(); mWebView = null; } super.onDestroy(); } /** * 1、不在xml中定义Webview,而是在需要的时候在Activity中创建 * 使用getApplicationgContext(),避免内存泄漏。 * <p> * 2、当然,你也可以配置webView所在Activity, * 在AndroidManifest中的进程为:android:process=":remote" * 避免泄漏影响主进程 **/ void addWebViewToLayout() { mRootLayout = (FrameLayout) findViewById(R.id.js_root_layout); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); mWebView = new WebView(getApplicationContext()); mWebView.setLayoutParams(params); mRootLayout.addView(mWebView); } /** * 配置webView */ void setWebView() { //声明WebSettings子类 WebSettings webSettings = mWebView.getSettings(); //支持Javascript交互 webSettings.setJavaScriptEnabled(true); //设置自适应屏幕,两者合用 webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小 webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小 //缩放操作 webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。 webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放 webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件 //其他细节操作 //webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存 webSettings.setAllowFileAccess(true); //设置可以访问文件 //对于不需要使用 file 协议的应用,禁用 file 协议;防止文件泄密,file协议即是file:// //webSettings.setAllowFileAccess(false); //webSettings.setAllowFileAccessFromFileURLs(false); //webSettings.setAllowUniversalAccessFromFileURLs(false); webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口 webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片 webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式 mWebView.setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { //网页中触发下载动作 } }); //增加js交互接口 mWebView.addJavascriptInterface(new JsCallAndroidInterface(), "JSCallBackInterface"); } /** * 设置webView的Client,如页面加载开始,错误,拦截请求,接受Error等 */ void setWebClient() { mWebView.setWebViewClient(new WebViewClient() { //拦截页面中的url加载,21以下的 @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (resolveShouldLoadLogic(url)) { return true; } return super.shouldOverrideUrlLoading(view, url); } //拦截页面中的url加载,21以上的 @TargetApi(21) @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { if (resolveShouldLoadLogic(request.getUrl().toString())) { return true; } 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) { super.onPageFinished(view, url); } //页面加载每一个资源,如图片 @Override public void onLoadResource(WebView view, String url) { super.onLoadResource(view, url); } //监听WebView发出的请求并做相应的处理 //浏览器的渲染以及资源加载都是在一个线程中,如果在shouldInterceptRequest处理时间过长,WebView界面就会阻塞 //21以下的 @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { return super.shouldInterceptRequest(view, url); } //监听WebView发出的请求并做相应的处理 //浏览器的渲染以及资源加载都是在一个线程中,如果在shouldInterceptRequest处理时间过长,WebView界面就会阻塞 //21以上的 @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { return super.shouldInterceptRequest(view, request); } //页面加载出现错误,23以下的 @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); switch (errorCode) { case 404: //view.loadUrl("加载一个错误页面提示,优化体验"); break; } } //页面加载出现错误,23以上的 @TargetApi(23) @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { super.onReceivedError(view, request, error); switch (error.getErrorCode()) { case 404: //view.loadUrl("加载一个错误页面提示,优化体验"); break; } } //https错误 @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); } }); } /** * 设置webView的辅助功能,如页面加载进度,title,图标,js弹框等 */ void setWebViewChromeClient() { mWebView.setWebChromeClient(new WebChromeClient() { //页面加载进度 @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); } //获取标题 @Override public void onReceivedTitle(WebView view, String title) { super.onReceivedTitle(view, title); } //获取图标 @Override public void onReceivedIcon(WebView view, Bitmap icon) { super.onReceivedIcon(view, icon); } //是否支持页面中的js警告弹出框 @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { Toast.makeText(APIWebViewActivity.this, message, Toast.LENGTH_SHORT).show(); return super.onJsAlert(view, url, message, result); } //是否支持页面中的js确定弹出框 @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { return super.onJsConfirm(view, url, message, result); } //是否支持页面中的js输入弹出框 @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { /** * 有时候,为了安全考虑,js的参数回调,会通过这类地方回调回来,然后不弹出框。 */ if(resolveJSPrompt(message)) { return true; } return super.onJsPrompt(view, url, message, defaultValue, result); } }); } /** * 加载url */ void loadUrl() { // 格式规定为:file:///android_asset/文件名.html mWebView.loadUrl("file:///android_asset/localHtml.html"); //方式1. 加载远程网页: //mWebView.loadUrl("http://www.google.com/"); //方式2:加载asset的html页面 //mWebView.loadUrl("file:///android_asset/localHtml.html"); //方式3:加载手机SD的html页面 //mWebView.loadUrl("file:///mnt/sdcard/database/taobao.html"); } /** * 执行网页中的js方法 */ void callJsFunction() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { //接受返回值 } }); } else { mWebView.loadUrl("javascript:callJS()"); } } /** * js与web交互1 * js 与 原生交互接口 */ private class JsCallAndroidInterface { //@JavascriptInterface注解方法,js端调用,4.2以后安全 //4.2以前,当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。 @JavascriptInterface public void callback(String msg) { Toast.makeText(APIWebViewActivity.this, "JS方法回调到web了 :" + msg, Toast.LENGTH_SHORT).show(); } } /** * js与web交互2 * 通过 shouldOverrideUrlLoading 与 js交互 */ private boolean resolveShouldLoadLogic(String url) { Uri uri = Uri.parse(url); //解析协议 if (uri.getScheme().equals("js")) { if (uri.getAuthority().equals("Authority")) { //你还可以继续接续参数 //Set<String> collection = uri.getQueryParameterNames(); Toast.makeText(APIWebViewActivity.this, "JS 2方法回调到web了", Toast.LENGTH_SHORT).show(); } return true; } return false; } /** * js与web交互3 * 通过 onJsPrompt 与 js交互 */ private boolean resolveJSPrompt(String message) { Uri uri = Uri.parse(message); if ( uri.getScheme().equals("js")) { if (uri.getAuthority().equals("Authority")) { //Set<String> collection = uri.getQueryParameterNames(); //参数result:代表消息框的返回值(输入值) //result.confirm("JS 3方法回调到web"); Toast.makeText(APIWebViewActivity.this, "JS 3方法回调到web了", Toast.LENGTH_SHORT).show(); } return true; } return false; } @Override public void onClick(View v) { switch (v.getId()) { case R.id.call_js_function: callJsFunction(); break; } } }