Hybrid App 应用开发中5个必备知识点复习

封面

前言

咱们大前端团队内部 📖每周一练 的知识复习计划还在继续,本周主题是 《Hybrid APP 混合应用专题》 ,这期内容比较多,篇幅也相对较长,每一个知识点内容也比较多。javascript

以前分享的每周内容,我都整理到掘金收藏集 📔《EFT每周一练》 上啦,欢迎点赞收藏咯💕💕。css

注:本文整理资料来源网络,有些图片/段落找不到原文出处,若有侵权,联系删除。

1、什么是 Hybrid App,与 Native App 及 Web App 有什么区别

参考文章:html

  1. 《Web App Hybrid App和 Native App的区别》
  2. 《Hybrid APP基础篇(二) -> Native、Hybrid、React Native、Web App方案的分析比较》

1.1 主流应用类型

随着如今移动互联网的快速发展,市面上目前主流移动应用程序主要分三类:Web AppNative AppHybrid App前端

三者大体关系以下: java

关系图

1.2 Web App

Web App,即移动端网站,通常指的是基于 Web 的应用,基于浏览器运行无需下载安装,基本上能够说是触屏版的网页应用。这类应用基本上是一个网页或一系列网页,旨在在移动屏幕上工做。android

Web 网站通常分为两种:css3

  1. MPA(Multi-page Application)
  2. SPA(Single-page Application)

通常的 Web App 是指 SPA 形式开发的网站。 git

优势:github

  • 开发和维护成本低,能够跨平台,调试方便;

前端人员开发的代码,可应用于各大主流浏览器(特殊状况能够代码进行下兼容),没有新的学习成本,并且能够直接在浏览器中调试。web

  • 更新最为快速;

因为web app资源是直接部署在服务器端的,因此只需替换服务器端文件,用户访问是就已经更新了(固然须要解决一些缓存问题)。

  • 无需安装App,不会占用手机内存;

经过浏览器便可访问,无需安装,用户使用成本更低。

缺点:

  • 性能低,用户体验差;

因为是直接经过的浏览器访问,因此没法使用原生的API,操做体验很差。

  • 依赖于网络,页面访问速度慢,耗费流量;

Web App每次访问都必须依赖网络,从服务端加载资源,当网速慢时访问速度很不理想,特别是在移动端,对网站性能优化要求比较高。

  • 功能受限,大量功能没法实现;

只能使用 HTML5 的一些特殊 API ,没法调用原生 API ,因此不少功能存在没法实现状况。

  • 临时性入口,用户留存率低;

这既是它的优势,也是缺点,优势是无需安装,肯定是用完后有时候很难再找到,或者说很难专门为某个web app留存一个入口,致使用户很难再次使用。

1.3 Native App

Native APP 指的是原生程序,须要用户下载安装使用,通常依托于操做系统,有很强的交互,是一个完整的App,可拓展性强,能发布应用商店。

目前市面上主流的平台有:AndroidiOS

优势:

  • 直接依托于操做系统,用户体验好操做流畅性能稳定
  • 用户留存率高;
  • 功能最为强大,特别是在与系统交互中,几乎全部功能都能实现;

因为 Native APP 是直接依托于系统,因此能够直接调用官方提供的API,功能最为全面(好比本地资源操做,通知,动画等)。

缺点:

  • 开发和维护成本高,没法跨平台,须要各平台各自独立开发;

Android 上基于 Java 开发,iOS 上基 OCSwift 开发,相互之间独立,必需要有各自的开发人员。

  • 门槛较高,原生人员有必定的入门门槛,人才较少;

原生的一个很大特色就是独立,因此不太容易入门,并且 AndroidiOS都须要独立学习。

  • 分发成本高,更新缓慢,特别是发布应用商店后,须要等到审核周期;

原生应用更新是一个很大的问题, Android中还能直接下载整包APK进行更新,可是 iOS中,若是是发布 AppStore ,必须经过 AppStore地址更新,而每次更新都须要审核,因此没法达到及时更新。

1.4 Hybrid App

Hybrid App 指的是混合开发,也就是半原生半 Web 的开发模式,有跨平台效果,固然了,实质最终发布的仍然是独立的原生APP(各类的平台有各类的SDK)。

优势:

  • 学习和开发成本较低,能够跨平台,调试方便

Hybrid 开发模式下,由原生提供统一的 API 给 JS 调用,实际的主要逻辑由 HTML 和 JS 完成,最终放在 webview 中显示,这样只须要写一套代码便可,达到跨平台效果,另外也能够直接在浏览器中调试,很方便。

