Android与js交互

WebView 使用和方法(持更)

2020-02-27  本文已影响0人  tingtingtina

正式项目经常是Android 和 iOS 同时开发的,使用H5进行部分开发,可以减少开发成本,又便于更新,所以在实际项目中会用 WebView 做很多事情。
在开发当中也遇到很多的问题和深坑,WebView 虽然主要类不多,但是方法甚多。再此做知识总结,也为有同样需求和问题的同仁们提供微薄的帮助,其中也借鉴了很多公开的解决方案,会附上相应传送门。
如果内容有帮助到你,请给个大拇哥❤

先看下导图(由于内容较多,分开更新)
Part 1 传送门 WebView 使用和方法
Part 2 传送门 常见功能 & 问题

1. WebView 基本使用

1.1 基本使用

在 Android 应用开发中会经常要嵌套H5来进行混合式开发,WebView是不可或缺的容器。

使用 WebView 加载一个网页很容易

  1. AndroidManifest.xml 中添加网络权限
<uses-permission android:name="android.permission.INTERNET"/>
  1. 在布局中添加 WebView
<WebView
    android:id="@+id/webView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
/>
  1. ActivityFragment 中获取控件
WebView mWebview = findViewById(R.id.webView);
// 也可以通过 new 的形式创建 WebView
  1. 加载目标地址
webView.loadUrl("https://developer.android.google.cn/");

so easy~
WebView 有很丰富的功能,继续学习

1.2 加载页面

//加载一个远程网页
webView.loadUrl("https://developer.android.google.cn/");

// 加载assets中资源
webView.loadUrl("file:///android_asset/test.html");

//加载sdcard中子源
webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");

使用 loadUrl,不过需要注意,这里因为是使用本地数据,所以传入的url需要做些处理,例如:
  a 如果html文件存于assets:则加前缀:file:///android_asset/
  b 如果html文件存于sdcard:则加前缀:content://com.android.htmlfileprovider/sdcard/
  注意:content 前缀可能导致异常,也可使用file:///sdcard/ 或者 file:/sdcard 做前缀。

2. 加载设置

2.1 页面自适应屏幕

settings.setUseWideViewPort(true); // 将图片调整到适合webview的大小
settings.setLoadWithOverviewMode(true);  // 缩放至屏幕的大小

2.2 缩放

settings.setSupportZoom(true);//启用缩放功能
settings.setBuiltInZoomControls(true);//使用WebView内置的缩放功能
settings.setDisplayZoomControls(false);//隐藏屏幕中的虚拟缩放按钮

2.3 个性化设置

settings.setTextZoom(100);//字体百分比,替代原API:setTextSize
settings.setMediaPlaybackRequiresUserGesture(false);//SDK>18 是否支持手势控制网页媒体,比如视频的全屏

2.4 Https 加载 Http 混合模式

当 WebView 加载 https 的地址中有 http 的地址时(比如 https 地址含有 http 的图片) WebView 无法加载 http 的资源

原因是 Android 5.0 (Lollipop)开始,WebView 默认不支持同时加载 Https 和 Http 混合模式。此时可以通过 setMixedContentMode() 方法设置混合模式

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}

3. 页面监听与拦截

3.1 WebViewClient

帮助 WebView 处理各种通知、请求事件、记录页面加载过程的。其中就包括URL地址,我们可以通过它来监控到地址的调用过程

// 设置 WebViewClient
mWebView.setWebViewClient(mWebViewClient); 

shouldOverrideUrlLoading

用户可选择是否拦截加载 URL

如果返回值为 true,拦截 WebView 加载 url,false 允许 WebView 加载 url

If a WebViewClient is provided, returning true causes the current WebView to abort loading the URL, while returning false causes the WebView to continue loading the URL as usual.

boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
boolean shouldOverrideUrlLoading(WebView view, String url) (API>21)

可以在这个方法里做什么呢,比如点击到已经定义好的 url 协议 电话号码 tel:// 时,那么可以在这里做拦截,跳转到系统拨号界面。

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith("tel:")) {
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        startActivity(intent);
        return true;
    }
    super.shouldOverrideUrlLoading(view, url);
}

