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<Uri> uploadMsg, String acceptType);

        void openFileChooser5CallBack(WebView webView, ValueCallback<Uri[]> valueCallback,
                                      FileChooserParams fileChooserParams);
    }

 //针对 Android 5.0+
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> valueCallback,

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

    }
//在实现里做自己的逻辑
 @Override
    public void openFileChooser5CallBack(WebView webView, ValueCallback<Uri[]> 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<String, String> 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<String>() {
                    @Override
                    public void onReceiveValue(String value) {

                    }
                });
                webView.evaluateJavascript("javascript:window.getlocationInfoFromApp(" + cityInfo + ")", new ValueCallback<String>() {
                    @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,否则会继续跳转登陆
    ....
    ....
    希望这篇博客能给新手少走弯路,如有错误之处还望指正!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容