通常 Hybrid 中的跨平台最少能够跨三个平台: Android App ,iOS App ,普通 webkit 浏览器。

须要前端人员关注一些原生提供的API,具体的实现无需关心,没有新的学习内容。

  • 维护成本低,功能可复用,而且更容易更新;

虽然没有 web app 更新那么快速,可是 Hybrid 中也能够经过原生提供 api ,进行资源主动下载,达到只更新资源文件,不更新 apk(ipa) 的效果。

  • 功能更加完善,性能和体验要比起 web app 好太多;

由于能够调用原生api,因此不少功能只要原生提供出就能够实现,另外性能也比较接近原生。

  • 部分性能要求的页面可用原生实现;

这种模式是原生混合 web ,因此咱们彻底能够将交互强,性能要求高的页面用原生写,而后一些其它页面用 JS 写,嵌入 webview 中,达到最佳体验。

缺点:

  • 相比原生,性能仍然有较大损耗;

这种模式受限于 webview 的性能,相比原生而言有很多损耗,体验没法和原生相比。

  • 不适用于交互性较强的app;

这种模式的主要适用:一些新闻阅读类,信息展现类的 app ,不适用于一些交互较强或者性能要求较高的 app (好比动画较多就不适合)。

1.5 三者区别

三者使用场景对比:

对比图

三者技术特征对比:

对比图

另外增长 ReactNative 一块儿放入做对比。

NativeApp WebApp HybridApp ReactNativeApp
原生功能体验 优秀 良好 接近优秀
渲染性能 很是快 接近快
是否支持设备底层访问 支持 不支持 支持 支持
网络要求 支持离线 依赖网络 支持离线(资源存本地状况) 支持离线
更新复杂度 高(几乎老是经过应用商店更新) 低(服务器端直接更新) 较低(能够进行资源包更新) 较低(能够进行资源包更新)
编程语言 Android(Java),iOS(OC/Swift) js+html+css3 js+html+css3 主要使用JS编写,语法规则JSX
社区资源 丰富(Android,iOS单独学习) 丰富(大量前端资源) 有局限(不一样的Hybrid相互独立) 丰富(统一的活跃社区)
上手难度 难(不一样平台须要单独学习) 简单(写一次,支持不一样平台访问) 简单(写一次,运行任何平台) 中等(学习一次,写任何平台)
开发周期 较短 中等
开发成本 昂贵 便宜 较为便宜 中等
跨平台 不跨平台 全部H5浏览器 Android,iOS,h5浏览器 Android,iOS
APP发布 AppStore Web服务器 AppStore AppStore

1.6 三者如何选择

这里简单介绍几种状况,具体仍是要以实际项目技术评估结果为主。

  • 选择纯 Native App 模式的状况:

性能要求极高,体验要求极好,不追求开发效率

  • 选择 Web App 模式的状况:

不追求用户体验和性能,对离线访问没要求,正常来讲,若是追求性能和体验,都不会选用web app。

  • 选择 Hybrid App 模式的状况

大部分状况下的App都推荐采用这种模式,这种模式能够用原生来实现要求高的界面,对于一些比较通用型,展现型的页面彻底能够用web来实现,达到跨平台效果,提高效率。通常好一点的Hybrid方案,都会把资源放在本地的,能够减小网络流量消耗

  • 选择React Native App模式的状况

追求性能,体验,同时追求开发效率,并且有必定的技术资本,舍得前期投入。

React Native这种模式学习成本较高,因此须要前期投入很多时间才能达到较好水平,可是有了必定水准后,开发起来它的优点就体现出来了,性能不逊色原生,并且开发速度也很快

2、什么是 Cordova,它的优缺点是什么

参考文章: 《浅谈Cordova框架》

2.1 Cordova 简介

Cordova 是一个用基于 HTML、CSS 和 JavaScript 的,用于建立跨平台移动应用程序的快速开发平台。它使开发者可以利用iPhone、Android、Palm、Symbian、WP七、Bada和Blackberry等智能手机的核心功能——包括地理定位、加速器、联系人、声音和振动等,此外 Cordova 拥有丰富的插件,能够调用。

也能够用来开发原生和WebView组件之间的插件接口

来源:
Cordova 是 PhoneGap 贡献给 Apache 后的开源项目,是从 PhoneGap 中抽出的核心代码,是驱动 PhoneGap 的核心引擎。能够把它们的关系想象成相似于 Webkit 和 Google Chrome 的关系。

2.2 Cordova 架构图

Cordova架构图