所以在实际项目中,可以在这里处理自定义的一些跳转协议。

onPageStarted() 开始载入页面调用

开始载入页面调用的,我们可以设定一个 loading 的页面,告诉用户程序在等待网络响应。

onPageFinished() 页面加载结束时调用。

页面加载结束时调用 onPageFinished()

onLoadResource()

在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。

onReceivedError()

加载页面的服务器出现错误时调用。

App 里面使用 webview 控件的时候遇到了诸如 404 这类的错误的时候,若也显示浏览器里面的那种错误提示页面就显得很丑陋了,那么这个时候我们的 app 就可以加载一个本地的错误提示页面,比如:

@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
    super.onReceivedError(view, request, error);
    view.loadUrl("file:///android_assets/error_handle.html");
}

onReceivedSslError()

SSL 证书验证错误

比如处理 https 请求时,webView 默认时不处理 https 请求的,页面显示空白。

webView.setWebViewClient(new WebViewClient() {    
        @Override    
        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {    
            handler.proceed();    //表示等待证书响应
        // handler.cancel();      //表示挂起连接,为默认方式
        // handler.handleMessage(null);    //可做其他处理
        }    
    });

onReceivedHttpError()

响应服务器返回的 Http 错误,当一个 http 正常响应时,状态码会是 200,当状态码异常时可以用该方法监听。

这里可以和页面做一些约定,比如页面没有登录,就返回 402,当 errorcode = 402 时,就跳转到登录页面。

@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
    super.onReceivedHttpError(view, request, errorResponse);
    int code = errorResponse.getStatusCode();      
    if (code == 402){
        // toLogin();
    }
}

3.2 WebChromeClient

和 webView 的网页内容管理有关,它的成员方法帮助 WebView 处理 JS 的弹框、网站图标、网站title,加载进度等等。

  // 设置 WebChromeClient
 mWebView.setWebChromeClient(mWebChromeClient);

页面加载进度 onProgressChange()

这个方法会在网页加载过程中多次触发,当 newProgress = 100 时,可以认为网页加载完成。这个方法比 onPageFinished 更为准确,一般用来实现自定义进度条加载。

/**
 * 页面提示框
 * @param view
 * @param newProgress 加载进度
 */
public void onProgressChanged(WebView view, int newProgress) {
    super.onProgressChanged(view, newProgress);
}

显示页面标题 onReceivedTitle()

通常会使用 Activity 或 Fragment 加载网页,我们想让页面标题随着网页内容变化的话,就可以用这个方法获取当前页面的 title。

public void onReceivedTitle(WebView view, String title) {
     //过滤掉服务器返回的连接地址作为标题 过滤掉 http 开头
     if (!title.startsWith("http")) {
         mTvTitle.setText(title);
     }
}

页面提示框 onJsAlert()

html 可以用下文 Android 与 JS 交互中的html 代码测试


/**
 * 页面提示框
 * @param message alert 弹出窗口中的提示信息(提示或警告信息对话框,仅一个确认按钮)
 * @param result 向网页中的 Javascript 代码反馈本次操作结果(result.confirm 代表点击了确定按钮,result.cancel 代表点击了取消按钮)
 */
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
//return super.onJsAlert(view, url, message, result);
    new AlertDialog.Builder(mContext)
            .setTitle("JsAlert")
            .setMessage(message)
            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    result.confirm();
                }
            })
            .setCancelable(false)
            .show();
    return true;
}

页面选择框 onJsConfirm()


/**
 * 页面选择框
 * @param message confirm 弹出窗口中的提示信息(确认对话框,有确认、取消两个按钮)
 * @param result 向网页中的 Javascript 代码反馈本次操作结果(result.confirm 代表点击了确定按钮,result.cancel 代表点击了取消按钮)
 */
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
}

页面选择框 onJsPrompt()


