WEB通知和React Native之即时通讯(iOS Android)

一,需求分析

1.1,允许服务器主动发送信息给客户端,客户端能监听到并且能接收。

1.2,为了方便同一个系统内的用户可以指定某个用户可以私聊。

1.3,给指定用户或多个用户发送通知。

二,技术介绍

2.1.WebSocket介绍

1,WebSocket 是什么?

  • WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。
  • WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

2,WebSocket 的作用

  • WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
  • HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

其他特点包括:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

3,WebSocket 构造函数

WebSocket 对象作为一个构造函数,用于新建 WebSocket 实例。执行如下语句之后,客户端就会与服务器进行连接。

  1. var ws = new WebSocket('ws://localhost:8080');

4,webSocket.readyState

  • CONNECTING:值为0,表示正在连接。
  • OPEN:值为1,表示连接成功,可以通信了。
  • CLOSING:值为2,表示连接正在关闭。
  • CLOSED:值为3,表示连接已经关闭,或者打开连接失败。

5,webSocket.onopen

实例对象的onopen属性,用于指定连接成功后的回调函数。

  1. ws.onopen = function () {
  2. ws.send('Hello Server!');
  3. }

如果要指定多个回调函数,可以使用addEventListener方法。

  1. ws.addEventListener('open', function (event) {
  2. ws.send('Hello Server!');
  3. });

6,webSocket.send()

实例对象的send()方法用于向服务器发送数据。

(1)发送文本

  1. ws.send('your message');

(2)发送 Blob 对象

  1. var file = document
  2. .querySelector('input[type="file"]')
  3. .files[0];
  4. ws.send(file);

(3)发送 ArrayBuffer 对象

  1. // Sending canvas ImageData as ArrayBuffer
  2. var img = canvas_context.getImageData(0, 0, 400, 320);
  3. var binary = new Uint8Array(img.data.length);
  4. for (var i = 0; i < img.data.length; i++) {
  5. binary[i] = img.data[i];
  6. }
  7. ws.send(binary.buffer);

(4)发送json对象

  1. var messageObj = {fromUserId:1,message:'您好,jackson影琪',toUserId:2};
  2. var messageJson = JSON.stringify(messageObj);
  3. ws.send(messageJson);

7,webSocket.onmessage

对象的onmessage属性,用于指定收到服务器数据后的回调函数。

  1. ws.onmessage = function(event) {
  2. var data = event.data;
  3. // 处理数据
  4. };
  5.  
  6. ws.addEventListener("message", function(event) {
  7. var data = event.data;
  8. // 处理数据
  9. });

服务器数据可能是文本,也可能是二进制数据(blob对象或Arraybuffer对象)

  1. ws.onmessage = function(event){
  2. if(typeof event.data === String) {
  3. console.log("Received data string");
  4. }
  5.  
  6. if(event.data instanceof ArrayBuffer){
  7. var buffer = event.data;
  8. console.log("Received arraybuffer");
  9. }
  10. }

8,webSocket.onclose

对象的onclose属性,用于指定连接关闭后的回调函数。

  1. ws.onclose = function(event) {
  2. var code = event.code;
  3. var reason = event.reason;
  4. var wasClean = event.wasClean;
  5. // handle close event
  6. };
  7.  
  8. ws.addEventListener("close", function(event) {
  9. var code = event.code;
  10. var reason = event.reason;
  11. var wasClean = event.wasClean;
  12. // handle close event
  13. });

9,webSocket.onerror

对象的onerror属性,用于指定报错时的回调函数。

  1. ws.onerror = function(event) {
  2. // handle error event
  3. };
  4.  
  5. ws.addEventListener("error", function(event) {
  6. // handle error event
  7. });

