音樂播放APP初步開發(一)websocket間的通訊

項目app頁面的開發採用mui框架來實現,支持模擬器和移動端的運行,真機運行會提示安裝hbuilder基座調試程序。彷佛開發狀況下只能再同一個網段下測試運行,跨網段就沒法鏈接到資源數據。javascript

若是是將項目部署到真機上去調試能夠在以下路徑去找項目文件:css

內存>android>data>io.dcloud.HBuilder>apps>wwwhtml

也能夠自定義部署資源的路徑 前端

:_doc/audio/   就是指:內存>android>data>io.dcloud.HBuilder>apps>doc>audio>文件資源名java

窗口頁面採用HTML5來顯示。android

index.html爲應用首頁,通常只須要實現底部選項卡便可,其餘功能能夠在主頁面或其餘頁面去完善:web

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <link rel="stylesheet" type="text/css" href="css/mui.min.css"/>
</head>
<body>
    
    <!--底部選項卡-->
    <nav class="mui-bar mui-bar-tab">
        <a class="mui-tab-item mui-active">
            <span class="mui-icon mui-icon-home"></span>
            <span class="mui-tab-label">首頁</span>
        </a>
        <a class="mui-tab-item">
            <span class="mui-icon mui-icon-phone"></span>
            <span class="mui-tab-label">電話</span>
        </a>
        <a class="mui-tab-item">
            <span class="mui-icon mui-icon-email"></span>
            <span class="mui-tab-label">郵件</span>
        </a>
        <a class="mui-tab-item">
            <span class="mui-icon mui-icon-gear"></span>
            <span class="mui-tab-label">設置</span>
        </a>
    </nav>
    
    <script src="js/mui.min.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript"> mui.init({ subpages: [{ url: "main.html", id: "main.html", styles: { top:'0px', //mui標題欄默認高度爲45px;
 bottom:'50px' //默認爲0px,可不定義;
 } }] }); var ws=new WebSocket("ws://192.168.1.114:3721/app/app01"); document.addEventListener("send_music",function (data) { var send_str=data.detail //{to_user:"toy123",music:"sdata.album_name"}
 ws.send(JSON.stringify(send_str)); }) </script>
</body>
</html>
index.html代碼

main.html是應用的主頁面,實現了數據的列表展現:數據庫

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <link rel="stylesheet" type="text/css" href="css/mui.min.css"/>
    <script src="js/mui.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
    <!--標題-->
    <header class="mui-bar mui-bar-nav">
        <h1 class="mui-title">音樂</h1>
    </header>
    <!--圖片輪播-->
    <div class="mui-content">
        <div id="slider" class="mui-slider" >
          <div class="mui-slider-group mui-slider-loop" style="height:300px; width: auto;">
            <!-- 額外增長的一個節點(循環輪播:第一個節點是最後一張輪播) -->
            <div class="mui-slider-item mui-slider-item-duplicate">
              <a href="#">
                <img src="http://192.168.1.114:9527/get_lb/1.jpg">
              </a>
            </div>
            <!-- 第一張 -->
            <div class="mui-slider-item">
              <a href="#">
                <img src="http://192.168.1.114:9527/get_lb/1.jpg">
              </a>
            </div>
            <!-- 第二張 -->
            <div class="mui-slider-item">
              <a href="#">
                <img src="http://192.168.1.114:9527/get_lb/2.jpg">
              </a>
            </div>
            <!-- 第三張 -->
            <div class="mui-slider-item">
              <a href="#">
                <img src="http://192.168.1.114:9527/get_lb/3.jpg">
              </a>
            </div>
            <!-- 第四張 -->
            <div class="mui-slider-item">
              <a href="#">
                <img src="http://192.168.1.114:9527/get_lb/4.jpg">
              </a>
            </div>
            <!-- 額外增長的一個節點(循環輪播:最後一個節點是第一張輪播) -->
            <div class="mui-slider-item mui-slider-item-duplicate">
              <a href="#">
                <img src="http://192.168.1.114:9527/get_lb/1.jpg">
              </a>
            </div>
          </div>
          <div class="mui-slider-indicator">
            <div class="mui-indicator mui-active"></div>
            <div class="mui-indicator"></div>
            <div class="mui-indicator"></div>
            <div class="mui-indicator"></div>
          </div>
        </div>
    </div>
    
     <!--圖文列表-->
    <ul class="mui-table-view" id="con_list">
        
    </ul>
    
    
    <script type="text/javascript"> mui.init() mui.plusReady(function () { mui.post(window.serv+'/get_list',{ },function(data){ for (var i = 0; i < data.data.length; i++) { creat_item(data.data[i]) } },'json' ); }) function creat_item (content) { var li=document.createElement("li") li.className="mui-table-view-cell mui-media"; var a=document.createElement("a") a.onclick=function () { mui.openWindow({ url:"player.html", id:"player.html", extras:content }) } var img=document.createElement("img") img.className="mui-media-object mui-pull-left"; img.src=window.serv_img+content.music_img var div=document.createElement("div") div.className="mui-media-body" div.innerText=content.album_name var p=document.createElement("p") p.className="mui-ellipsis" p.innerText=content.author_name li.appendChild(a) a.appendChild(img) a.appendChild(div) div.appendChild(p) document.getElementById("con_list").appendChild(li) } </script>
