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


    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 
     71   /****************************聊天 start **************************************************/
     72   onSend(messages = []) {
     73     this.setState(previousState => ({
     74       messages: GiftedChat.append(previousState.messages, messages),
     75     }))
     76     //  alert(messages[0].text)
     77     let self = this
     78     self.state.messageId += 2
     79     let m = {
     80       _id: self.state.messageId,
     81       text: '前端知识点总结(HTML)',
     82       createdAt: new Date(),
     83       user: {
     84         _id: 2,
     85         name: '',
     86         avatar: 'https://img2018.cnblogs.com/blog/1040068/201811/1040068-20181101192529807-2132606645.jpg'
     87       },
     88       image: 'https://img2018.cnblogs.com/blog/1040068/201811/1040068-20181109115100292-977588541.png',
     89     }
     90     self.setState(previousState => ({
     91       messages: GiftedChat.append(previousState.messages, m),
     92     }))
     93   }
     94   //气泡
     95   renderBubble(props) {
     96     return (
     97       <Bubble
     98         {...props}
     99         wrapperStyle={{
    100           left: {
    101             backgroundColor: '#ffffff',
    102           },
    103           right: {
    104             backgroundColor: '#1fb922',
    105           }
    106         }}
    107       />
    108     );
    109   }
    110   //头像
    111   renderAvatar(props) {
    112     return (
    113       <Avatar
    114         {...props}
    115       />
    116     );
    117   }
    118   /****************************聊天 end **************************************************/
    119   render() {
    120     let self = this;
    121     return (
    122       <TouchableOpacity
    123         activeOpacity={1}
    124         style={{ flex: 1,}}
    125         onPress={() => { Keyboard.dismiss() }}
    126       >
    127 
    128         {/* //聊天 */}
    129         <GiftedChat
    130           //   onPressAvatar={()=>{alert('Keyboard.dismiss'); Keyboard.dismiss()}}
    131           messages={this.state.messages}
    132           onSend={messages => this.onSend(messages)}
    133           renderBubble={this.renderBubble}//气泡
    134           renderAvatar={this.renderAvatar}//头像
    135           showUserAvatar={true}//是否显示自己的头像,默认不显示
    136           //onLongPress={()=>{alert('onLongPress')}}//长按消息
    137           // 输入组件
    138           placeholder={'请输入内容'}//输入框占位符
    139           // label={'发送'}
    140           containerStyle={{ marginBottom: 2 }}//发送按钮
    141           children={
    142             <View
    143               style={[
    144                 styles.buttonBoxBorder
    145               ]}
    146             >
    147               <Text
    148                 style={[
    149                   styles.buttonText,
    150                 ]}
    151               >发送</Text>
    152             </View>
    153           }
    154           // textStyle={{ color: '#70b24e' }}//按钮字的颜色
    155           timeFormat={'MM月DD日 HH:mm:ss'}//格式化日前
    156           dateFormat={'YYYY年MM月DD日'}
    157           // locale={'zh-cn'}
    158           isAnimated={true}
    159           // renderAvatarOnTop={true}
    160           user={this.state.userData}//用户信息
    161         />
    162       </TouchableOpacity>
    163     )
    164   }
    165 
    166 }
    167 
    168 const styles = StyleSheet.create({
    169   buttonText: {
    170     paddingHorizontal: 15,
    171     paddingVertical: 5,
    172     textAlign: 'center',
    173     color: '#fff',
    174     fontSize: 14
    175   },
    176   buttonBoxBorder: {
    177     overflow: 'hidden',
    178     borderRadius: 5,
    179     borderWidth: 1,
    180     backgroundColor: "#70b24e",
    181     borderColor: "#70b24e",
    182     marginRight: 12,
    183     marginBottom: 6,
    184   },
    185 })
    效果如下:

    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 
    11           //加载更多消息
    12           loadEarlier={self.state.isMore}//
    13           isLoadingEarlier={self.state.isMore}//
    14           renderLoadEarlier={() => {
    15             return (
    16               <Text
    17                 onPress={self.onLoadEarlier}
    18                 style={[
    19                   styles.LookMoreStyle
    20                 ]}
    21               >{self.state.moreData}</Text>
    22             );
    23           }}
    24 
    25           listViewProps={{
    26             // //ListView/FlatView中标识是否可以加载更多(当现在获取到的数据已经是全部了,不能再继续获取数据了,则设为false,当还有数据可以获取则设为true)
    27              canLoad: true,
    28            //标识现在是否ListView/FlatView现在正在加载(根据这个值来决定是否显示"正在加载的cell")(loadMore()方法进去后设为true,fetch加载完数据后设为false)
    29              isLoadding: false,
    30              //是否显示下拉刷新的cell
    31             ifShowRefresh: true,
    32              //ListView/FlatList是否可以滚动
    33              scrollEnabled: true,
    34              //记录当前加载到了哪一页
    35              page: 1,
    36             onScroll:self._onScroll.bind(this)
    37           }}
    38         />
     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 
    14 }

    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);//发送通知
  • 相关阅读:
    Linux mail 命令使用
    django+nginx+xshell简易日志查询,接上<关于《rsyslog+mysql+loganalyzer搭建日志服务器<个人笔记>》的反思>
    django admin后台提示没有static样式相关的文件
    nginx+uwsgi<django web环境的搭建>
    CentOS 6.5升级Python和安装IPython
    关于《rsyslog+mysql+loganalyzer搭建日志服务器<个人笔记>》的反思
    记一次创建LVM的日志记录
    django TEMPLATES
    Only the sqlmigrate and sqlflush commands can be used when an app has migrations.
    rsyslog+mysql+loganalyzer搭建日志服务器<个人笔记>
  • 原文地址:https://www.cnblogs.com/jackson-yqj/p/9950568.html
Copyright © 2020-2023  润新知