2.2,react-native-gifted-chat介绍

  1. messages(Array) - 消息数组,用于展示消息 有特定的格式

    1. {
    2. _id: 1, //消息的ID
    3. text: 'My message', //发送的消息内容
    4. createdAt: new Date(), //发送的时间
    5. user: {/发送方的用户信息
    6. _id: 2, //发送方的ID
    7. name: 'Jackson', //发送方的昵称
    8. avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',, //发送方的头像
    9. },
    10. image: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',
    11. //添加你所需要扩展的键值对
    12. }
  2. user(Object) - 配置用户信息
    1. {
    2. _id: 1, //发送消息需要和配置的id一致 avatar:'https://pic.cnblogs.com/avatar/1040068/20181013100635.png', //头像 若不设置则不显示
    3. name:'jackson影琪', //昵称
    4. }
  3. renderBubble(Function) - 自定义气泡
    1. //气泡
    2. renderBubble(props) {
    3. return (
    4. <Bubble
    5. {...props}
    6. wrapperStyle={{
    7. left: {//对方的气泡
    8. backgroundColor: '#ffffff',
    9. },
    10. right: {//我方的气泡
    11. backgroundColor: '#1fb922',
    12. }
    13. }}
    14. />
    15. );
    16. }
  4. text(String) - 输入框的默认值;默认是undefined
  5. placeholder(String) - 输入框的占位字符
  6. messageIdGenerator(Function) - 为你的新消息自动生成一个id. 默认是用 UUID v4, 由uuid库实现uuid
  7. onSend(Function) - 点击send时的回调
  8. locale(String) -本地化日期
  9. timeFormat(String) - 格式化时间,默认是本地时间,即当前时区的时间
  10. dateFormat(String) - 日期格式化
  11. isAnimated(Bool) - 键盘出现时,是否有动画
  12. loadEarlier(Bool) - 是否显示加载更早的消息按钮 "Load earlier messages"
  13. onLoadEarlier(Function) - 加载更多消息时的回调
  14. isLoadingEarlier(Bool) - 点击加载更早的消息时是否出现转菊花的图标
  15. renderLoading(Function) - 加载页面未加载出来时的页面
    1. //加载更多消息
    2. loadEarlier={self.state.isMore}//
    3. isLoadingEarlier={self.state.isMore}//
    4. renderLoadEarlier={() => {
    5. return (
    6. <Text
    7. onPress={self.onLoadEarlier}
    8. style={[
    9. styles.LookMoreStyle
    10. ]}
    11. >{self.state.moreData}</Text>
    12. );
    13. }}
  16. renderLoadEarlier(Function) - 配置 "Load earlier messages" 加载更早消息的按钮
  17. renderAvatar(Function) - 配置头像,如果设置'null'则头像都不显示
    1. //头像
    2. renderAvatar(props) {
    3. return (
    4. <Avatar
    5. {...props}
    6. />
    7. );
    8. }
  18. showUserAvatar(Bool) - 是否展示自己的头像,默认时false 只展示别人的头像
  19. onPressAvatar(Function(user)) - 点击头像时的回调
  20. renderAvatarOnTop(Bool) 头像显示在顶部还是底部,默认是底部
  21. renderSystemMessage(Function) - 自定义系统消息
  22. onLongPress(Function(context,message)) - 长按消息气泡时的回调,详细可以看github的演示 showActionSheetWithOptions()
  23. inverted(Bool) - 反转消息的显示顺序,默认是true 即消息显示的顺序是否和你message数组的顺序相同
  24. renderMessage(Function) - 自定义消息的内容View
  25. renderMessageText(Function) - 自定义消息的文本
  26. renderMessageImage(Function) - 自定义图片消息
  27. imageProps(Object) - 额外的属性要传递给默认创建的组件rendermessageimage点去去查看文档
  28. lightboxProps(Object) - 额外的属性传递给Modal框(体现在点击图片的Modal)
  29. 点击查看第三方 - Lightbox
  30. renderCustomView(Function) - 在气泡内创建一个自己自定义的视图,即创建自定义的消息
  31. renderDay(Function) - 自定义消息上方的日期
  32. renderTime(Function) - 自定义消息中的时间
  33. renderFooter(Function) - 自定义listView的底部, 例如.'User is typing...'; 点击查看示例 example/App.js for an example
  34. renderChatFooter(Function) - 自定义组件的渲染下messagecontainer(从ListView分开)
  35. renderInputToolbar(Function) - 自定义你的底部工具栏
  36. renderComposer(Function) - 自定义textInput输入框
  37. renderActions(Function) - 自定义输入框左边的按钮
  38. renderSend(Function) -自定义发送按钮;您可以很容易地将子组件传递给原始组件,例如使用自定义图标。
  39. renderAccessory(Function) - 在消息编辑器下面的自定义第二行操作
  40. onPressActionButton\(Function) - 当点击输入框左边的按钮时的回调 (如果设置了 actionSheet将不会执行)
  41. bottomOffset(Integer) - 从屏幕底部的聊天距离(如显示选项卡栏,则非常有用)
  42. minInputToolbarHeight(Integer) - 工具栏的最小高度,默认是44
  43. listViewProps(Object) - 列表的属性,用于扩展你的列表
    1. listViewProps={{
    2. // //ListView/FlatView中标识是否可以加载更多(当现在获取到的数据已经是全部了,不能再继续获取数据了,则设为false,当还有数据可以获取则设为true)
    3. canLoad: true,
    4. //标识现在是否ListView/FlatView现在正在加载(根据这个值来决定是否显示"正在加载的cell")(loadMore()方法进去后设为true,fetch加载完数据后设为false)
    5. isLoadding: false,
    6. //是否显示下拉刷新的cell
    7. ifShowRefresh: true,
    8. //ListView/FlatList是否可以滚动
    9. scrollEnabled: true,
    10. //记录当前加载到了哪一页
    11. page: 1,
    12. onScroll:self._onScroll.bind(this)
    13. }}
  44. textInputProps(Object) - 输入框的属性,用于扩展你的输入框
  45. keyboardShouldPersistTaps(Enum) - 确定键盘在敲击后是否应该保持可见。一个枚举; 详情见 <ScrollView>
  46. onInputTextChanged(Function) - 输入框编辑时的回调
  47. maxInputLength(Integer) - 输入框输入的最多字符数
  48. showAvatarForEveryMessage(Bool) - 默认是false每条消息都显示头像

