默认
打赏 发表评论 14
想开发IM:买成品怕坑?租第3方怕贵?找开源自已撸?尽量别走弯路了... 找站长给点建议
基于WebSocket实现Hybrid移动应用的消息推送实践(含代码示例)
阅读(90443) | 评论(14 收藏1 淘帖2
微信扫一扫关注!

1、前言


消息推送对于 APP 至关重要,一个及时有效的消息推送能够帮助客户获取有价值的信息,所以说消息推送是移动应用的一项重要功能。目前主流的移动操作系统(Android、iOS)的 webview 都已经支持 WebSocket, 所以对于Hybrid移动应用,WebSocket 也将成为消息推送的备用选择。本文将围绕 Hybrid App(以Cordova为例)的 WebSocket 消息推送进行一系列的实践性探索。

2、基础知识


1何为Hybrid移动应用?


Hybrid App(混合模式移动应用)是指介于web-app、native-app这两者之间的app,兼具“Native App良好用户交互体验的优势”和“Web App跨平台开发的优势”。目前流行的Hyrbid技术有很多,比如:Cordova (来自Phonegap)、React Native、Weex(来自阿里巴巴)、WeX5等,有兴趣可自行百度。

2WebSocket简介


WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 有很大区别,具体详见《WebSocket详解(二):技术原理、代码演示和应用案例》。

3、参考资料


[1] 有关WEB端即时通讯开发:
新手入门贴:史上最全Web端即时通讯技术原理详解
WebSocket详解(一):初步认识WebSocket技术
WebSocket详解(二):技术原理、代码演示和应用案例
WebSocket详解(三):深入WebSocket通信协议细节
Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE
SSE技术详解:一种全新的HTML5服务器推送事件技术
Comet技术详解:基于HTTP长连接的Web端实时通信技术
socket.io实现消息推送的一点实践及思路
LinkedIn的Web端即时通讯实践:实现单机几十万条长连接
Web端即时通讯技术的发展与WebSocket、Socket.io的技术实践
>> 更多同类文章 ……

[2] 有关推送技术的文章:
iOS的推送服务APNs详解:设计思路、技术原理及缺陷等
Android端消息推送总结:实现原理、心跳保活、遇到的问题等
扫盲贴:认识MQTT通信协议
一个基于MQTT通信协议的完整Android推送Demo
IBM技术经理访谈:MQTT协议的制定历程、发展现状等
求教android消息推送:GCM、XMPP、MQTT三种方案的优劣
移动端实时消息推送技术浅析
扫盲贴:浅谈iOS和Android后台实时消息推送的原理和区别
绝对干货:基于Netty实现海量接入的推送服务技术要点
移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)
为何微信、QQ这样的IM工具不使用GCM服务推送消息?
极光推送系统大规模高并发架构的技术实践分享
从HTTP到MQTT:一个基于位置服务的APP数据通信实践概述
魅族2500万长连接的实时消息推送架构的技术实践分享
专访魅族架构师:海量长连接的实时消息推送系统的心得体会
深入的聊聊Android消息推送这件小事
>> 更多同类文章 ……

4、Hybrid移动应用消息推送技术原理


混合应用一般使用 Cordova 之类的中间件,以 WebView 作为用户界面层,以 Javascript 作为基本逻辑,以及和中间件通讯,再由中间件访问底层 API 的方式,进行应用开发。开发时可能不采用或者大部分不采用原生语言,但是却有所有原生应用的特性。而开发消息推送功能时,我们就既可以使用 Native App 的系统自带推送如 GCM 和 APNS,又可以使用基于 html5 的 websocket 推送。

对于Hybrid应用的 websocket 消息推送,其基本原理如下:
基于WebSocket实现Hybrid移动应用的消息推送实践(含代码示例)_1.png

而原生应用的消息推送,其基本原理如下图:
基于WebSocket实现Hybrid移动应用的消息推送实践(含代码示例)_2.png

目前后者主要应用于原生 app, 而前者由于开发周期短,跨平台性好,维护成本低,一般可以用于混合应用的消息推送。基于 websocket 的消息推送,我们就可以自己去实现消息推送的服务端,这样我们就掌握了推送服务的主动权,对于安全性极高的企业,websocket 推送无疑是最好的选择,因为如果使用 GCM 或者 APNS 推送,我们不得不将信息发送到 GCM server 或者 APNS server, 再由 GCM 或者 APNS 服务端转发到客户端,信息安全性不得而知。一旦推送服务器出现异常,我们的消息推送将变得非常被动。 但是 GCM 和 APNS 也是使用长连接进行消息推送,而且一个手机上的所有 app 共用一个长连接,对于手机性能将会有极大的帮助。两种推送各有利弊,读者可自选选择。本文将针对 websocket 的消息推送进行一系列介绍。

