Android webView和h5的常用交互场景浅析

前言

现如今,大部分APP都是原生+h5形式的,原生的流畅+h5的内容多样性,使得APP内容丰富多彩,因此android开发中,常会遇到与H5交互的形式,其原理说白了无非是:原生方法和JS方法的互调,该文主要简单谈谈通用web展示界面的封装,APP内需要用到h5的地方直接用它就行了。

常用的交互场景

有哪些常用交互场景呢?这里简单做了个总结,可能会有不全面的地方。

场景名称 场景介绍 js->原生 原生->js
标题内容以及隐藏和展示 有的界面需要原生标题
有的界面需要展示web端的标题
标题栏和状态栏颜色修改 不同web界面可能有不同的主题效果
可以参考《京东金融》app
社交分享 带弹窗的
直接分享的(web提供弹窗)
跳转内部 如跳转到登陆界面
跳转外部 如选择用什么地图打开
类微信更多弹窗 刷新、复制链接、浏览器打开等
控制更多弹窗的内容数量 a界面只有分享
b界面有浏览器打开
地图转换 点击web地图模块跳转到原生地图界面
支付 调用原生微信、支付宝支付
图片预览 调用原生图片预览控件效果更佳
返回机制 该情况只有在隐藏原生标题栏时用得到
隐藏底部导航栏 如首页fragment内容点击详情时
需要隐藏底部的tab,防止遮挡
拨号、系统弹窗 很常见的场景
文件操作 上传图片、文件等
同步登陆信息 原生登陆登出,web登陆状态变更
同步定位信息 h5直接用原生定位信息
... ... ... ...

准备工作

既然是js和原生的交互,webview要设置一番才行。

        WebSettings webSettings = webView.getSettings();
        //支持javascript
        webSettings.setJavaScriptEnabled(true);
        //将图片调整到适合webview的大小
        webSettings.setUseWideViewPort(true);
        // 缩放至屏幕的大小
        webSettings.setLoadWithOverviewMode(true);
        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);

        //缩放操作
        //支持缩放,默认为true。是下面那个的前提。
        webSettings.setSupportZoom(true);
        //设置内置的缩放控件。若为false,则该WebView不可缩放
        webSettings.setBuiltInZoomControls(true);
        //隐藏原生的缩放控件
        webSettings.setDisplayZoomControls(false);
        // 文件操作
        webSettings.setAllowFileAccess(true);

        //启用数据库
        webSettings.setDatabaseEnabled(true);
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        //不设置可能会导致js调用失败
        webSettings.setDomStorageEnabled(true);
       //加载速度优化:先加载文字后加载图片
        webSettings.setBlockNetworkImage(true);
        webSettings.setLoadsImagesAutomatically(true);
       //设置UA,让浏览器知道使用者设备
        String ua = webSettings.getUserAgentString();
        if (!ua.contains(YYNATIVE)) {
            webSettings.setUserAgentString(webSettings.getUserAgentString() + "; " + YYNATIVE);
        }

        webSettings.setAppCacheEnabled(true);
        webSettings.setAppCacheMaxSize(1024 * 1024 * 8);
        webSettings.setAppCachePath(getApplication().getCacheDir().getAbsolutePath());
        //向web端注入对象
        webView.addJavascriptInterface(this, YYNATIVE);

到此,webview的参数配置完了,需要注意的是webSettings.setJavaScriptEnabled(true);webSettings.setUserAgentString(webSettings.getUserAgentString() + "; " + YYNATIVE);以及webView.addJavascriptInterface(this, YYNATIVE);这三行代码是必不可少的。第一行,设置js支持,不设置js不鸟你;第二行,设置UA标识,不设置web端不知道这是android还是ios或者微信web端;第三行不注入对象,web端也是无法唤醒Java方法的。

实现场景案例

js调用android

标题栏的隐藏和展示

android端代码方法的命名需要和web端约定,web端调用方式是:window.约定对象名.方法名(参数)window.yynative.setTopVisiable(true)

 /**
     * 设置标题
     *
     * @param title
     */
    @JavascriptInterface
    public void setTopTitle(String title) {
        AppUtils.runOnUIThread(() -> tvTitle.setText(title));
    }
/**
     * 设置标题栏可见与否
     *
     * @param visiable
     */
    @JavascriptInterface
    public void setTopVisiable(boolean visiable) {
        AppUtils.runOnUIThread(() -> llTop.setVisibility(visiable ? View.VISIBLE : View.GONE));
    }

注意:@JavascriptInterface该注解必不可少,否则js会提示找不到方法,另外UI操作需要在UI线程里进行。

标题栏和状态栏颜色修改

 /**
     * 设置标题背景色
     *
     * @param
     */
    @JavascriptInterface
    public void setTopBgColor(String color) {
      AppUtils.runOnUIThread(() -> llTop.setBackgroundColor(Color.parseColor(color)));
      ....//修改标题字体颜色
      ....//修改icon
    }
   /**
     * 设置状态栏背景色
     *
     * @param
     */
    @JavascriptInterface
    public void setStatusBgColor(String color) {
        AppUtils.runOnUIThread(() ->StatusBarUtils.setColor(Color.parseColor(color)));
    }