系统消息格式

  1. {
  2. _id: 1,
  3. text: 'This is a system message',
  4. createdAt: new Date(),
  5. system: true,
  6. // Any additional custom parameters are passed through
  7. }

三,即时通讯实现

3.1,实现步骤

第一步:建立链接

  1. componentWillMount() {
  2. let self = this;
  3. //建立链接
  4. ws = new WebSocket('ws://127.0.0.1:8080/websocket/'+str);
  5. ws.onopen = (evt) => {
  6. // 打开一个连接
  7. // console.log('WebSocket==' + evt)
  8. alert("连接成功啦")
  9. //ws.send('something'); // 发送一个消息
  10. };
  11. ws.onmessage = (e) => {
  12. // }
  13. // 接收到了一个消息
  14. //alert(JSON.parse(e.data).text)
  15. console.log('e.data==' + e.data);
  16. };
  17.  
  18. ws.onerror = (e) => {
  19. // 发生了一个错误
  20. console.log('e.message==' + e.message);
  21. };
  22.  
  23. ws.onclose = (e) => {
  24. // 连接被关闭了
  25. console.log('e.code===' + e.code, 'e.reason===' + e.reason);
  26. };
  27. }

第二步:发送消息

  1. onSend(messages = []) {
  2. let self = this
  3. this.setState(previousState => ({
  4. messages: GiftedChat.append(previousState.messages, messages),
  5. }))
  6. // alert(messages[0].text)
  7. this.doSend(messages[0].text)
  8. }
  9.  
  10. // 发送消息
  11. doSend = (message) => {
  12. var messageObj = {
  13. fromUserId: this.state.userData._id,
  14. fromNickName: this.state.userData.name,
  15. message: message,
  16. toUserId: this.props.Account.id,
  17. toNickName: this.props.Account.name,
  18. sendTime: new Date()
  19.  
  20. };
  21. var messageJson = JSON.stringify(messageObj);
  22. ws.send(messageJson);
  23. }

第三步:接收消息

  1. ws.onmessage = (e) => {
  2. // {
  3. // _id: 1, //消息的ID
  4. // text: 'My message', //发送的消息内容
  5. // createdAt: new Date(), //发送的时间
  6. // user: {/发送方的用户信息
  7. // _id: 2, //发送方的ID
  8. // name: 'Jackson', //发送方的昵称
  9. // avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',, //发送方的头像
  10. // },
  11. // }
  12. // 接收到了一个消息
  13. //alert(JSON.parse(e.data).text)
  14. console.log('e.data==' + e.data);
  15. }
 
第四步:关闭链接
  1. componentWillUnmount() {
  2. ws.close()
  3. this.setState = (state, callback) => {
  4. return;
  5. };
  6. }

3.2.聊天界面构建

1,使用react-native-gifted-chat,安装

  1. npm install react-native-gifted-chat --save