即时通讯网注:以上观点有一个常识性的错误,那就是iOS平台上,消息推送属系统级的行为,当应用处于后台时消息推送必须通过iOS官方的APNS通道进行,否则没有第2种可能性。(详见文章《扫盲贴:浅谈iOS和Android后台实时消息推送的原理和区别》、《iOS的推送服务APNs详解:设计思路、技术原理及缺陷等》)

5、WebSocket 接口简介


WebSocket 的实现分为客户端和服务端两部分,客户端(通常为浏览器)发出 WebSocket 连接请求,服务端响应,实现类似 TCP 握手的动作,从而在浏览器客户端和 WebSocket 服务端之间形成一条 HTTP 长连接快速通道。两者之间后续进行直接的数据互相传送,不再需要发起连接和响应。同时两者都可以关闭这个长连接。我们正是利用了混合移动应用的 webview 可以支持 websocket 的这个特性来实现服务器端对客户端的一个消息推送。Websocket 针对客户端而言,性能,资源使用以及及时性要比传统的轮询更好。

6、Websocket 客户端 API


对于 websocket 客户端,目前主流的移动操作系统的 webview 层都已经支持 websocket 服务。以下列举了常见的移动系统支持情况。

表 1. 主流移动系统的 webview 对 websocket 的支持情况:
移动系统版本
IOSIOS 5+
AndroidAndroid 4.4+
Windows phoneWindows phone 8+


常见浏览器和移动系统的 webview 都已经实现了 w3 规范的 websocket 接口,具体接口参考清单 1。

清单 1. Websocket 客户端接口:
 [Constructor(in DOMString url, in optional DOMString protocol)] 
 interface WebSocket { 
   readonly attribute DOMString URL; 
        // ready state 
   const unsigned short CONNECTING = 0; 
   const unsigned short OPEN = 1; 
   const unsigned short CLOSED = 2; 
   readonly attribute unsigned short readyState; 
   readonly attribute unsigned long bufferedAmount; 
   //networking 
   attribute Function onopen; 
   attribute Function onmessage; 
   attribute Function onclose; 
   boolean send(in DOMString data); 
   void close(); 
 }; 
 WebSocket implements EventTarget;

其中 URL 属性代表 WebSocket 服务器的网络地址,协议通常”ws”或者”wss“,send 方法就是发送数据到服务器端,close 方法就是关闭连接。除了这些方法,还有一些很重要的事件:onopen,onmessage,onerror 以及 onclose。详细解释请参考表 2。

表 2. Websocket 的对象方法属性:
基于WebSocket实现Hybrid移动应用的消息推送实践(含代码示例)_WX20170410-103733@2x.png

下面一段代码展示了建立 websocket 实例。

清单 2. Websocket 创建连接实例:
 var ws= new WebSocket("ws://localhost:8080/PushNotification"); 
 ws.onopen = function (event) { 
 console.log("connected to server"); 
 }; 
 ws.onmessage = function (event) { 
 //when a new message coming, we will call Cordova plugin here 
 };

当有新消息到达时,onmessage 会自动触发,我们可以在这个方法里利用 Cordova plugin 去实现调用 android 或者 ios 的 notification。

7、Websocket 服务器端 API


对于 websocket 服务器端,目前主流的 web 服务器都已经支持。以下列举了常见的服务器支持情况。

表 3. 主流 web 服务器对 websocket 的支持情况:
应用服务器版本
Websphere8.0+
Websphere Liberty8.5.5.5+
Weblogic12c+
IIS7.0+
Tomcat7.0.5+
Jetty7.0+


以下我们使用 websphere liberty 8.5.5.5 作为 web 服务器,websocket 功能需要另外安装,步骤如下。

1)安装 websocket,执行以下命令:
 bin/featureManager install websocket-1.0 --when-file-exists=ignore

2)在 server.xml 中引入如下配置:
 <featureManager> 
   <feature>websocket-1.0</feature> 
 </featureManager>

3)重启 websphere liberty 即可生效。

WebSocket 服务端的代码示例如下。

清单 3. Websocket 服务器端接口:
@ServerEndpoint(value = "/PushNotification") 
 public class PushNotifyWithWebSocket { 
         private static Set<Session> 
         sessions = Collections.newSetFromMap(new
          ConcurrentHashMap<Session,Boolean>()); 
        
         @OnMessage 
         public void receiveMessage(String message) { 
                 //todo 
         } 
         @OnOpen 
         public void onOpen(Session session, EndpointConfig ec) { 
                 sessions.add(currentSession); 
                
         }         
         @OnClose 
         public void onClose(Session session, CloseReason reason) { 
                 //todo 
         } 
         @OnError 
         public void onError(Throwable t) { 
                 //todo 
         } 
         /** 
          * Send a message to a all client 
          * @param message 
          */ 
 public void sendMessage(JSONArray message) throws Exception { 
        
                 ObjectMapper mapper = new ObjectMapper(); 
                 //send message to all online user 
                 for (Session session: sessions){ 
 session.getBasicRemote().sendText(message. toString()); 
                 } 
          } 
 }

