网页版直播和聊天室

作者: zhuguohui | 来源:发表于2016-10-27 14:08 被阅读760次

    序言

    话说上一回,我说到了直播和聊天室,使用的是原生实现的。然而对我来说这太简单了,不足以体现我技术的优越性。下面开启我的装逼之旅。


    这里写图片描述

    效果

    1.截图

    这里写图片描述

    2.视频

    关键看游客模式,登录提醒,跳转登录,发送缓存消息这些功能

    网页聊天室效果

    这里写图片描述

    直播实现

    直播使用的是乐视的标准直播。为什么使用乐视标准直播呢,因为他提供了推流客户端,为什么要使用它的推流客户端呢,首先减少开发成本,其次也是最重要的我将在本文的最后揭晓谜底。

    乐视云地址

    乐视云

    这里进入标准直播

    这里写图片描述

    选择直播活动管理,就可以下载云采集了。

    这里写图片描述

    Android和IOS都支持

    这里写图片描述

    接着大家可以在手机,或者在控制台创建活动

    这里写图片描述

    创建成功就是这样了

    这里写图片描述

    在操作选择分析活动,就可以拿到HTML代码了,然后网页直播的功能就实现了。

    这里写图片描述

    聊天室实现

    聊天室的实现就比较有技术含量了,而基于的SDK是LeanCloud的网页聊天室
    文档在这里实时通信开发指南 · JavaScript

    我遇到的困难有几点

    1.实现游客模式,在用户未登录的情况下可以看到聊天内容

    2.如果用户有聊天,点击发送按钮必须判断用户是否登录,给出提示,并弹到相应的界面。

    3.用户在游客模式下输入的内容,要保存起来,在登录以后发送出去

    4.用户clientID与用户名的处理,clicendID必须唯一,但是用户昵称可以重复。

    这里面涉及到JavaScript和Java的交互,我先把接口提供出来。

        //用于和JavaScript交互的接口
        class Mobile {
            
            //保存聊天信息
            @JavascriptInterface
            public void saveMessage(String msg){
                Log.i("zzz","saveMessage() msg="+msg);
                isNeedSendMessage=true;
                needSendMessage=msg;
            }
    
            //是否需要发送缓存的聊天信息
            @JavascriptInterface
            public boolean isNeedSendMessage(){
             //   Log.i("zzz","isNeedSendMessage() isNeedSendMessage="+isNeedSendMessage);
                return isNeedSendMessage;
            }
            
            //获取缓存的聊天信息
            @JavascriptInterface
            public String getNeedSendMessage(){
             //   Log.i("zzz","getNeedSendMessage()");
                return needSendMessage;
            }
            
            //消失已经发送,重置状态
            @JavascriptInterface
            public void  messageHaveSend(){
            //    Log.i("zzz","messageHaveSend()");
                isNeedSendMessage=false;
                needSendMessage="";
            }
    
            //是否是游客模式
            @JavascriptInterface
            public boolean isTouristMode() {
                return !isLogin();
            }
            
            //获取游客ID
            @JavascriptInterface
            public String getTouristID() {
                return TouristID;
            }
    
            //是否已经登录
            @JavascriptInterface
            public boolean isLogin() {
                return isLogin;
            }
    
            //获取客户端ID
            @JavascriptInterface
            public String getClientID() {
                return clientID;
            }
    
            //弹出警告
            @JavascriptInterface
            public void alert(String msg) {
                Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
            }
    
            //跳转到登录
            @JavascriptInterface
            public void moveToLogin() {
                needLogin = true;
                startActivity(new Intent(MainActivity.this, LoginActivity.class));
            }
        }
    

    这是和聊天相关的JavaScript代码

    //判断是否是游客模式
    if(moblie.isTouristMode()){
        //如果是游客模式的话,获取游客ID并登陆
        initChatroom(moblie.getTouristID());
    }else{
      //如果不是游客模式,则判断是否登录
    if(moblie.isLogin()){
            //如果已经登录,则获取clientID
           initChatroom(moblie.getClientID());
       }else{
            //否则弹出提示
           moblie.alert("请登录");
            //跳转到登录界面
           moblie.moveToLogin();
       }
     }
    
    //初始化聊天室
    function initChatroom(clientID){
    angular.module('realTimeModule', [])
        .controller('realTimeCtrl', ['$scope', '$timeout', function($scope, $timeout) {
            initStatus();
            initData();
    
            function initStatus() {
                $scope.status = {
                    Realtime: AV.Realtime,
                    appId: "jPbaegow8uVUDUDw1XS7LCv0-gzGzoHsz",
                    region: ['cn', 'us'],
                    //用户名ID
                    clientId:clientID,
                    roomId: "57fcac8ea22b9d005b0e9884",
                    client: "",
                    isLinkSuccess: false,
                    isLinkFailed: false,
                    isLinking: false,
                    messageIterator: "",
                    msg: "",
                    room: "",
                    myMsgs: [],
                    members: [],
                    historys: []
                };
            }
    
            function initData() {
                $scope.data = {
                    realtime: new $scope.status.Realtime({
                        appId: $scope.status.appId,
                        region: $scope.status.region[0],
                    }),
                };
                initChatroom();
                setVideoScale();
                setChatroomHeight();
    
            }
    
            /**
             * [initChatroom description] 初始化聊天室
             * @return {[type]} [description]
             */
            function initChatroom() {
                $scope.status.isLinking = true;
                $scope.data.realtime.createIMClient($scope.status.clientId)
                    .then(function(client) {
                        $timeout(function() {
                            $scope.status.isLinkSuccess = true;
                            $scope.status.client = client;
                        }, 500);
                        client.on('disconnect', function() {
                            $scope.status.isLinkFailed = true;
                        });
                        return client.getConversation($scope.status.roomId);
                    })
                    .then(function(conversation) {
                        if (conversation) {
                            return conversation;
                        } else {
                            return $scope.status.client.createConversation({
                                name: "chatRoom",
                                members: [],
                            });
                        }
                    })
                    .then(function(conversation) {
                        $scope.status.roomId = conversation.id;
                        return conversation;
                    })
                    .then(function(conversation) {
                        $scope.status.members = conversation.members;
                        return conversation;
                    })
                    .then(function(conversation) {
                        return conversation.join();
                    })
                    .then(function(conversation) {
                        // 获取聊天历史
                        $scope.status.room = conversation;
                        conversation.queryMessages({
                            limit: 50,
                        }).then(function(messages) {
                        var newMessages=new Array();
                       for(var i=0, len=messages.length; i<len; i++){
                            var message=messages[i];
                           handleName(message);
                           newMessages.unshift(message);
                        }
                            $scope.status.historys = newMessages;
                        });
                        conversation.on('message', function(message) {
                            $timeout(function() {
                               handleName(message);
                                $scope.status.historys.unshift(message);
                            });
                        });
                        //发送游客登录时未发送的消息
                          if(moblie.isNeedSendMessage()){
                                          var msg= moblie.getNeedSendMessage();
                                           $scope.status.msg=msg;
                                               //sendMessage();
                                               sendMsg();
                                               moblie.messageHaveSend();
                                               moblie.alert("评论成功");
                                           }
                    });
            }
    
    
            function handleName(message){
                   var from=message.from;
                     var index=from.indexOf("#");
                          if(index!=-1){
                            from=from.substr(index+1);
                         }
                          message.from=from;
                        //  return message;
            }
            /**
             * [sendMsg description] 发送消息
             * @return {[type]} [description]
             */
            function sendMsg() {
            if(!moblie.isLogin()){
                  moblie.saveMessage($scope.status.msg);
                  moblie.alert("请登录");
                  moblie.moveToLogin();
                  return;
             }
                $scope.status.room.send(new AV.TextMessage($scope.status.msg)).then(function(message) {
                    $timeout(function() {
                        message.from="自己";
                        $scope.status.historys.unshift(message);
                    });
                    $scope.status.msg = '';
                });
            }
    
            /**
             * [linkToServer description] 连接到服务器
             * @return {[type]} [description]
             */
            $scope.linkToServer = function() {
                initChatroom();
            };
    
            /**
             * [sendMessage description] 发送消息
             * @return {[type]} [description]
             */
            $scope.sendMessage = function(e) {
                if ((angular.isDefined(e) && e.keyCode == 13) || angular.isUndefined(e)) {
                    sendMsg();
                }
            };
    
            /**
             * [setVideoScale description] 根据16/9的比例设置视频高度
             */
            function setVideoScale() {
                var cw = document.documentElement.clientWidth;
                var scale = 16 / 9;
                var vh = cw / scale;
                var video = document.getElementById("player");
                video.style.height = vh + "px";
            }
    
            /**
             * [setChatroomHeight description] 设置聊天室的高度(为了移动端一屏展示)
             */
            function setChatroomHeight() {
                var video = document.getElementById("player");
                var chatroomHeader = document.getElementById("chatroomHeader");
                var chatroom = document.getElementById("chatroom");
                var sendBox = document.getElementById("sendBox");
                var ch = document.documentElement.clientHeight;
                var vh = getStyle(video).height;
                var crheader = getStyle(chatroomHeader).height;
                var sbh = getStyle(sendBox).height;
                var crh = parseInt(ch) - parseInt(vh) - parseInt(crheader) - parseInt(sbh);
                if (crh < 1) {
                    crh = crh + 500;
                }
                console.log(crh);
                chatroom.style.height = crh + "px";
                chatroom.style.marginBottom = sbh;
    
            }
    
            function getStyle(ele) {
                var style = null;
                if (window.getComputedStyle) {
                    style = window.getComputedStyle(ele, null);
                } else {
                    style = ele.currentStyle;
                }
                return style;
            }
    
            window.onresize = function() {
                setVideoScale();
                setChatroomHeight();
            };
    
    
        }]);
      }
    
    

    1.游客模式

    实现游客模式的逻辑如下,这是聊天室相关的JavaScript代码。

    这里写图片描述

    而游客ID的获取如下,通过拼接两个随机数来实现。

    这里写图片描述

    2.登录判断

    看关键的JS代码,主要就是在发送消息的时候判断登录状态。

     /**
             * [sendMsg description] 发送消息
             * @return {[type]} [description]
             */
            function sendMsg() {
            //判断是否登录
            if(!moblie.isLogin()){
                   //未登录,则保存输入框中的消息
                  moblie.saveMessage($scope.status.msg);
                  //弹出提示
                  moblie.alert("请登录");
                  //跳转到登录界面
                  moblie.moveToLogin();
                  return;
             }
                $scope.status.room.send(new AV.TextMessage($scope.status.msg)).then(function(message) {
                    $timeout(function() {
                        message.from="自己";
                        $scope.status.historys.unshift(message);
                    });
                    $scope.status.msg = '';
                });
            }
    

    如果需要登录则跳转到登录界面

       //跳转到登录
            @JavascriptInterface
            public void moveToLogin() {
                //表示需要登录
                needLogin = true;
                startActivity(new Intent(MainActivity.this, LoginActivity.class));
            }
    

    LoginActivity如下

    public class LoginActivity extends AppCompatActivity {
    
        EditText et_name;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_login);
            et_name = (EditText) findViewById(R.id.et_name);
        }
    
        public void login(View view) {
            String name = et_name.getText().toString();
            MainActivity.isLogin = true;
            MainActivity.clientID = name;
            Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
            finish();
        }
    }
    

    回来以后在聊天室界面的onResume方法中判断是否需要登录,如果需要通过 webView.reload()重新加载网页。

        @Override
        protected void onResume() {
            super.onResume();
            if (needLogin) {
                webView.reload();
                needLogin = false;
            }
        }
    

    网页每次加载都会执行这个方法,这里就会进入到登录的逻辑了。

    //判断是否是游客模式
    if(moblie.isTouristMode()){
        //如果是游客模式的话,获取游客ID并登陆
        initChatroom(moblie.getTouristID());
    }else{
      //如果不是游客模式,则判断是否登录
    if(moblie.isLogin()){
            //如果已经登录,则获取clientID
           initChatroom(moblie.getClientID());
       }else{
            //否则弹出提示
           moblie.alert("请登录");
            //跳转到登录界面
           moblie.moveToLogin();
       }
     }
    
    

    3.保存未发送信息

    1.在发送消息的时候,如果没有登录,则通过Java接口保存消息

     /**
             * [sendMsg description] 发送消息
             * @return {[type]} [description]
             */
            function sendMsg() {
            //判断是否登录
            if(!moblie.isLogin()){
                   //未登录,则保存输入框中的消息
                  moblie.saveMessage($scope.status.msg);
                  //弹出提示
                  moblie.alert("请登录");
                  //跳转到登录界面
                  moblie.moveToLogin();
                  return;
             }
                $scope.status.room.send(new AV.TextMessage($scope.status.msg)).then(function(message) {
                    $timeout(function() {
                        message.from="自己";
                        $scope.status.historys.unshift(message);
                    });
                    $scope.status.msg = '';
                });
            }
    

    在登录聊天室成功以后,发送保存的信息

                          //发送游客登录时未发送的消息
                          if(moblie.isNeedSendMessage()){
                                  //获取保存的消息
                                 var msg= moblie.getNeedSendMessage();
                                 $scope.status.msg=msg;
                                 //发送消息
                                 sendMsg();
                                 //重置状态
                                 moblie.messageHaveSend();
                                  //提示用户
                                  moblie.alert("评论成功");
                           }
    

    4.用户ID

    这个处理我用在实际的项目中了,这Demo没有,大致的说一样原理
    为了实现ClientID唯一,我选择了使用LeanCloud的ObjectID做ClientID但是现实的效果是
    一大堆的数字字母串,聊天的时候就这这样了,没有显示用户昵称,效果和差。

    aojogo80900nfjf3883949:我是李四
    oiigjiooiijglle9939948:我是张三,你的名字好奇怪
    

    于是我使用了ObjectID:昵称 这种形式。比如

    aogoijsojgoij5651211sdjk:李四
    

    在获取到消息以后就只截取后面的昵称用来显示

    李四:我是李四
    张三:我是张三
    

    效果很不错,而且解决了唯一性和易用性的问题,我在真实项目中已经实现了,所以大家也不要怀疑是实用性了。

    源码

    结构

    realtime.html是聊天室界面
    realtime.js是聊天室逻辑

    这里写图片描述

    大家只需要替换,AppID,和roomID为你们自己的就可以用在自己的项目中了

    关于弹幕,在获取到消息以后实用JavaScript以弹幕的方式显示就行了。

    这里写图片描述

    下载

    下载地址

    总结

    这个项目主要的难度是考察了我Java和JavaScript的交互能力,和我的JavaScript的水平。realtime.js中的所有和客户端交互的逻辑都是我自己写的,这里需要重点表扬一下。还有一点可以说明一下,通过这种方式,我们的IOS也可以实现一样的效果,所有小伙伴们不要害怕,如果你们的IOS端做不出来,只能说明水平太菜,一个小插曲就是我的Mobile这个单词写错了,已改是mobile,不过无关大雅。然后说说我们会什么要使用网页实现直播和聊天室,为什么我要在这儿装逼,全都是被逼的,在上一篇博客结束以后,我们告诉客户我们准备用百度云SDK,客户说:”什么!我们乐视云都已经付费了,必须用乐视“。在上一篇文章我就说过乐视云根本不能用,客户不信,于是就有了下面这个。

    这里写图片描述

    还有这个

    这里写图片描述

    由于移动直播根本不能用,于是改用标准直播,还有他们官方的推流工具,但是官方的推流工具在Android端依然有兼容性问题。

    奉劝大家,真心不要用乐视,这是我曾经的一些坑,如果能帮到你,那我这个逼也就没有白装了。

    相关文章

      网友评论

      • DTC123:嗯。很好!成功的装了一会b。哈哈!!学习了:sunglasses:
        zhuguohui: @DTC007 😂

      本文标题:网页版直播和聊天室

      本文链接:https://www.haomeiwen.com/subject/evtsuttx.html