2,引入使用

  1. /**
  2. * Created by Jackson on 2018/11/12.
  3. * 聊天界面
  4. */
  5. import React, { PureComponent } from 'react';
  6. import {
  7. View,
  8. Text,
  9. StyleSheet,
  10. TouchableOpacity,
  11. Keyboard,
  12. Platform,
  13. StatusBar
  14. } from 'react-native';
  15. //聊天
  16. import { GiftedChat, Bubble, Avatar } from 'react-native-gifted-chat'
  17. export default class ChatBox extends PureComponent {
  18. constructor(props) {
  19. super(props);
  20. this.renderBubble = this.renderBubble.bind(this);
  21. this.renderAvatar = this.renderAvatar.bind(this);
  22. this.state = {
  23. //聊天
  24. messages: [],
  25. userData: {
  26. _id: 1,
  27. name:'jackson影琪',
  28. avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',
  29. },
  30. messageId: 1,
  31. }
  32.  
  33. }
  34. componentDidMount() {
  35. let self = this
  36. /****************************聊天组件 start **************************************************/
  37. setTimeout(function(){
  38. self.setState({
  39. messages: [
  40.  
  41. {
  42. _id: 2,
  43. text: '微信小程序开发的基本流程',
  44. createdAt: new Date('2018-10-25T15:41:00+08:00'),
  45. user: {
  46. _id: 1,
  47. name: 'jackson影琪',
  48. avatar: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',
  49. },
  50. //image: 'https://img2018.cnblogs.com/blog/1040068/201810/1040068-20181024162047704-1159291775.png',
  51. },
  52. {
  53. _id: 1,
  54. text: 'Hello jackson影琪',
  55. createdAt: new Date('2016-06-07T10:00:00+08:00'),
  56. user: {
  57. _id: 2,
  58. name: 'jackson',
  59. avatar: 'https://img2018.cnblogs.com/blog/1040068/201811/1040068-20181101192529807-2132606645.jpg'
  60. },
  61. image: 'https://pic.cnblogs.com/avatar/1040068/20181013100635.png',
  62. },
  63. ],
  64. })
  65. },2000)
  66. /****************************聊天组件 end **************************************************/
  67.  
  68. }
  69.  
  70. /****************************聊天 start **************************************************/
  71. onSend(messages = []) {
  72. this.setState(previousState => ({
  73. messages: GiftedChat.append(previousState.messages, messages),
  74. }))
  75. // alert(messages[0].text)
  76. let self = this
  77. self.state.messageId += 2
  78. let m = {
  79. _id: self.state.messageId,
  80. text: '前端知识点总结(HTML)',
  81. createdAt: new Date(),
  82. user: {
  83. _id: 2,
  84. name: '',
  85. avatar: 'https://img2018.cnblogs.com/blog/1040068/201811/1040068-20181101192529807-2132606645.jpg'
  86. },
  87. image: 'https://img2018.cnblogs.com/blog/1040068/201811/1040068-20181109115100292-977588541.png',
  88. }
  89. self.setState(previousState => ({
  90. messages: GiftedChat.append(previousState.messages, m),
  91. }))
  92. }
  93. //气泡
  94. renderBubble(props) {
  95. return (
  96. <Bubble
  97. {...props}
  98. wrapperStyle={{
  99. left: {
  100. backgroundColor: '#ffffff',
  101. },
  102. right: {
  103. backgroundColor: '#1fb922',
  104. }
  105. }}
  106. />
  107. );
  108. }
  109. //头像
  110. renderAvatar(props) {
  111. return (
  112. <Avatar
  113. {...props}
  114. />
  115. );
  116. }
  117. /****************************聊天 end **************************************************/
  118. render() {
  119. let self = this;
  120. return (
  121. <TouchableOpacity
  122. activeOpacity={1}
  123. style={{ flex: 1,}}
  124. onPress={() => { Keyboard.dismiss() }}
  125. >
  126.  
  127. {/* //聊天 */}
  128. <GiftedChat
  129. // onPressAvatar={()=>{alert('Keyboard.dismiss'); Keyboard.dismiss()}}
  130. messages={this.state.messages}
  131. onSend={messages => this.onSend(messages)}
  132. renderBubble={this.renderBubble}//气泡
  133. renderAvatar={this.renderAvatar}//头像
  134. showUserAvatar={true}//是否显示自己的头像,默认不显示
  135. //onLongPress={()=>{alert('onLongPress')}}//长按消息
  136. // 输入组件
  137. placeholder={'请输入内容'}//输入框占位符
  138. // label={'发送'}
  139. containerStyle={{ marginBottom: 2 }}//发送按钮
  140. children={
  141. <View
  142. style={[
  143. styles.buttonBoxBorder
  144. ]}
  145. >
  146. <Text
  147. style={[
  148. styles.buttonText,
  149. ]}
  150. >发送</Text>
  151. </View>
  152. }
  153. // textStyle={{ color: '#70b24e' }}//按钮字的颜色
  154. timeFormat={'MM月DD日 HH:mm:ss'}//格式化日前
  155. dateFormat={'YYYY年MM月DD日'}
  156. // locale={'zh-cn'}
  157. isAnimated={true}
  158. // renderAvatarOnTop={true}
  159. user={this.state.userData}//用户信息
  160. />
  161. </TouchableOpacity>
  162. )
  163. }
  164.  
  165. }
  166.  
  167. const styles = StyleSheet.create({
  168. buttonText: {
  169. paddingHorizontal: 15,
  170. paddingVertical: 5,
  171. textAlign: 'center',
  172. color: '#fff',
  173. fontSize: 14
  174. },
  175. buttonBoxBorder: {
  176. overflow: 'hidden',
  177. borderRadius: 5,
  178. borderWidth: 1,
  179. backgroundColor: "#70b24e",
  180. borderColor: "#70b24e",
  181. marginRight: 12,
  182. marginBottom: 6,
  183. },
  184. })
  1. 效果如下:

3.3,使用的方法

1,下拉加载更多

  1. {/* //聊天 */}
  2. <GiftedChat
  3. // onPressAvatar={()=>{alert('Keyboard.dismiss'); Keyboard.dismiss()}}
  4. messages={this.state.messages}
  5. onSend={messages => this.onSend(messages)}//发送消息
  6.  
  7. ...
  8.  
  9. //加载更多消息
  10. loadEarlier={self.state.isMore}//
  11. isLoadingEarlier={self.state.isMore}//
  12. renderLoadEarlier={() => {
  13. return (
  14. <Text
  15. onPress={self.onLoadEarlier}
  16. style={[
  17. styles.LookMoreStyle
  18. ]}
  19. >{self.state.moreData}</Text>
  20. );
  21. }}
  22.  
  23. listViewProps={{
  24. // //ListView/FlatView中标识是否可以加载更多(当现在获取到的数据已经是全部了,不能再继续获取数据了,则设为false,当还有数据可以获取则设为true)
  25. canLoad: true,
  26. //标识现在是否ListView/FlatView现在正在加载(根据这个值来决定是否显示"正在加载的cell")(loadMore()方法进去后设为true,fetch加载完数据后设为false)
  27. isLoadding: false,
  28. //是否显示下拉刷新的cell
  29. ifShowRefresh: true,
  30. //ListView/FlatList是否可以滚动
  31. scrollEnabled: true,
  32. //记录当前加载到了哪一页
  33. page: 1,
  34. onScroll:self._onScroll.bind(this)
  35. }}
  36. />
  1. //加载更早的数据
  2. onLoadEarlier = () => {
  3. let self = this;
  4. self.state.Currentpage += 1;
  5. self.setState({
  6. isMore: true,
  7. moreData: '正在加载更多...'
  8. })
  9. self.getMessageData()
  10. }
  11.  
  12. //上拉加载//翻页
  13. _onScroll(event) {
  14. let self = this
  15. let y = event.nativeEvent.contentOffset.y;
  16. let height = event.nativeEvent.layoutMeasurement.height;
  17. let contentHeight = event.nativeEvent.contentSize.height;
  18. if (y + height >= contentHeight - 20 && y > 0 && this.state.contentHeight != contentHeight) {//上啦下一页
  19. self.state.contentHeight=contentHeight
  20. self.onLoadEarlier()
  21.  
  22. }
  23. else if (y < 0 || y == 0) {//下拉上一页ios
  24.  
  25. }
  26. }

2,在消息前后追加消息

  1. //prepend(),在父级最前面追加一个子元素
  2. self.setState(previousState => ({
  3. messages: GiftedChat.prepend(previousState.messages, ReceivedMessageData),
  4. }))
  5.  
  6. //append(),在父级最后追加一个子元素
  7. this.setState(previousState => ({
  8. messages: GiftedChat.append(previousState.messages, messages),
  9. }))