架构图介绍:

  • Web App

用于存放咱们程序的代码,包括业务逻辑,还有一些运行须要的资源(如:CSS,JavaScript,图片,媒体文件等)。
应用的实现是经过 web 页面,默认的本地文件名称是 index.html ,应用执行在原生应用包装的 WebView 中,这个原生应用是你分发到应用商店中的。

  • WebView

Cordova 用的 WebView 能够给应用提供完整用户访问界面,使得应用混合了 Webview 和原生的应用组件。

  • Cordova Plugins

插件是 Cordova 生态系统的重要组成部分。它提供了 Cordova 和原生组件相互通讯的接口,并绑定到了标准的设备API上,这使你可以经过 JavaScript 调用原生代码

2.3 优缺点

优势:

  • 跨平台,开发简单,学习成本低;
  • 框架多,插件多,可自定义插件;
  • 发展最先,社区资源丰富;

缺点:

  • WebView性能低下时,用户体验差,反应慢;
  • 中文文档资源少;
  • 调试不方便,既不像原生那么好调试,也不像纯web那种调试;

3、Cordova 插件的原理是什么

Cordova 插件就是一些附加代码用来提供原生组件的 JavaScript 接口,它容许你的 App 可使用原生设备的能力,超越了纯粹的 Web App。

Cordova 在 iOS 上的实现原理:
cordova

3.1 工做流程

  1. Cordova 发起对原生的请求:
cordova.exec(successCallback, failCallback, service, action, actionArgs); 
// successCallback: 成功回调方法
// failCallback: 失败回调方法
// server: 所要请求的服务名字
// action: 所要请求的服务具体操做
// actionArgs: 请求操做所带的参数
  1. 这五个参数并非直接传给原生,Cordova JS 端会作如下处理:

    • 为每一个请求生成一个惟一标识( callbackId ),并传给原生端,原生端处理完后,会把 callbackId 连同处理结果一块儿返回给 JS 端;
    • callbackIdkey{success:successCallback, fail:failCallback}value,把这个键值对保存在 JS 端的字典里,successCallbackfailCallback 这两个参数不须要传给原生,原生返回结果时带上 callbackId,JS 端就能够根据 callbackId 找到回调方法;
    • 每次 JS 请求,最后发到原生的数据包括:callbackId, service, action, actionArgs

iOS

  1. 原生代码拿到 callbackIdserviceactionactionArgs 后,会作如下处理:

    • 根据 service 参数找到对应插件类;
    • 根据 action 参数找到插件类中对应的处理方法,并把 actionArgs 做为处理方法请求参数的一部分传给处理方法;
    • 处理完成后,把处理结果及 callbackId 返回给 JS 端,JS 端收到后会根据 callbackId 找到回调方法,并把处理结果传给回调方法;

iOS

  1. JS 端根据 callbackId 回调 cordova.js
// 根据 callbackId 及是否成功标识,找到回调方法,并把处理结果传给回调方法
callbackFromNative: function(callbackId, success, status, args, keepCallback) {
    var callback = cordova.callbacks[callbackId];
    if (callback) {
        if (success && status == cordova.callbackStatus.OK) {
            callback.success && callback.success.apply(null, args);
        } else if (!success) {
            callback.fail && callback.fail.apply(null, args);
        }

        // Clear callback if not expecting any more results
        if (!keepCallback) {
            delete cordova.callbacks[callbackId];
        }
    }
}

4、什么是 JS Bridge,它的做用是什么

参考文章:《JSBridge的原理》

4.1 JS Bridge 介绍

JSBridge 简单来说,主要是 给 JavaScript 提供调用 Native 功能的接口,让混合开发中的前端部分能够方便地使用地址位置、摄像头甚至支付等 Native 功能。

JSBridge 就像其名称中的 “Bridge” 的意义同样,是 Native 和非 Native 之间的桥梁,它的核心是 构建 Native 和非 Native 间消息通讯的通道,并且是 双向通讯的通道

JSBridge 另外一个叫法及你们熟知的 Hybrid app 技术。

brige

所谓 双向通讯的通道:

  • JS 向 Native 发送消息 :

调用相关功能、通知 Native 当前 JS 的相关状态等。

  • Native 向 JS 发送消息 :

回溯调用结果、消息推送、通知 JS 当前 Native 的状态等。

4.2. JS Bridge 实现原理

参考文章:《Hybrid APP基础篇(四)->JSBridge的原理》

Android 和 iOS 的 JSBridge 实现方式:

brige原理

4.2.1 基本流程