使用 ServerEndpoint 注释的类必须有一个公共的无参数构造函数,@onMessage 注解的 Java 方法用于接收传入的 WebSocket 信息,这个信息可以是文本格式,也可以是二进制格式。

OnOpen 在这个端点一个新的连接建立时被调用。参数提供了连接的另一端的更多细节。Session 表明两个 WebSocket 端点对话连接的另一端,可以理解为类似 HTTPSession 的概念,我们可以将多个 Session 保存到 server 端,以便我们与客户端通信。OnClose 在连接被终止时调用。参数 closeReason 可封装更多细节,如为什么一个 WebSocket 连接关闭。OnError 在 websocket 通信出现异常才会调用。

在清单 3 中我们可以看出,如果我们有新消息需要推送到客户端,我们就可以调 sendMessage 方法,一旦客户端收到 message 就可以通过 Cordova plugin 调用系统 API 来实现消息提示。下文将着重讲解如何创建一个 Cordova plugin。

8、利用 Cordova plugin 调用本地 notification


以上我们了解了如何在 Hybrid App 中创建 websocket 连接,当我们收到信息时,这个时候只需要调用 IOS 或者 Android 的本地消息推送即可让用户知道有新的信息到达。这个时候我们就要创建一个新的 Cordova plugin 去触发 notification。

9、创建一个 Cordova plugin


Cordova plugin 是我们通过 JavaScript 调用系统 API 的中间件,一般情况下我们通过 JavaScript 不能够完成而系统 API 可以完成的任务时,我们就要创建一个 plugin。不同的平台在调用底层 API 时会有不同,但是前段代码不会改变。 以下我们将创建一个 Cordova plugin 用来调用本地消息推送。下文以 android 平台为例,其他平台思路类似。

1)在 cordova_plugins.js 中引入新的 plugin:
 { 
"file": "plugins/com.test.notification/www/notification.js", 
"id": "com.test.notification.localNotification", 
        "clobbers": [ 
"cordova.plugins.localNotification"
         ] 
 }

2)然后在 plugins/com.test.notification/www 目录下边创建 notification.js,代码示例如下:
 cordova.define("com.test.notification.localNotification",
  function(require, exports, module) { 
 var argscheck = require('cordova/argscheck'), 
    utils = require('cordova/utils'), 
    exec = require('cordova/exec'); 
 var localNotification = function() { 
 }; 
 localNotification.sendNotify = function(message,success, error) {  
   cordova.exec(success, error, 'localNotification', 'sendNotify', message); 
 }; 
 module.exports = localNotification; 
 });

3)创建一个 java 类来继承 Cordova 接口,代码示例如下:
 package com.test.notification; 
 import org.apache.cordova.CallbackContext; 
 import org.apache.cordova.CordovaInterface; 
 import org.apache.cordova.CordovaPlugin; 
 import org.apache.cordova.CordovaWebView; 
 import org.apache.cordova.PluginResult; 
 import org.json.JSONArray; 
 import org.json.JSONException; 
 import org.json.JSONObject; 

 //ellipsis code 
 // …… . 

 public class localNotification extends CordovaPlugin { 
         //ellipsis code 
                 // … .. 
         @Override 
         public boolean execute(String action, final JSONArray args, 
                         final CallbackContext callbackContext) throws JSONException { 
                 if ("sendNotify".equals(action)) { 
                      NotificationManager manager = (NotificationManager) this.cordova 
                          .getActivity().getSystemService( 
                              Context.NOTIFICATION_SERVICE); 

                      String title = args.getString(0); 
                      String text = args.getString(1); 
                      Notification notification 
                      = new Notification.Builder(this.cordova.getActivity()) 
                      .setTicker("New notification").setDefaults(1) 
                      .setSmallIcon(R.drawable.icon) 
                      .setAutoCancel(true) 
                      .setContentTitle(title).setContentText(text) 
                      .setContentIntent(PendingIntent.getActivity(this.cordova.getActivity(), 
                              0, this.cordova.getActivity().getIntent(), 0)).build(); 
                      manager.notify(1, notification); 

                      return true; 
                 } 
         } 
 }

4)在 res/xml/config.xml 配置 plugin feature:
 <feature name=" localNotification "> 
 <param name="android-package" 
 value="com.test.notification.
  localNotification" />  </feature>

至此一个完整的 plugin 已经建立完成。然后我们只需要在 JavaScript 端调用这个 plugin 就可以触发一个 notification,具体调用方式参考清单 4。