3,完整代码

  1. {/* //聊天 */}
  2. <GiftedChat
  3. // onPressAvatar={()=>{alert('Keyboard.dismiss'); Keyboard.dismiss()}}
  4. messages={this.state.messages}
  5. onSend={messages => this.onSend(messages)}//发送消息
  6. renderBubble={this.renderBubble}//气泡
  7. renderAvatar={this.renderAvatar}//头像
  8. showUserAvatar={true}// 显示发送方的头像
  9. showAvatarForEveryMessage={true}//每条消息都显示头像
  10. //onLongPress={()=>{alert('onLongPress')}}
  11. // 输入组件
  12. placeholder={'请输入内容'}
  13. // label={'发送'}
  14. containerStyle={{ marginBottom: 2 }}
  15. children={
  16. <View
  17. style={[
  18. styles.buttonBoxBorder
  19. ]}
  20. >
  21. <Text
  22. style={[
  23. styles.buttonText,
  24. ]}
  25. >发送</Text>
  26. </View>
  27. }//渲染发送按钮
  28. // textStyle={{ color: '#70b24e' }}
  29. timeFormat={'MM月DD日 HH:mm:ss'}
  30. dateFormat={'YYYY年MM月DD日'}
  31. // locale={'zh-cn'}
  32. isAnimated={true}
  33. // renderAvatarOnTop={true}
  34. user={this.state.userData}
  35.  
  36. // 系统消息样式
  37. wrapperStyle={{ paddingLeft: 12, paddingRight: 12 }}
  38. textStyle={{ lineHeight: 20 }}
  39. //加载更多消息
  40. loadEarlier={self.state.isMore}//
  41. isLoadingEarlier={self.state.isMore}//
  42. renderLoadEarlier={() => {
  43. return (
  44. <Text
  45. onPress={self.onLoadEarlier}
  46. style={[
  47. styles.LookMoreStyle
  48. ]}
  49. >{self.state.moreData}</Text>
  50. );
  51. }}
  52.  
  53. listViewProps={{
  54. // //ListView/FlatView中标识是否可以加载更多(当现在获取到的数据已经是全部了,不能再继续获取数据了,则设为false,当还有数据可以获取则设为true)
  55. canLoad: true,
  56. //标识现在是否ListView/FlatView现在正在加载(根据这个值来决定是否显示"正在加载的cell")(loadMore()方法进去后设为true,fetch加载完数据后设为false)
  57. isLoadding: false,
  58. //是否显示下拉刷新的cell
  59. ifShowRefresh: true,
  60. //ListView/FlatList是否可以滚动
  61. scrollEnabled: true,
  62. //记录当前加载到了哪一页
  63. page: 1,
  64. onScroll:self._onScroll.bind(this)
  65. }}
  66. />

效果展示:

注意:

1,如下格式的图片链接不能正常显示

  1. avatar: 'http://img3.imgtn.bdimg.com/it/u=1614455141,2952757874&fm=26&gp=0.jpg',

四,后台实现

4.1,Java spring cloud实现

Java 的 web 一般都依托于 servlet 容器。Tomcat、Jetty、Resin等。Spring 框架对 WebSocket 也提供了支持。

1.Spring 对于 WebSocket 的支持基于下面的 jar 包:

  1. <dependency>
  2. <groupId>javax.websocket</groupId>
  3. <artifactId>spring-websocket</artifactId>
  4. <version>${spring.version}</version>
  5. </dependency>

2.Spring 在收到 WebSocket 事件时,会自动调用事件对应的方法。

  1. import javax.websocket.*;
  2. import javax.websocket.server.PathParam;
  3. import javax.websocket.server.ServerEndpoint;
  4. import java.io.IOException;
  5. import java.util.Date;
  6. import java.util.Map;
  7. import java.util.concurrent.ConcurrentHashMap;
  8. public class WebSocketService {
  9.  
  10. private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketService.class);
  11. // ...
  12.  
  13. }

