H5必知必会之与App交互

html-css012

H5必知必会之与App交互,第1张

奇技指南

2018年11月26日发表的“360 AI音箱H5开发实践”一文中,曾简单提到“与Native交互”。本文将就此主题深入探讨H5与App交互的几种常见模式。

本文内容如下:

H5,在中国被专门用来指代开发内嵌于手机应用中的网页的技术,外国好像并没有这个说法。从技术上讲,H5是HTML5即Hyper Text Markup Language(超文本标记语言)第5版的简称。而HTML只是开发网页要用到的多种技术之一。除了HTML,还要用CSS设计界面,用JavaScript实现交互,甚至要用Node.js实现服务端逻辑。为什么H5会被用来笼统地指代这些技术呢?我猜一是因为它简单,二是移动端网页开发技术又恰好需要这么一个概念。

移动端网页运行在手机应用内嵌的浏览器引擎中,这个没有UI的内核容器统称WebView,即iPhone的UIWebView(iOS 2.0–12.0)、WKWebView(iOS 8.0+,macOS 10.10+)和Android的WebView。总之,WebView就是在手机应用中运行和展示网页的界面和接口(神奇的是,英文Interface,既可以翻译成“界面”也可以翻译成“接口”)。

H5与原生应用的交互都是通过原生应用中的WebView实现的。通过这个环境,H5可以调用原生应用注入其中的原生对象的方法,原生应用也可以调用H5暴露在这个环境中的JavaScript对象的方法,从而实现指令与数据的传输。

比如,在Android应用中,WebView类有一个公有方法addJavascriptInterface,签名为:

调用这个方法可以向WebView中以指定的名称name注入指定的Java对象object。这样,WebView中的JavaScript就可以通过name调用object的方法。比如:

在iOS或macOS中,需要通过创建WKWebView类的实例在应用中嵌入网页,交互过程类似。

所谓基础接口,就是首先要规定原生应用和JS分别在WebView里注入/暴露一个什么对象:

并约定在这两个对象上分别可以调用什么方法:

顾名思义,NativeBridge.callNative是由JS调用向Native传递指令或数据的方法,而JSBridge.callJS则是由Native调用向JS传递指令或数据的方法。方法签名中的参数含义如下:

基础接口只有两个对象和两个方法,JS与App间的互操作则通过action和params来扩展和定义。

对于JS而言,虽然这里只定义了一个对象一个方法,但实践中,可以把action对应方法的实现附加到JSBridge上,只要把callJS实现为一个分发方法即可,比如:

这样,所有对callJS的调用,都会转化成对JSBridge上相应action方法的调用,优点是只需一行代码。

另一种实现方式是通过switch...case语句实现调用分发,比如:

这样实现的优点是所有方法一目了然,当然同样也是把所有相关接口都附加到同一个JSBridge对象上。

以上两种实现模式各有利弊。

由JS发起的单向调用App的操作,主要涉及加载URL和切换到原生界面,可对应如下action:

loadUrl调用的参考协议如下:

这里NativeBridge是App的原生对象,其callNative方法被调用时,会收到一个对象(字典/映射)参数。根据这个参数的action属性的值,App可知需要执行的操作是加载URL。于是再取得params属性中的url,发送请求即可。

loadContent调用的参考协议如下:

同上,这里通过params向App传递了必要参数,App负责切换到相应的原生界面。

由App发起的单向调用JS的操作,主要涉及用户点击后退按钮(<),可对应如下action:

can_back调用的参考协议如下:

此调用返回的值示例如下:

顾名思义,can_back用于App询问JS:在返回上一级界面前,是否弹窗提示用户?

返回值中的can如果是true,则直接返回,不提示;如果是false,则弹出一个确认框,请用户确认。另一个值target是与App约定的返回目标,比如prev表示返回上一级,top表示返回顶级,等等。

双向调用是JS先调用App,然后App在完成操作后再调用JS,双向通常都需要传递数据。双向调用主要涉及JS调用App原生组件和用户点击右上角按钮,可对应如下action:

loadComponent的参考协议如下:

在这个例子中,涉及JS调用App显示其实现的城市选择组件:type: 'location',用户选择完城市之后,App再调用set_location,将用户选择的城市名称传给JS:

JS根据拿到的值更新界面,完成一次双向调用。另一个例子是JS调用原生的日期选择组件,与此类似。

为什么叫displayNextButton?因为根据具体业务场景,可能存在如下三种情况:

displayNextButton协议的参考实现如下:

以上代码示例表明,JS调用App,告诉App显示“下一步”按钮,但是要禁用变灰,因为enable: false。如果传递的是enable: true,那么用户就可以点击“下一步”按钮了。点击之后,App再调用JS的save_form。最后,如果不想显示按钮,可以传递name: ''。

下面重点说一下用户点击“下一步”按钮,App调用save_form的场景。此时也分两种情况:

如果是JS通过App保存数据——可能因为App端实现了数据写入必需的加密机制——那么,JS可以在App调用save_form时将约定好的数据返回给App,由App去保存数据。

如果是JS直接保存数据,比如通过Ajax,那么在保存完数据之后,则还需要调用前面所说的App暴露的loadUrl或loadComponent方法,以告知App切换界面。当然这种情况下会出现第三次调用,但仍然属于双向调用。

本文介绍了JS与App交互的几种模式,而且只讨论了JS端的实现。在开发实践中,团队各端总会面临哪一端主导的问题。本文展示的参考实现就是H5端主导的一种实现形式。H5主导的特点是把主要业务逻辑都封装到WebView中,App主要协同配合,而优点是业务逻辑的变更不会蔓延到App。毕竟相对于H5,App的安装部署模式会造成多版本共存问题,需要尽可能控制新版本。假如由App端主导,将逻辑封装在App端,势必造成版本不受控,给整个项目或产品埋下隐患。

当然,事无绝对。具体情况还要具体分析。而且,哪方主导有时候也取决多方面因素。实践中还是要因人、因时、因势制宜。

生App vs 移动Web App

Git@OSC 的 Android 和 iOS 客户端全面开源

每当你打算开发移动应用程序时,都要考虑你的应用如何创建以及如何部署。如今已有两个主要的方向:原生App 以及 移动Web App。那么在今天这篇文章中就来比较一下两者的区别来供大家参考。

原生App vs 移动Web App : 定义

什么叫做原生App?

原生App是专门针对某一类移动设备而生的,它们都是被直接安装到设备里,而用户一般也是通过网络商店或者卖场来获取例如The App Store 与 Android Apps on Google Play .

随便说几个原生App的例子,比如 iOS 的 Camera+ 以及 Android 的 KeePassDroid

什么叫做移动Web App?

一般说来,移动Web App都是都是需要用到网络的,它们利用设备上的浏览器(比如iPhone的Safari)来运行,而且它们不需要在设备上下载后安装。

原生App 与 移动Web App的比较

用户界面

有些公司为同样的产品制作了原生App与移动Web App,下图就是Facebook的原生App与移动Web App的界面比较:

注意这两者的不同地方会产生不同的用户体验结果。

开发方面

原生App

每一种移动操作系统都需要独立的开发项目

每种平台都需要独立的开发语言。Java(Android), Objective-C(iOS)以及Visual C++(Windows Mobile)等等

需要使用各自的软件开发包,开发工具以及各自的控件

移动Web App

因为运行在移动设备的浏览器上,所以只需要一个开发项目

这种应用可以使用HTML5,CSS3以及JavaScript以及服务器端语言来完成(PHP,Ruby on Rails,Python)

这里可没有标准的SDK,基本任意选择

别忘了有一些跨平台的开发工具,比如PhoneGap, Sencha Touch 2以及Appcelerator Titanium等等。

能力方面

原生App

能够与移动硬件设备的底层功能,比如个人信息,摄像头以及重力加速器等等

移动Web App

只能使用有限的移动硬件设备功能。

赢利

原生App

可以使用专门的移动平台的广告,比如AdMob

移动Web App

基本没有限制,但也没什么优势

注意原生App能够直接在对应的官方商店进行定价就能马上赢利,然而移动Web App需要你自己完成付款的系统,这可不是一件简单的事情。

获取方法

原生App

直接下载到设备

以独立的应用程序运行(并不需要浏览器)

用户必须手动去下载并安装这些原生App

有一些商店与卖场来帮助用户寻找你的App

移动Web App

从移动设备上的浏览器访问

不需要安装额外的软件

软件更新只需要服务器就够了

因为现在没有什么商品或卖场提供这种App,所以如何搜索这些移动Web App相当不简单

版本控制

原生App

用户可以自由地选择是否更新软件版本,所以会出现不同用户同时使用不同版本的情况

移动Web App

所有的用户都是用同样的版本

优势

原生App

比移动Web App运行快

一些商店与卖场会帮助用户寻找原生App

官方卖场的应用审核流程会保证让用户得到高质量以及安全的App

官方会发布很多开发工具或者人工支持来帮助你的开发

移动Web App

跨平台开发

用户不需要去卖场来下载安装App

任何时候都可以发布App,因为根本不需要官方卖场的审核

如果你已经有了一个Web App,你可以使用 responsive web design来辅助改进(这也是优势?)

缺陷

原生App

开发成本高,尤其是当需要多种移动设备来测试时

因为是不同的开发语言,所以开发,维护成本也高

因为用户使用的App版本不同,所以你维护起来很困难

官方卖场审核流程复杂且慢,会严重影响你的发布进程

移动Web App

无法使用很多移动硬件设备的独特功能

要同时支持多种移动设备的浏览器让开发维护的成本也不低

如果用户使用更多的新型浏览器,那问题就更不好处理了

对于用户来说,这种App很难被用户发现

原生App vs 移动Web App : 你如何选择?

所以在你准备做移动App时,你应该先问问自己以下几个问题:

你的应用是否需要使用某些设备的特殊功能,比如摄像头,摄像头闪光灯或者重力加速器

你的开发预算

你的应用是否一定需要网络

你的应用的目标硬件设备是所有的移动设备还是仅仅只是一部分而已

你自己已经熟悉的开发语言

这个应用对于性能要求是否苛刻

如何靠这个应用赢利

我想这几个问题应该能让你做出明智的选择。

结论

你的选择是原生App还是移动Web App,主要受商业目标,目标用户,以及技术需要这些因素影响的。其实更多时候你也不要为选择那种App模式烦恼,正如上文提到,类似Facebook这样的公司就为用户提供了两种选择。然而对于大部分人来说,预算,资源限制将会逼迫我们只能选择其中一种(或者只能以其中一种为重点)。

一、html5手机网站调用微信分享包括 :

获取网络类型。

调起客户端的图片播放组件。

调用微信扫描二维码。

判断是否安装对应的应用。

发送邮件。

分享到微信朋友圈。

二、代码如下:

[html] view plain copy

<!DOCTYPE html>  

<html>  

  

    <head>  

        <meta charset="utf-8">  

        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">  

        <title>HTML5网页如何调用浏览器APP的微信分享功能</title>  

        <meta name="viewport" content="width=device-width, initial-scale=1" />  

        <link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />  

        <link rel="stylesheet" type="text/css" href="css/test.css" />  

        <style type="text/css">  

            html,  

            body {  

                margin: 0  

                padding: 0  

            }  

        </style>  

  

    </head>  

  

    <body>  

        <span class="shareBtn" id="toshare">点击分享到</span>  

        <div id="nativeShare"></div>  

        <script type="text/javascript" src="js/vendor/jquery-1.11.3.min.js"></script>  

        <script type="text/javascript" src="js/nativeShare.js"></script>  

        <script type="text/javascript">  

            $("#toshare").bind("click", function() {  

                    var config = {  

                        url: '', //分享url  

                        title: '', //内容标题  

                        desc: '', //描述  

                        img: '', //分享的图片  

                        img_title: '', //图片名称  

                        from: '' //来源  

                    }  

                    var share_obj = new nativeShare('nativeShare', config)  

                    $(".am-share").addClass("am-modal-active")  

                    if ($(".sharebg").length > 0) {  

                        $(".sharebg").addClass("sharebg-active")  

                    } else {  

                        $("body").append('<div class="sharebg"></div>')  

                        $(".sharebg").addClass("sharebg-active")  

                    }  

                    $(".sharebg-active,.share_btn").click(function() {  

                        $(".am-share").removeClass("am-modal-active")  

                        setTimeout(function() {  

                            $(".sharebg-active").removeClass("sharebg-active")  

                            $(".sharebg").remove()  

                        }, 300)  

                    })  

            })  

        </script>  

  

    </body>  

  

</html>

三、输出结果:

四、其他分享接口:

A.获取“分享到朋友圈”按钮点击状态及自定义分享内容接口。

wx.onMenuShareTimeline({

    title: '', // 分享标题

    link: '', // 分享链接

    imgUrl: '', // 分享图标

    success: function () { 

        // 用户确认分享后执行的回调函数

    },

    cancel: function () { 

        // 用户取消分享后执行的回调函数

    }

})

B.获取“分享给朋友”按钮点击状态及自定义分享内容接口。

wx.onMenuShareAppMessage({

    title: '', // 分享标题

    desc: '', // 分享描述

    link: '', // 分享链接

    imgUrl: '', // 分享图标

    type: '', // 分享类型,music、video或link,不填默认为link

    dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空

    success: function () { 

        // 用户确认分享后执行的回调函数

    },

    cancel: function () { 

        // 用户取消分享后执行的回调函数

    }

})

C.获取“分享到QQ”按钮点击状态及自定义分享内容接口。

wx.onMenuShareQQ({

    title: '', // 分享标题

    desc: '', // 分享描述

    link: '', // 分享链接

    imgUrl: '', // 分享图标

    success: function () { 

       // 用户确认分享后执行的回调函数

    },

    cancel: function () { 

       // 用户取消分享后执行的回调函数

    }

})

D.获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口。

wx.onMenuShareWeibo({

    title: '', // 分享标题

    desc: '', // 分享描述

    link: '', // 分享链接

    imgUrl: '', // 分享图标

    success: function () { 

       // 用户确认分享后执行的回调函数

    },

    cancel: function () { 

        // 用户取消分享后执行的回调函数

    }

})

E.获取“分享到QQ空间”按钮点击状态及自定义分享内容接口

wx.onMenuShareQZone({

    title: '', // 分享标题

    desc: '', // 分享描述

    link: '', // 分享链接

    imgUrl: '', // 分享图标

    success: function () { 

       // 用户确认分享后执行的回调函数

    },

    cancel: function () { 

        // 用户取消分享后执行的回调函数

    }

})