</body>
</html>
main.html代碼

player.html歌曲的播放頁面,並實現websocket鏈接遠程控制播放曲目:json

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <link rel="stylesheet" type="text/css" href="css/mui.min.css"/>
    <style type="text/css"> @-webkit-keyframes rotation { from { -webkit-transform: rotate(0deg);
        } to { -webkit-transform: rotate(360deg);
        } } .an { -webkit-transform: rotate(360deg); animation: rotation 3s linear infinite; -moz-animation: rotation 3s linear infinite; -webkit-animation: rotation 3s linear infinite; -o-animation: rotation 3s linear infinite;
    }
    </style>
</head>
<body>
    <header class="mui-bar mui-bar-nav">
        <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
        <h1 class="mui-title" id="title">正在播放</h1>
    </header>
    <div class="mui-content" style="text-align:center; margin-top: 20px;">
        <img class="an" src="" id="cover"/ style="height:150px; width:150px; border-radius:50%;">
    </div>
    
       <button type="button" id="zt" class="mui-btn mui-btn-yellow mui-btn-block">暫停</button>
    <button type="button" id="bf" class="mui-btn mui-btn-green mui-btn-block">播放</button>
    <button type="button" id="tz" class="mui-btn mui-btn-red mui-btn-block">中止</button>
    <button type="button" id="fs" class="mui-btn mui-btn-blue mui-btn-block">發送</button>
    
    
    <script src="js/mui.min.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript"> mui.init() var sdata=null; var myplayer=null mui.plusReady(function () { sdata=plus.webview.currentWebview() document.getElementById("title").innerText="正在播放 - "+sdata.album_name document.getElementById("cover").src=window.serv_img+sdata.music_img myplayer=plus.audio.createPlayer(window.serv_music+sdata.play_url) myplayer.play() }) document.getElementById('zt').addEventListener('tap',function () { myplayer.pause() }) document.getElementById('bf').addEventListener('tap',function () { myplayer.resume() }) document.getElementById('tz').addEventListener('tap',function () { myplayer.stop() }) document.getElementById('fs').addEventListener('tap',function () { var index=plus.webview.getWebviewById("HBuilder"); mui.fire(index,'send_music',{to_user:"toy123",music:sdata.play_url}); }) </script>
</body>
</html>
player.html代碼

爲了不hbuilder中的js頻繁的給後端發送AJAX,更換ip,能夠將ip設置爲所有變量,在引用文件的js文件添加代碼以下:flask

window.serv="http://192.168.1.114:9527";
window.serv_img=window.serv+"/get_photo/";
window.serv_music=window.serv+"/get_music/";

H5頁面實現首頁加載子頁面main.html :

mui.init({ subpages: [{ url: "main.html", id: "main.html", styles: { top:'0px', //mui標題欄默認高度爲45px;
            bottom:'50px' //默認爲0px,可不定義;爲了避免遮住index底部選項卡
 } }] });

圖文列表的數據展現:

在不肯定記錄條數的狀況下,會經過查詢數據庫,對數據迭代,去顯示數據列表,因此在body主體裏面只會有一個空列表元素:

<!--圖文列表-->
    <ul class="mui-table-view" id="con_list">
        
    </ul>

經過JavaScript給後端發送post請求,去獲取記錄數,並迭代生成列表元素:

mui.init() mui.plusReady(function () { mui.post(window.serv+'/get_list',{ },function(data){ for (var i = 0; i < data.data.length; i++) {  creat_item(data.data[i]) } },'json' ); })      #window.serv指向js文件中配置好的ip地址

返回的數據以字典形式推送給前端JavaScript ,迭代數據傳遞給creat_item去生成列表元素。

function creat_item (content) { var li=document.createElement("li") li.className="mui-table-view-cell mui-media"; var a=document.createElement("a")  a.onclick=function () { mui.openWindow({ url:"player.html", id:"player.html", extras:content }) } var img=document.createElement("img") img.className="mui-media-object mui-pull-left"; img.src=window.serv_img+content.music_img        #去後臺請求對應圖片信息數據 var div=document.createElement("div") div.className="mui-media-body" div.innerText=content.album_name var p=document.createElement("p") p.className="mui-ellipsis" p.innerText=content.author_name li.appendChild(a) a.appendChild(img) a.appendChild(div) div.appendChild(p) document.getElementById("con_list").appendChild(li) }

後端會有路由去處理這個請求:

在實際項目中,爲了便於擴展,一般會採用藍圖註冊到app的方式去處理請求。處理請求文件放到serv目錄下。

獲取數據列表:

from flask import Blueprint,jsonify from setting import mongoDB,RET content=Blueprint("content",__name__) @content.route('/get_list',methods=["POST",]) def get_list(): music=list(mongoDB.music.find({})) for index,item in enumerate(music): music[index]["_id"]=str(item.get("_id")) RET["code"]=0 RET["msg"]="查詢列表數據" RET["data"]=music return jsonify(RET)

列表數據顯示完成,點擊列表,實現跳轉播放頁面player.html :

a.onclick=function () { mui.openWindow({ url:"player.html", id:"player.html", extras:content }) }

將字典數據傳遞給新頁面player.html,在新頁面獲取傳遞過來的數據字典:

並獲取播放曲目圖片和歌曲名,建立一個播放對象來播放歌曲

 mui.init() var sdata=null; var myplayer=null mui.plusReady(function () { sdata=plus.webview.currentWebview() document.getElementById("title").innerText="正在播放 - "+sdata.album_name document.getElementById("cover").src=window.serv_img+sdata.music_img myplayer=plus.audio.createPlayer(window.serv_music+sdata.play_url)        #去後臺請求音樂數據 myplayer.play() })

至此已經能完成圖文列表正常顯示數據,點擊列表,打開播放頁面,播放歌曲。效果如圖所示:

關於圖片的旋轉能夠經過設置img的樣式來實現:

<div class="mui-content" style="text-align:center; margin-top: 20px;">
        <img class="an" src="" id="cover"/ style="height:150px; width:150px; border-radius:50%;">
    </div>

style樣式設置:

<style type="text/css"> @-webkit-keyframes rotation { from { -webkit-transform: rotate(0deg); } to { -webkit-transform: rotate(360deg); } } .an { -webkit-transform: rotate(360deg); animation: rotation 3s linear infinite; -moz-animation: rotation 3s linear infinite; -webkit-animation: rotation 3s linear infinite; -o-animation: rotation 3s linear infinite; } </style>

 進一步實現經過websocket遠程通訊,實現指定客戶端播放app中指定的音樂:

即:點擊app中的發送按鈕,遠程的客戶端(玩具)會切換到當前播放歌曲。來播放新的音樂

監聽「發送按鈕」事件,將數據傳遞至index頁面,並建立websocket對象,經過websocket對象將數據發送給後端:

<button type="button" id="fs" class="mui-btn mui-btn-blue mui-btn-block">發送</button>

在JavaScript中監聽:

document.getElementById('fs').addEventListener('tap',function () {
            var index=plus.webview.getWebviewById("HBuilder");
            mui.fire(index,'send_music',{to_user:"toy123",music:sdata.play_url});
    })

在index中去監聽send_music事件:並獲取攜帶的數據

var ws=new WebSocket("ws://192.168.1.114:3721/app/app01");
    document.addEventListener("send_music",function (data) {
        var send_str=data.detail    //{to_user:"toy123",music:"sdata.album_name"}
        ws.send(JSON.stringify(send_str));
    })

運行程序就建立app的websocket對象,此時,後端會經過路由去獲取app_id和app的websocket對象,保存在空字典裏,準備後續的使用。

後端websocket的代碼實現:

from flask import Flask,request
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
from geventwebsocket.websocket import WebSocket
import json

ws_app=Flask(__name__)

user_socket_dict={}

@ws_app.route("/app/<app_id>")
def app(app_id):
    user_socket= request.environ.get("wsgi.websocket")  #type:WebSocket
    if user_socket:
        user_socket_dict[app_id]=user_socket  # {'app01': <geventwebsocket.websocket.WebSocket object at 0x0000022989215250>}
    print(user_socket_dict)

    while 1:
        user_msg=user_socket.receive()      # {"to_user":"toy123","music":"8155d294-0552-460d-b5f5-57afddbfdfd4.mp3"} <class 'str'>

        msg_dict=json.loads(user_msg)

        toy_socket=user_socket_dict.get(msg_dict.get("to_user"))    #獲取當前客戶端的websocket對象
        toy_socket.send(user_msg)                      #給對象發送以前的數據,必須是str類型 不然報錯 

@ws_app.route("/toy/<toy_id>")
def toy(toy_id):
    user_socket=request.environ.get("wsgi.websocket")   # type: WebSocket
    if user_socket:

        user_socket_dict[toy_id]=user_socket  # 'toy123': <geventwebsocket.websocket.WebSocket object at 0x00000229892152B8>
    print(user_socket_dict)

    while 1:
        user_msg=user_socket.receive()
        print(user_msg)

if __name__ == '__main__':
    http_serv=WSGIServer(("0.0.0.0",3721),ws_app,handler_class=WebSocketHandler)
    http_serv.serve_forever()

app的websocket邏輯代碼寫完後,還須要建立一個客戶端toy的websocket對象,一樣會存儲到字典裏去

toy頁面代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>玩具</title>
</head>
<body>
<audio autoplay="autoplay" controls id="player"></audio>

<script type="application/javascript">
    var addr="192.168.1.114";
    var ws=new WebSocket("ws://"+addr+":3721/toy/toy123");
    ws.onmessage=function (data) {
        console.log(data.data);
        var msg=JSON.parse(data.data);
        document.getElementById("player").src="http://"+addr+":9527/get_music/"+msg.music;

    }
</script>
</body>
</html>

添加訪問toy客戶端的路由地址到player.py flask框架

@app.route("/")
def toy():
    return render_template("toy.html")

基本完成該階段的邏輯代碼,項目運行流程:

1.啓動player.py 運行flask框架,啓動服務器。

2.運行腳本文件play_ws.py 啓動websocket服務

3.訪問toy客戶端頁面,將toy的toy_id和websocket對象傳遞給後端

4.啓用app項目,將app_id和app的websocket對象傳遞給後端。

5.後端的websocket中會將數據保存在字典中

{'toy123': <geventwebsocket.websocket.WebSocket object at 0x00000293E4655250>, 'app01': <geventwebsocket.websocket.WebSocket object at 0x00000293E46552B8>}

6.點開圖文列表,音樂開始播放,點擊發送,會將當前音樂的歌名和toy_id以字典方式發送給index.html頁面

7.index.html頁面獲取數據,並將字典對象轉化爲str,發送給後端app路由去處理

8.後端收到數據,反序列化,獲得內容,按照收件人的id去字典中獲取到這個websocket對象,給這個對象發送收到的數據 未反序列化的str字典

9.toy獲取數據字典中music的值,並讀取到audio中播放該曲目

toy收到app經過websocket發過來的代碼:

相關文章
相關標籤/搜索