/**
 * 页面选择框
 * @param message prompt 弹出窗口中的提示信息(输入信息对话框,有一个输入框,还有确认、取消两个按钮)
 * @param result 向网页中的 Javascript 代码反馈本次操作结果
 */
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
    final EditText editText = new EditText(MainActivity.this);
    editText.setText("默认数据");//设置默认数据
    new AlertDialog.Builder(MainActivity.this)
            .setTitle("JsPromt")
            .setView(editText)//为弹出窗口设置输入框
            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    result.confirm(editText.getText().toString());//向Javascript传递输入值
                }
            })
            .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    result.cancel();
                }
            })
            .setCancelable(false)
            .show();
    return true;
}

4. 网页的前进与后退

在 Android 中 如果在 WebView 触发物理返回键会直接关闭整个 WebView,所以需要监听事件,用 WebView 的返回处理。

webView.goBack();//跳到上个页面
webView.goForward();//跳到下个页面
webView.canGoBack();//是否可以跳到上一页(如果返回false,说明已经是第一页)
webView.canGoForward();//是否可以跳到下一页(如果返回false,说明已经是最后一页)

实际项目中可以可以监听物理键的返回键,也可以重写 onBackPressed()方法,以下代码段仅做参考:

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (KeyEvent.KEYCODE_BACK == keyCode) {
        if (mWebView != null && mWebView.canGoBack()) {
            mWebView.goBack();
        } else {
            finish();
        }
        return true;
    }
    return super.onKeyUp(keyCode, event);
}

5. 缓存

加载 html 界面时:

缓存方式

settings.setCacheMode(WebSettings.LOAD_NO_CACHE);

PS:这里还有其他和缓存相关的,由于还没有搞的很清楚,暂时不总结了

清除缓存

//清除网页访问留下的缓存
//由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
webView.clearCache(true);

//清除当前webview访问的历史记录
//只会webview访问历史记录里的所有记录除了当前访问记录
webView.clearHistory();

//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
webView.clearFormData();

6. Android 和 JS 的交互

允许 WebView 与 Js 交互,先设置权限

settings.setJavaScriptEnabled(true); //使 WebView 支持 JS
settings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口

index.html 文件(可放入asset 文件夹下测试)

<!DOCTYPE html>
<head>
    <meta charset=utf-8>
    <meta http-equiv=X-UA-Compatible content="IE=edge">
    <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
    <title></title>
</head>

<body>
    <button id="app">点击</button>
</body>

<script>
  document.getElementById("app").onclick = function () {
     alert('Tina')
     //window.open("http://www.baidu.com")
  }
</script>
</html>

6.1 Android 调用 JS

在 html 有如下 javascript 方法(可放进上面 script 中验证)

function jsFunc(){   
    alert('调用 JS 方法')
}

Android 主动调用 JavaScript 的函数,使用如下方法

mWebView.loadUrl("javascript:jsFunc()");

加参也一样,比如调用下面方法

function jsFunc(msg){   
    alert(msg)
}

WebView 操作:

mWebView.loadUrl("javascript:jsFunc(\'jsFunc:Tina\')");

6.2 JS 调用 Android

Android 使用如下方法和 Js 建立联系

addJavascriptInterface(Object obj, String interfaceName); 

第一个参数为类的对象,第二是自定义别名,Js 通过这个别名来调用 Java 的方法

前面的 Html 中,点击 button 触发事件,通过 Android 别名调用 Android 的 callJava 方法

  document.getElementById("app").onclick = function () {
     window.android.callJava()
  }

设置 WebView

 mWebView.addJavascriptInterface(new JSRelation(mContext), "android");

在定义的类中添加相关方法 方法需添加@JavascriptInterface注解

public class JSRelation {
    private Context mContext;
    public JSRelation(Context context) {
        this.mContext = context;
    }

    @JavascriptInterface
    public void callJava(){
        Toast.makeText(mContext,"Android:callJava",Toast.LENGTH_LONG).show();
    }
}
上一篇下一篇

猜你喜欢

热点阅读