清单 4. 客户端调用系统 notification:
 var ws= new WebSocket("ws://localhost:8080/PushNotification"); 
 ws.onopen = function (event) { 
         console.log("connected to server"); 
 }; 
 ws.onmessage = function (event) { 
 var notifys = jQuery.parseJSON(evnt.data); 
    var message = ["New Job Notification",notifys[0].message]; 
    cordova.plugins.localNotification.sendNotify(message); 
 };

当执行 cordova.plugins.localNotification.sendNotify 方法时,cordova 会向 webview 发送一个 XMLHttpRequest 请求并且包含的参数中有 sendNotify 关键字,这个请求会被我们已经实现的 Cordova plugin 拦截住,并且执行一个 android Notification,这个时候一个消息就成功推送到客户端。如下图3所示。

图 3. 通过 websocket server 成功将信息推送到客户端:
基于WebSocket实现Hybrid移动应用的消息推送实践(含代码示例)_3.png

10、Websocket 消息推送的利弊


Websocket 消息推送优点 :

  • 开发周期短,维护成本低。
  • 消息不经转第三方服务器,直接由服务器发送到客户端,安全性好。基于 GCM 或者 APNS 的消息推送会把消息发送 GCM 服务器或 APNS 服务器,再由他们转发到客户端。
  • 自己开发服务端,可扩展性好。
  • 对于客户端而言,长连接比轮询的方式性能和及时性更好。

Websocket 消息推送缺点 :

  • 长连接浪费服务端资源。
  • 不能后台运行,一旦 app 退出就不能收到 notification。
  • 由于服务器保持多个长连接,性能将会下降,最大连接数也会有限制。

11、本文小结


随着手机性能的不断改善,hybrid app 的性能几乎接近原生 app 的体验,最重要的是还可以跨平台,所以 hybrid app 越来越受到开发者的青睐,尤其是前端开发者。他们既能利用熟悉的 html5, css3,angular js 作为主体开发语言,又能适时利用 Cordova plugin 调用底层的 API,这样既节约了开发成本,又能体验原生开发的乐趣。

本文就利用 Cordova 跨移动平台框架,开发了一组调用 local notification 的 android plugin, 前端代码不需要改变,我们只需要编写 iOS 端的 local notification 就可以适配一下 ios 平台。利用这种方法,我们同样可以扩展更多 plugin。而 websocket 作为 HTML5 的新特性,它不像传统的轮询查询服务端的方式而是主动 push 的方式向客户端推送消息,websocket 这种长连接的特性不仅适合消息推送,对于实时在线聊天功能也是非常适合。本文就结合了 websocket 和 Cordova 的特性开发了混合移动的消息推送。

(原文链接:https://www.ibm.com/developerworks/cn/mobile/mo-cn-websocket/index.html

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

上一篇:深入的聊聊Android消息推送这件小事下一篇:一个基于长连接的安全可扩展的订阅/推送服务实现思路

本帖已收录至以下技术专辑

推荐方案
评论 14
值得好好看下。
签名: 该会员没有填写今日想说内容.
这个很好,非常好
签名: 不错,很好
提示: 该帖被管理员或版主屏蔽
签名: 不错,很好
如果是vue webapp怎么使用呢?
vue项目中如何使用  cordova.plugins.localNotification.sendNotify(message)
引用:难得开发 发表于 2018-06-28 12:28
如果是vue webapp怎么使用呢?
vue项目中如何使用  cordova.plugins.localNotification.sendNotify(messag ...

vue不是混合框架吧,你直接调用html5的websocket API不就可以了
引用:JackJiang 发表于 2018-06-28 14:06
vue不是混合框架吧,你直接调用html5的websocket API不就可以了

现在websocket已经拿到了后台给的数据,现在是您文章的第八步和第九步,如何将消息推送到用户手机的通知栏里,如文章图所示。
引用:难得开发 发表于 2018-06-28 14:27
现在websocket已经拿到了后台给的数据,现在是您文章的第八步和第九步,如何将消息推送到用户手机的通知 ...

你的意思是,你在手机里打开的html5页面中收到了服务端通过websocket推送过来的数据,但不知道如何能将此数据显示在手机的通知栏里,是这个意思?
是的,就是这意思,消息已经拿到了,就是不知道怎么弄到通知栏里去,,,折磨人
引用:难得开发 发表于 2018-06-28 14:31
是的,就是这意思,消息已经拿到了,就是不知道怎么弄到通知栏里去,,,折磨人

其实你的问题就是在android的webview里如何调用系统api了,是吧
是的。怎么处理呢?
引用:难得开发 发表于 2018-06-28 14:54
是的。怎么处理呢?

你看看这个文章《Android:你要的WebView与 JS 交互方式 都在这里了
好的,谢谢了!!
websocket好像听说安全性不大好,是不是有这方面问题
喜欢
签名: 怎么开始
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部