3.完整代码实现

  1. ...
  2.  
  3. public class WebSocketService {
  4. private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketService.class);
  5.  
  6. public static Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>();
  7.  
  8. private HcAppchatService hcAppchatService = SpringContextHandler.getBean(HcAppchatService.class);
  9. /**
  10. * 建立连接后触发的回调
  11. */
  12. @OnOpen
  13. public void onOpen(@PathParam("userId") String userId, Session session) {
  14. LOGGER.info("聊天打开onOpen:userId={}", userId);
  15. if (sessionMap == null) {
  16. sessionMap = new ConcurrentHashMap<String, Session>();
  17. }
  18. /**
  19. * 断开连接后触发的回调
  20. */
  21. @OnClose
  22. public void OnClose(@PathParam("userId") String userId) {
  23. LOGGER.info("聊天关闭OnClose:userId={}", userId);
  24. sessionMap.remove(userId);
  25. }
  26. /**
  27. * 收到消息时触发的回调
  28. */
  29. @OnMessage
  30. public void OnMessage(@PathParam("userId") String userId, Session session, String message) throws IOException{
  31. LOGGER.info("发送消息:userId={}", userId);
  32. LOGGER.info("发送消息:message={}", message);
  33. HcAppchat hcAppchat = JSON.parseObject(message, HcAppchat.class);
  34. sendMessageTo(hcAppchat);
  35. //sendMessageAll(message);
  36. }
  37. /**
  38. * 传输消息出错时触发的回调
  39. */
  40. @OnError
  41. public void error(Session session, Throwable t) {
  42. LOGGER.error("socket通讯出现异常:", t.getMessage());
  43. t.printStackTrace();
  44. }
  45.  
  46. public void sendMessageTo(HcAppchat hcAppchat) throws IOException {
  47. Session se = sessionMap.get(String.valueOf(hcAppchat.getAcceptId()));
  48. Date now = new Date();
  49. hcAppchat.setCreateDate(now);
  50. if(se != null){
  51. WebMessage webms = new WebMessage();
  52. hcAppchat.setStatus(1);
  53. boolean result = hcAppchatService.insert(hcAppchat);
  54. LOGGER.info("用户在线,直接发送消息:result={}", result);
  55. webms.setId(hcAppchat.getId());
  56. webms.setCreatedAt(DateUtil.dateStr(now, "yyyy-MM-dd HH:mm:ss"));
  57. webms.setText(hcAppchat.getText());
  58. User user = hcAppchatService.queryUserInfo(hcAppchat.getSendId());
  59. webms.setUser(user);
  60. LOGGER.info("发送消息给【" + user.getName() + "】, message={}", JSON.toJSONString(webms));
  61. se.getAsyncRemote().sendText(JSON.toJSONString(webms));
  62. }else{
  63. hcAppchat.setStatus(0);
  64. boolean result = hcAppchatService.insert(hcAppchat);
  65. if(result){
  66. LOGGER.info("接受消息用户不在线,将消息保存数据库成功!");
  67. }else{
  68. LOGGER.info("接受消息用户不在线,将消息保存数据库失败!");
  69. }
  70. }
  71. }
  72. se.getAsyncRemote().sendText(message);
  73. }
  74. }
  75. }

4.2,nodeJS实现

常用的 Node 实现有以下三种。

下面以socket.io为例

  1. var IO = require('socket.io');
  2. //var dbservice = require('./services/db_mssql.js');//链接数据库
  3. //var settingConfig = require('./config/settingConfig.js');//解析存储过程
  4.  
  5. //var dbName = settingConfig.getValueByKey("dbName");
  6.  
  7. var socketFun = function (server) {
  8. var socketIO = IO(server);
  9. var userSockets = {};
  10. socketIO.on('connection', function (socket) {
  11.  
  12. //已建立链接 加入
  13. socket.on('join', function (userId) {
  14. socket.userId = userId;
  15. userSockets[userId] = socket;
  16. })
  17. //发送通知
  18. socket.on('notification', function (json) {
  19. if (socket.userId == undefined) {
  20. socket.emit('notification', {
  21. "httpCode": 500,
  22. "message": "请登录后再发送消息",
  23. "data": {}
  24. });
  25. return;
  26. }
  27. //var spName = "存储过程的代称";
  28. json.createPeopleId = socket.userId;
  29. //支持多人接收消息
  30. var receivePeopleIds = [];
  31. if (json.receivePeopleId!=null)
  32. receivePeopleIds = json.receivePeopleId.split(';');
  33. for (var i = 0; i < receivePeopleIds.length; i++) {
  34.  
  35. var json = {
  36. "receivePeopleId": receivePeopleIds[i],
  37. "content": json.content,
  38. "url": json.url,
  39. "creatPeopleId": json.creatPeopleId
  40. };
  41. console.log('-------json---------',json);
  42. //dbservice.operateDatabase(dbName, spName, json, function (data) {//存进数据库
  43. //console.log(data);
  44. //});
  45. var otherSocket = userSockets[json.receivePeopleId]
  46. if (otherSocket != null) {
  47. otherSocket.emit('notification', {
  48. "httpCode": 200,
  49. "message": "",
  50. "data": json
  51. });
  52. }
  53. }
  54. });
  55. //关闭链接
  56. socket.on('disconnect', function () {
  57. var userId = socket.userId;
  58. delete userSockets[userId];
  59. });
  60. })
  61. }
  62.  
  63. module.exports = socketFun;

web端调用实例

  1. var socket = io('ws://127.0.0.1:3000');//链接消息系统
  2.  
  3. socket.on('connect', function () {//建立链接
  4. socket.emit('join', userId);
  5. console.log('1')
  6. });
  7. var json = {
  8. "receivePeopleId": createId,
  9. "content": content,
  10. "url": TaskUrl,
  11. "creatPeopleId": d.CreateUserId
  12. };
  13. socket.emit('notification', json);//发送通知