注意:状态栏的颜色修改,避免状态栏字体看不清;标题栏颜色修改时,要防止背景色和图标、字体颜色重复。

以上2种场景为大多数js调用android的模式,还有一些是不需要我们自己定义方法,系统帮给我们方法或者另辟蹊径,需要我们用到WebViewClientWebChromeClient,他们的区别是:
  • WebViewClient:在影响【View】的事件到来时,会通过WebViewClient中的方法回调通知用户,主要帮助WebView处理各种通知、请求事件的;
  • WebChromeClient:当影响【浏览器】的事件到来时,就会通过WebChromeClient中的方法回调通知用法,主要辅助WebView处理Javascript的对话框、网站图标、网站title、加载进度,文件处理等。

拨号处理

使用WebViewClient实现shouldOverrideUrlLoading方法

     @Override
        public boolean shouldOverrideUrlLoading(WebView webView, String s) {
            //拨号处理
            if (s.startsWith(TEL)) {
                Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(s));
                startActivity(intent);
                return true;
            } 
            webView.loadUrl(s);
            return true;
        }

js系统弹窗

使用WebChromeClient实现onJsAlertonJsConfirmonJsPrompt方法,这里给出onJsAlert的实现代码:

@Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
//          return super.onJsAlert(view, url, message, result);
        final AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());

        builder.setTitle("温馨提示")
                .setMessage(message)
                .setPositiveButton("确定", null);

        // 不需要绑定按键事件
        // 屏蔽keycode等于84之类的按键
        builder.setOnKeyListener((dialog, keyCode, event) -> true);
        // 禁止响应按back键的事件
        builder.setCancelable(false);
        AlertDialog dialog = builder.create();
        dialog.show();
        result.confirm();// 因为没有绑定事件,需要强行confirm,否则页面会变黑显示不了内容。
        return true;
    }

文件处理

同样,用到WebChromeClient,实现onShowFileChooser方法(android5.0+,android4.1.1+实现openFileChooser),然后自己实现文件处理逻辑。

  //回调的接口
    public interface OpenFileChooserCallBack {
        void openFileChooserCallBack(ValueCallback uploadMsg, String acceptType);

        void openFileChooser5CallBack(WebView webView, ValueCallback valueCallback,
                                      FileChooserParams fileChooserParams);
    }

 //针对 Android 5.0+
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback valueCallback,

                                     FileChooserParams fileChooserParams) {
        mOpenFileChooserCallBack.openFileChooser5CallBack(webView, valueCallback, fileChooserParams);
        return true;

    }
//在实现里做自己的逻辑
 @Override
    public void openFileChooser5CallBack(WebView webView, ValueCallback valueCallback, WebChromeClient.FileChooserParams fileChooserParams) {
        mValueCallback = valueCallback;
        //文件处理函数
        showOptions();
    }

使用WebChromeClient还可以处理加载进度,视频横竖屏切换等,这里不做详细介绍。

android调用js

同步信息

假设需要同步的信息是用户的tokentoken有效视为已登陆,失效需要重新登陆。一定要区分清楚h5什么时候需要你给的参数,h5的内容需要登陆后才能展示,那token传递需要在webview加载时传给h5,不能在WebViewClient中的onPageFinished方法中传递,我们需要在加载时调用,应该在onLoadResource中传递,反之在onPageFinished中调用;同理,定位信息也是。

@Override
        public void onLoadResource(WebView webView, String s) {
            if (mFirstLoad) {
                String token = getUserIdAuthKey();
                HashMap cityMap = new HashMap<>();
                cityMap.put("city", CityBean.city);
                cityMap.put("lat", CityBean.Latitude + "");
                cityMap.put("lon", CityBean.Longitude + "");
                String cityInfo = JsonUtils.serialize(cityMap);
                webView.evaluateJavascript("javascript:window.getUserIdFromApp(" + "\"" + token + "\"" + ")", new ValueCallback() {
                    @Override
                    public void onReceiveValue(String value) {

                    }
                });
                webView.evaluateJavascript("javascript:window.getlocationInfoFromApp(" + cityInfo + ")", new ValueCallback() {
                    @Override
                    public void onReceiveValue(String value) {

                    }
                });
            }
            super.onLoadResource(webView, s);
        }

注意:evaluateJavascript在android4.4后才能使用,在此之前可以使用webview.loadUrl方法。

总结

android和h5的交互场景大致就是这些,很多场景是类似的,因此不做过多分析,可以举一反三,只是其中有些细节需要大家注意,比如:

  1. 支付结束后需要把支付结果和状态回调给h5,即android调js,让h5来操作后续;
  2. 又如图片预览,如果传过来的是多组图片,h5还需要传递用户点击的图片索引给webview,我们预览时可以显示当前图片所在位置。
  3. 通过h5跳转登陆成功后,要记得立马同步登陆信息给h5,否则会继续跳转登陆
    ....
    ....
    希望这篇博客能给新手少走弯路,如有错误之处还望指正!

你可能感兴趣的:(Android webView和h5的常用交互场景浅析)