WebView初使用心得

基本设置

WebView可以实现将网页嵌入到为应用程序专门设计的环境中。大多数情况还是建议使用标准的网络浏览器将内容交互给用户。WebView缺少一些完全开发的浏览器功能。

private Context mContext;
private WebView mWebView;
/**
 * WebView父亲布局
 */
private LinearLayout llContainerWeb;

//可以使用addView的方式,也可以使用xml方式,只要记得必要的时候移除view、清除缓存和置空即可
mWebView = new WebView(mContext);
ViewGroup.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
//把webView加入llContainerWeb
llContainerWeb.addView(mWebView, layoutParams);

//不显示横向滑动条
mWebView.setHorizontalScrollBarEnabled(false);
//不显示纵向滑动条
mWebView.setVerticalScrollBarEnabled(false);

WebSettings webSettings = mWebView.getSettings();
//设置是否启用javascript
webSettings.setJavaScriptEnabled(true);
//是否支持缩放(这个是前提)
webSettings.setSupportZoom(false);
//是否显示缩放工具(当上门那个成立,再设置这个,必要条件)
webSettings.setBuiltInZoomControls(false);
//设置此属性,可任意比例缩放
webSettings.setUseWideViewPort(false);
//设置充满全屏
webSettings.setLoadWithOverviewMode(true);
//把所有内容放到WebView组件等宽的一列中
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
//设置字体默认大小
/*webSettings.setDefaultFontSize(15);*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    //设置不允许安全来源从不安全的位置加载内容起源,非常建议使用这个。
    //webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
    //不建议使用这个,从任何来源加载内容。5.1以上默认禁止了https和http混用 这是开启
    //webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
//如下设置是解决Cross origin requests are only supported for protocol schemes: http, data, chrome, https.这种问题的。
//webSettings.setAllowFileAccess(true);
//webSettings.setAllowFileAccessFromFileURLs(true);
//能否访问来自于任何源的文件标识的URL
//webSettings.setAllowUniversalAccessFromFileURLs(true);

//设置WebViewClient,可以拦截url,做一些处理
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    mWebView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            //拦截url:根据协议的参数,判断是否是所需要的url
            //比如约定好的字段为"https://www.haizhuo.com/user?name=hz&id=15"
            Uri uri = request.getUrl();
            if (uri.getScheme().equals("https")) {
                if (uri.getHost().equals("www.haizhuo.com")) {
                    //todo
                }
            }
            return super.shouldOverrideUrlLoading(view, request);
        }
    });
} else {
    mWebView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Uri uri = Uri.parse(url);
            if (uri.getScheme().equals("https")) {
                if (uri.getHost().equals("www.haizhuo.com")) {
                    //todo
                }
            }
            return super.shouldOverrideUrlLoading(view, url);
        }
    });
}

//设置WebChromeClient,可以获得js回调、进度、标题、图标等的回调。
mWebView.setWebChromeClient(new 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) {
        return super.onJsPrompt(view, url, message, defaultValue, result);
    }

    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        super.onProgressChanged(view, newProgress);
    }

    @Override
    public void onReceivedTitle(WebView view, String title) {
        super.onReceivedTitle(view, title);
    }
});

mWebView.loadUrl("https://www.baidu.com/");

//直接加载assets目录下的html网页,并且与网页有关的css,js,图片等文件会加载。
//mWebView.loadUrl("file:///android_asset/test/test.html");


@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (event.getKeyCode()==KeyEvent.KEYCODE_BACK&&mWebView.canGoBack()){
        //获取webView的浏览记录
        WebBackForwardList webBackForwardList=mWebView.copyBackForwardList();
        //有上一个界面,就跳转上一个界面
        if (webBackForwardList.getCurrentIndex()>0){
            mWebView.goBack();
            return true;
        }
    }
    return super.onKeyDown(keyCode, event);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    //使用完毕,清除webview缓存和历史,避免内存泄露
    mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
    mWebView.clearHistory();
//    mWebView.clearCache(true);
//    mWebView.clearSslPreferences();
    //不要在android.graphics.Canvas#draw、dispatchDraw、onDraw方法里调用此方法
//    webView.removeAllViews();
    
    ((ViewGroup) mWebView.getParent()).removeView(mWebView);
    mWebView.destroy();
    mWebView = null;
}

Android调用JS代码

注意事项:JS代码调用要在 onPageFinished() 回调之后。

通过WebView的loadUrl()

一般样式

String url = "javascript:methodName(params……)"; 
webView.loadUrl(url); 

例1:调用网页端写好的方法

<script type="text/javascript">
    function testPop(){
            alert("17张牌你能秒我?");
    }
</script>
mWebView.loadUrl("javascript:testPop()"); 

例2:调用在客户端写的方法(网页端没有)

//设置网页中所有标签名字为‘img’的对象高宽
mWebView.loadUrl("javascript:(function(){" +
                "var objs = document.getElementsByTagName('img'); " +
                "for(var i=0;i<objs.length;i++)  " +
                "{"
                + "var img = objs[i];   " +
                "    img.style.maxWidth = '100%'; img.style.height = 'auto';  " +
                "}" +
                "})()");

通过WebView的evaluateJavascript()

mWebView.evaluateJavascript("javascript:test()", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            //此处为返回的结果
        }
    });

2个方法对比

image

JS通过WebView调用 Android 代码

不管通过什么方式,双方都需要做一个约定。比如通过WebChromeClient拦截JS对话框消息,当消息是某约定的字段,Android客户端做某个操作;再如通过WebViewClient拦截url,当网页跳转到某个约定的地址时,Android客户端做某个操作;还有WebView方法addJavascriptInterface,也要约定对象映射的名字,这样,js端才能通过这个“名字”的对象调用方法。

1. 通过对象映射

WebView的addJavascriptInterface()。addJavascriptInterface是WebKit的原生API,属于WebView对象的公共方法,用于暴露一个java对象给js,使得js可以直接调用方法。当然,java与js的相互调用也离不开loadUrl()方法的配合使用。

例子

先写一个映射关系的类

class MyJsInterfaceTest{


    //API 17以后一定要添加注解,否则addJavascriptInterface方法会失效。
    @SuppressLint("JavascriptInterface")
    @JavascriptInterface
    public String getAppNameFromAndroid(){
        return MyApplication.getInstance().getAppName();
    }
}

然后设置WebView

webView.getSettings().setJavaScriptEnabled(true);
//...省略部分代码
webView.addJavascriptInterface(new MyJsInterfaceTest(), "MyJsInterfaceTest");

在html里通过映射关系类调用方法

<script type="text/javascript">
        //调用Android端代码获取名字,并弹框显示
        function getName(){
             alert(myJsTest.getName());
        }
</script>
//...省略部分代码
<button class="bt_todo_java" id="button_one" onclick="getName()">js调用Android代码</button>

2. 拦截url

WebViewClient 的shouldOverrideUrlLoading ()方法

当即将在当前WebView中加载URL时,使主机应用程序有机会控制。 如果未提供WebViewClient,则默认情况下,WebView将要求“活动管理器”为URL选择适当的处理程序。 如果提供了WebViewClient,则返回true会导致当前WebView放弃加载URL,而返回false会导致WebView照常继续加载URL。

WebView在加载一个页面开始的时候会回调onPageStarted方法,在该页面加载完成之后会回调onPageFinished方法。而如果该链接发生了重定向,回调shouldOverrideUrlLoading会在回调onPageFinished之前。

示例

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    mWebview.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            //拦截url:根据协议的参数,判断是否是所需要的url
            //比如约定好的字段为"https://www.haizhuo.com/user?name=hz&id=15"
            Uri uri = request.getUrl();
            if (uri.getScheme().equals("https")) {
                if (uri.getHost().equals("www.haizhuo.com")) {
                    //todo
                }
            }

            return super.shouldOverrideUrlLoading(view, request);
        }
    });
} else {
    mWebview.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            Uri uri = Uri.parse(url);
            if (uri.getScheme().equals("https")) {
                if (uri.getHost().equals("www.haizhuo.com")) {
                    //todo
                }
            }
            return super.shouldOverrideUrlLoading(view, url);
        }
    });
}

shouldOverrideUrlLoading不执行的情况

通常情况,直接与用户交互,跳转的地址,shouldOverrideUrlLoading是能拦截到的。
延时跳转的话(比如在某个回调里跳转),之后的跳转shouldOverrideUrlLoading都拦截不到了,但会将请求的页面加载到webview中

以example.html为例

//...省略其他代码

function goToResultPage(){
    //本页跳转
    window.location.href='http://www.myresult.com.cn';
}


//延时跳转
setTimeout(goToResultPage, 3000);

分析:正常操作到example.html,url都有拦截到,但是跳转到http://www.myresult.com.cn及他以后的地址shouldOverrideUrlLoading都不会有回调了。

3. 拦截JS对话框

WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息
除此之外,还有onProgressChanged()、onReceivedTitle()、onReceivedTouchIconUrl()等一些列方法。

上述3方法对比

image

补充知识点

WebViewClient

WebViewClient这个组件主要用来辅助webview处理一些简单的html页面内容,比如各种通知,请求事件等等,也就是说在用这个组件你可以监听网页加载是否完成,是否开始等等,然后还有就是可以设置是否在外部浏览器中浏览。

onLoadResource、onPageStart、onPageFinish、onReceiveError、onReceivedHttpAuthRequest、shouldOverrideUrlLoading

WebChromeClient

WebChromeClient可以设置标题啥的。

关于在js中打印日志,java中获取打印信息以及提示、打印回调
  • 在 Javascript 代码中调用console.log(‘xxx’)方法,中Native代码会回调WebChromeClient.consoleMessage()
  • JS中调用window.prompt(message, value),WebChromeClient.onJsPrompt()就会收到回调信息
  • JS中调用 alert(“信息”),WebChromeClient.onJsAlert()就会收到回调信息

除上面几个外,再记录一些(不是全部):onReceivedTitle、onReceivedIcon、onReceivedTouchIconUrl、onHideCustomView、onRequestFocus、onCloseWindow、onJsBeforeUnload、onGeolocationPermissionsShowPrompt、onGeolocationPermissionsHidePrompt、onPermissionRequest、onPermissionRequestCanceled、onConsoleMessage、getDefaultVideoPoster、getVisitedHistory等

跨域

什么是跨域?什么是Cross-origin_resource_sharing?

跨域请求存在的原因:由于浏览器的同源策略,即属于不同域的页面之间不能相互访问各自的页面内容。

跨域使用的场景?

  • 域名不同
    www.jiuyescm.com和www.jiuye.com即为不同的域名
  • 二级域名相同,子域名不同
    a.jiuyescm.com和b.jiuyescm.com为子域不同
  • 端口不同,协议不同
    http://www.jiuyescm.com 和 https://www.jiuyescm.com
    www.jiuyescm.com:8888和www.jiuyescm.com:8080

解决跨域的方式?

  • jsonp
    安全性差,已经不推荐
  • CORS(W3C标准,跨域资源共享 - Cross-origin resource sharing)
    服务端设置,安全性高,推荐使用
  • websocke
    特殊场景时使用,不属于常规跨域操作
  • 代理服务(nginx)
    可作为服务端cors配置的一种方式,推荐使用

参考文章

最全面总结 Android WebView与 JS 的交互方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章