WEB通知和React Native之即时通讯(iOS Android)的更多相关文章

  1. React Native之微信分享(iOS Android)

    React Native之微信分享(iOS Android) 在使用React Native开发项目的时候,基本都会使用到微信好友或者微信朋友圈分享功能吧,那么今天我就带大家实现以下RN微信好友以及朋 ...

  2. React Native 真机调试(iOS / Android)

    React Native 真机调试(iOS / Android) https://reactnative.dev/docs/running-on-device https://developer.ap ...

  3. 30天React Native从零到IOS/Android双平台发布总结

    前言 本人有近十年的技术背景,除了APP开发之外对后端.前端等都比较熟悉,近期做一个APP项目需要IOS.Android两个平台都需要,只能硬着头皮上.其实很早就想开发APP也很早就接触Android ...

  4. React Native之通知栏消息提示(android)

    React Native之通知栏消息提示(android) 一,需求分析与概述 1.1,推送作为手机应用的基本功能,是手机应用的重要部分,如果自己实现一套推送系统费时费力,所以大部分的应用都会选择使用 ...

  5. 教程视频、项目源码、全部干货【微信小程序、React Native、Java、iOS、数据结构】

    把我收藏多年的教学视频.项目源码分享给大家,大神就可以忽略了,很多东西都是基础性的,都是期初学习阶段收集的东西. 微信小程序(入门级,有web前端基础的人群): 链接: https://pan.bai ...

  6. React Native分析(index.ios.js)

    定义创建组件MyComponent(index.ios.js): 'use strict' var React = require('react-native'); var { AppRegistry ...

  7. 【REACT NATIVE 系列教程之十二】REACT NATIVE(JS/ES)与IOS(OBJECT-C)交互通信

    http://blog.csdn.net/xiaominghimi/article/details/51586492 一用到跨平台的引擎必然要有引擎与各平台原生进行交互通信的需要.那么Himi先讲解R ...

  8. react native报错处理com.android.build.api.transform.TransformException: com.android.builder.dexing.DexArchiveBuilderException: com.android.builder.dexing.DexArchiveBuilderException: Failed to process

    背景:最近准备在使用react-native开发的app中接入友盟,来进行用户行为统计,分享,授权登录等操作. 在使用的过程中,遇到了一些错误信息,在此记录一下. 在修改android目录下的buil ...

  9. react native 5.54 出ios版本遇到的坑(应该是在xcode10下才会有的吧)记录。。。。。。 据说5.7已经修复了

    1. config.h找不到 rm -r ~/.rncache/cd node_modules/react-native/third-party/glog-0.3.4/./configure --ho ...

随机推荐

  1. 杭电ACM2012--素数判定

    素数判定 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submis ...

  2. Python全栈开发之---mysql数据库

    1.数据库的安装和连接 #数据库安装 pip install PyMySQL #数据库操作 import pymysql db = pymysql.connect("数据库ip", ...

  3. Java几种常见的设计模式

    --------------------- 本文来自 旭日Follow_24 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/xuri24/article/detail ...

  4. 学习前端笔记1(HTML)

    (注:此文是在看过许多学习资料和视频之后,加上自身理解拼凑而成,仅作学习之用.若有版权问题,麻烦及时联系) 标准页面结构: HTML发展历史:  注:每一种HTML需要有对应的doctype声明. H ...

  5. java 线程方法 ---- join()

    class MyThread2 implements Runnable{ @Override public void run() { for (int i = 0; i < 5; i++){ S ...

  6. windows蓝屏代码

    原始链接 引用自  https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/bug-check-code-referenc ...

  7. android FragmentTabhost导航分页

    基本模板 public class MainActivity extends FragmentActivity { private FragmentTabHost mTabHost; private ...

  8. chart 模板 - 每天5分钟玩转 Docker 容器技术(165)

    Helm 通过模板创建 Kubernetes 能够理解的 YAML 格式的资源配置文件,我们将通过例子来学习如何使用模板. 以 templates/secrets.yaml 为例: 从结构看,文件的内 ...

  9. Python XML解析之ElementTree

    参考网址: http://www.runoob.com/python/python-xml.html https://docs.python.org/2/library/xml.etree.eleme ...

  10. python模块shutil

    shutil.copyfileobj(fsrc, fdst,[ length]) 拷贝文件句柄,将类文件对象fsrc的内容复制到类文件对象fdst.如果给定整数长度,则为缓冲区大小.如果长度是负值意味 ...