WebView的使用及交互方式

先记录一些使用技巧:
WebView有3个子类,可联合他们一起使用
WebSettings:对webView进行管理和配置
WebViewClient:处理通知和请求事件
WebChromeClient:处理js的对话框等

1、加载开始的时候显示loading,加载完成关闭loading

webView.setWebViewClient(new WebViewClient(){

            @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);
            }
        });

//支持js
webSettings.setJavaScriptEnabled(true);

//允许js弹窗
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

//支持插件
webSettings.setPluginsEnabled(true);

//设置编码格式
webSettings.setDefaultTextEncodingName(“utf-8”);

//缓存相关
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);

//LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
//LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
//LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
//LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。

//离线加载

if (NetStatusUtil.isConnected(getApplicationContext())) {
    webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。
} else {
    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载
}

//https请求,默认不能处理https请求,显示的是空白

webView.setWebViewClient(new WebViewClient() { 
@Override public void onReceivedSslError(WebView view, 
SslErrorHandler handler, SslError error) { 
handler.proceed(); 
// handler.cancel(); 
// handler.handleMessage(null); } });
//onReceivedSslError为webView处理ssl证书设置

//其中handler.proceed();表示等待证书响应
//handler.cancel();表示挂起连接,为默认方式
//handler.handleMessage(null);可做其他处理

交互

一、native调js,都需要在子线程中执行。
1、Android调用js无参无返回值方法:
首先要在js代码中添加被调用的方法名比如:

function sayHello() {
alert("我是无参无返回toast") 
}

然后在android代码中:格式如下
webview.loadUrl(“javascript:sayHello()”);
2、Android调用js有参无返回值其实和第一种一样,将参数写在方法里面就行了。

3、Android调用js有参有返回值(API4.4以前)
在4.4之前没有一个方法是直接调js代码然后可以返回值的。所以4.4之前的思路是通过调js方法,然后js调android方法将值传回。
比如js代码:

function sumToJava(number1, number2){ 
window.control.onSumResult(number1 + number2) 
}

native调用js方法,子线程中执行:

webview.loadUrl(“javascript:sayHello(1,2)”);

js将最终的值传给native:

@JavascriptInterface
public void onSumResult(int result) {   
Toast.makeText(getApplicationContext(), "我是android调用js方法(4.4前),结果是" + result, Toast.LENGTH_LONG).show();
}

4.4之后,有了一个方法直接调用js代码并直接拿到返回值:
js代码:

function sumToJava2(number1, number2) {
return number1 + number2;
}

native代码:

@TargetApi(Build.VERSION_CODES.KITKAT)
public void Android2JsHaveParmHaveResult2(View view) { 

 mWebView.evaluateJavascript("sumToJava2(3,4)", new ValueCallback<String>() {
@Override 
public void onReceiveValue(String Str) {   
Toast.makeText(getApplicationContext(), "我是android调用js方法(4.4后),入参是3和4,js返回结果是" + Str, Toast.LENGTH_LONG).show(); 
  }    
});}

二、js调native
1、对象映射
在native代码中定义与js建立对象映射的管理类,比如JsToAndroid.java

public class JsToAndroid extends Object {
    // 定义JS需要调用的方法
    // 被JS调用的方法必须加入@JavascriptInterface注解
    @JavascriptInterface
    public void hello(String msg) {
        System.out.println(msg);
    }
}

然后js代码中调用:
function callNative(){
window.JsToAndroid .hello(“你好”);
}

这样,native的hello方法就得到执行了。

2、拦截uri
这种方式需要native和js端约定好uri的格式,然后native去拦截js端的uri并解析,然后做进一步操作

js代码:

function callAndroid(){
            /*约定的url协议为:js://webview?arg1=111&arg2=222*/
            document.location = "js://webview?arg1=111&arg2=222";
         }

native拦截并解析:

// 复写WebViewClient类的shouldOverrideUrlLoading方法
mWebView.setWebViewClient(new WebViewClient() {
                                      @Override
                                      public boolean shouldOverrideUrlLoading(WebView view, String url) {

                                          // 步骤2:根据协议的参数,判断是否是所需要的uri
                                          // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
                                          //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)

                                          Uri uri = Uri.parse(url);                                 
                                          // 如果url的协议 = 预先约定的 js 协议
                                          // 就解析往下解析参数
                                          if ( uri.getScheme().equals("js")) {

                                              // 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议
                                              // 所以拦截url,下面JS开始调用Android需要的方法
                                              if (uri.getAuthority().equals("webview")) {

                                                 //  步骤3:
                                                  // 执行JS所需要调用的逻辑
                                                  System.out.println("js调用了Android的方法");
                                                  // 可以在协议上带有参数并传递到Android上
                                                  HashMap<String, String> params = new HashMap<>();
                                                  Set<String> collection = uri.getQueryParameterNames();

                                              }

                                              return true;//拦截
                                          }
                                          return super.shouldOverrideUrlLoading(view, url);
                                      }
                                  }
        );  

3、native拦截js的对话框alert()、confirm()、prompt()
js代码:

function clickprompt(){
    // 调用prompt()
    var result=prompt("js://demo?arg1=111&arg2=222");
    alert("demo " + result);
}

然后native去拦截:

 mWebView.setWebChromeClient(new WebChromeClient() {
                                        // 拦截输入框(原理同方式2)
                                        // 参数message:代表promt()的内容(不是url)
                                        // 参数result:代表输入框的返回值
                                        @Override
                                        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
                                            // 根据协议的参数,判断是否是所需要的url(原理同方式2)
                                            // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)
                                            //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的)

                                            Uri uri = Uri.parse(message);
                                            // 如果url的协议 = 预先约定的 js 协议
                                            // 就解析往下解析参数
                                            if ( uri.getScheme().equals("js")) {

                                                // 如果 authority  = 预先约定协议里的 webview,即代表都符合约定的协议
                                                // 所以拦截url,下面JS开始调用Android需要的方法
                                                if (uri.getAuthority().equals("webview")) {

                                                    //
                                                    // 执行JS所需要调用的逻辑,比如打印
                                                    System.out.println("js调用了Android的方法");
                                                    // 可以在协议上带有参数并传递到Android上
                                                    HashMap<String, String> params = new HashMap<>();
                                                    Set<String> collection = uri.getQueryParameterNames();

                                                    //参数result:代表消息框的返回值(输入值)
                                                    result.confirm("js调用了Android的方法成功啦");
                                                }
                                                return true;
                                            }
                                            return super.onJsPrompt(view, url, message, defaultValue, result);
                                        }

// 通过alert()和confirm()拦截的原理相同,此处不作过多讲述

                                        // 拦截JS的警告框
                                        @Override
                                        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                                            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调Android有一些问题已经webview本身有内存泄漏的问题。

漏洞1、对象映射的方式可能会让js攻击者获取手机的信息,因为是通过对象访问的方式去调用native代码,有可能会拿到系统级的类,然后拿到sd卡中的信息。
漏洞2、webview如果设置了保存密码的话,那么密码会被保存在应用程序的数据库中,那么就有可能被别人窃取。
漏洞3、域控制不严漏洞,就是通过uri的方式交互,可能会被利用。
漏洞4、onPageFinished 函数不一定有用。

内存泄漏:
webview默认是持有Activity的引用的,所以关闭Activity的时候要确保webview被销毁。但webview销毁有一些坑爹的东西:目前最好的方案是,当要用webview的时候,最好单独开一个进程去使用webview 并且当这个进程结束时,手动调用System.exit(0)。
这是目前对于webview 内存泄露 最好的解决方案。使用此方法 所有因为webview引发的 资源无法释放等问题 全部可以解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值