brige原理

  • H5 页面经过某种方式触发一个 url scheme
  • Native 捕获到 url scheme,并进行分析和处理;
  • Native 调用 H5 的 JSBridge 对象传递回调;

原生的 WebView/UIWebView 控件已经可以和 JS 实现数据通讯了,那为何还要 JSBridge呢?

其实使用JSBridge有不少方面的考虑:

  • Android4.2如下,addJavascriptInterface 方式有安全漏掉。
  • iOS7如下,JS 没法调用 Native。
  • url scheme 交互方式是一套现有的成熟方案,能够完美兼容各类版本,对之前老版本技术的兼容。
4.2.1 实现流程(Android 为例)

brige原理

  1. 拟定协议,参考 http 制定的协议为:jsbridge://className:port/methodName?jsonObj
className   // Android端实现暴露给前端的类名
port        // Android返回结果给前端的端口
methodName  // 前端须要调用的函数
jsonObj     // 前端给Android传递的参数
  1. 新建 HTML 文件命名为 index.html, 编写一个 button 绑定 click 事件;
<button onclick="JSBridge.call(
    'bridge',
    'showToast',
    {'msg':'Hello JSBridge'},
    function(res){
        alert(JSON.stringify(res))
    }
)">
    测试showToast 
</button>
  1. 新建 JS 文件命名为 JSBridge.js, 第2步中的 JSBridge.call 即为调用 JSBridge.js 中的 call 方法,后面带了四个参数;
call: function (obj, method, params, callback) {
    console.log(obj+" "+method+" "+params+" "+callback);
    var port = Util.getPort();
    console.log(port);
    this.callbacks[port] = callback;
    var uri=Util.getUri(obj,method,params,port);
    console.log(uri);
    window.prompt(uri, "");
},

JSBridge.js 中的 call 方法,最后调用了 window.prompt 方法,这个方法就是触发 Android 端 webChromeClient 的回调函数用的。

  1. window.prompt 触发了 WebChromeClient(这个须要使用函数WebView.setWebChromeClient( new WebChromeClietn() )进行设定);

类中的以下回调 onJsPrompt。这时就完成了前端与 Android端 的通讯了,由于前端的信息都顺利经过这个函数传递给Android了。

@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
    result.confirm(JSBridge.callJava(view,message));
    return true;
}
  1. Android 中会定义一个类 JSBridge.java 来管理暴露给前端使用的函数;

这个类有两个功能:

  • 暴露给前端的函数的动态注册功能。
  • 解析前端信息,调用了 Android 端对应的函数,这个示例中是:showToast 函数。

解析前端的信息,获取前端调用的函数名:

Uri uri = Uri.parse(uriString);
className = uri.getHost();
param = uri.getQuery();
port = uri.getPort() + "";
String path = uri.getPath();
HashMap< String, Method> methodHashMap = exposedMethod.get(className);
Method method = methodHashMap.get(methodName);

经过获取的函数名,这里是 showToast ,调用 Android 端的 showToast 函数。

method.invoke(null,webView,new JSONObject(param),new Callback(webView,port));
  1. 定义类 BridgeImpl.java 来具体的实现暴露给前端的全部函数。这里的 showToast 函数以下:
public static void showToast(WebView webView, JSONObject param, final JSBridge.Callback callback){
    String message = param.optString("msg");
    Toast.makeText(webView.getContext(),message,Toast.LENGTH_LONG).show();
    if(null != callback){
        try {
            JSONObject object = new JSONObject();
            object.put("key","value");
            object.put("key1","vaule1");
            callback.apply(getJSONObject(0,"ok",object));
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

5、请列举 Android 与 iOS 平台下 JS Bridge 的实现方式

这边代码比较多,我使用图片来展现,你们能够放大来查看。

5.1 Android 实现方式

5.1.1 Android 调用 JS 的 2 种方式
  1. 经过 WebView loadUrl():

JS 代码调用必定要在 onPageFinished() 回调以后才能调用,不然不会调用。

Web 端代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>前端代码</title>
        <script>
        // Android须要调用的方法
        function callJS(){
            alert("Android调用了JS的callJS方法");
        }
    </script>
   </head>
</html>

Android 端代码:

Android

  1. 经过 WebView evaluateJavascript()
// 只须要将第一种方法的loadUrl()换成下面该方法便可
mWebView.evaluateJavascript(
    "javascript:callJS()", 
    new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String value) {
            //此处为 js 返回的结果
        }
    });
}
5.1.2 JS 调用 Android 的 3 种方式
  1. 经过 WebViewaddJavascriptInterface() 进行对象映射:

Android 映射:

// 继承自Object类
public class AndroidtoJs extends Object {
    // 定义JS须要调用的方法
    // 被JS调用的方法必须加入@JavascriptInterface注解
    @JavascriptInterface
    public void hello(String msg) {
        System.out.println("JS调用了Android的hello方法");
    }
}

Web:

<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <title>前端代码</title>  
      <script>
         function callAndroid(){
        // 因为对象映射,因此调用test对象等于调用Android映射的对象
            test.hello("js调用了android中的hello方法");
         }
      </script>
   </head>
   <body>
      //点击按钮则调用callAndroid函数
      <button type="button" id="button1" "callAndroid()"></button>
   </body>
</html>

Android 端:

Android

  1. 经过 WebViewClientshouldOverrideUrlLoading () 方法回调拦截 url

Web 端:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>前端代码</title>
        <script>
            function callAndroid(){
                /*约定的url协议为:js://webview?arg1=111&arg2=222*/
                document.location = "js://webview?arg1=111&arg2=222";
            }
        </script>
    </head>
    <!-- 点击按钮则调用callAndroid()方法  -->
    <body>
        <button type="button" id="button1" 
            onclick="callAndroid()"
        >点击调用Android代码</button>
    </body>
</html>

Android 端:

Android

  1. 经过 WebChromeClient 的方法回调拦截JS对话框方法:

经过 WebChromeClient 的 onJsAlert()onJsConfirm()onJsPrompt()方法回调拦截JS对话框 alert()confirm()prompt() 消息。

Web 端:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>前端代码</title>
        <script>
            function clickprompt(){
            // 调用prompt()
            var result=prompt("js://demo?arg1=111&arg2=222");
                alert("demo " + result);
            }
        </script>
    </head>
    <!-- 点击按钮则调用clickprompt()  -->
    <body>
        <button type="button" id="button1" 
            onclick="clickprompt()"
        >点击调用Android代码</button>
    </body>
</html>

Android 端:

Android

5.2 iOS 实现方式

5.2.1 JS 调用 iOS 的 2 种方式
  1. 使用 XMLHttpRequest 发起请求的方式:

Web 端:

iOS

XMLHttpRequest bridge:

JS 端使用 XMLHttpRequest 发起了一个请求:execXhr.open('HEAD', "/!gap_exec?" + (+new Date()), true);,请求的地址是 /!gap_exec;并把请求的数据放在了请求的 header 里面,见这句代码:execXhr.setRequestHeader('cmds', iOSExec.nativeFetchMessages());

而在 Objective-C 端使用一个 NSURLProtocol 的子类来检查每一个请求,若是地址是 /!gap_exec 的话,则认为是 Cordova 通讯的请求,直接拦截,拦截后就能够经过分析请求的数据,分发到不一样的插件类(CDVPlugin 类的子类)的方法中:

iOS

Cordova 中优先使用这种方式, Cordova.js 中的注释有说起为何优先使用 XMLHttpRequest 的方式,及为何保留第二种 iframe bridge 的通讯方式:

// XHR mode does not work on iOS 4.2, so default to IFRAME_NAV for such devices.
// XHR mode’s main advantage is working around a bug in -webkit-scroll, which
// doesn’t exist in 4.X devices anyways123

iframe bridge:

在 JS 端建立一个透明的 iframe,设置这个 ifamesrc 为自定义的协议,而 ifame src 更改时,UIWebView 会先回调其 delegatewebView:shouldStartLoadWithRequest:navigationType: 方法,关键代码以下:

iOS

  1. 经过设置透明的 iframesrc 属性:
5.2.2 iOS 调用 JS 的方式

UIWebView 有一个这样的方法 stringByEvaluatingJavaScriptFromString:,这个方法可让一个 UIWebView 对象执行一段 JS 代码,这样就能够达到 Objective-C 跟 JS 通讯的效果,在 Cordova 的代码中多处用到了这个方法,其中最重要的两处以下:

  1. 获取 JS 的请求数据:

iOS

  1. 把 JS 请求的结果返回给 JS 端:

iOS

结语

对于初入混合应用开发的小伙伴,这些会有点难度,可是好好理解下那几张流程图,再理一理思路,相信会有帮助😁

给你们加加油~~

关于我

本文首发在 pingan8787我的博客,如需转载请保留我的介绍
Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推荐 https://github.com/pingan8787...
ES小册 js.pingan8787.com

微信公众号

bg

相关文章
相关标签/搜索