前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android UDP协议,实现Yodar遥控器

Android UDP协议,实现Yodar遥控器

作者头像
饮水思源为名
发布2019-06-15 14:23:53
6460
发布2019-06-15 14:23:53
举报

近期做了一个Yodar的音乐播放器,其实就是在Android上做了一个Yodar的遥控器,用了播放器的样式与Yodar的设备屏幕同步。而比较麻烦得是Yodar的协议是UDP的。并且涉及到发json数据,不能有丝毫错误,必须按规则来,否则不会返回任何信息。接下来,笔者就简单描述下制作过程中碰到的一些问题。还有,音乐播放器的源码属于公司产品项目,所以笔者上传的源码是测试写的API源码,当然与实际项目比较,也就差了一个界面和交互而已,核心的都在。

UDP的发送和接收

public class UDPUtil {
    private static UDPUtil udpUtil;
    DatagramSocket ds;
    private final int SENDPORT=10061;
    private final int REVCIVEPORT=10062;
    private final int SOCKETTIMEOUT=1000;
    private final String IP="255.255.255.255";
    private OnResolveDataListener onResolveDataListener;
    private UDPUtil() throws IOException {
        init();
        startUDPService();
    }
    private void init() throws IOException {
        ds= new DatagramSocket(REVCIVEPORT);
        ds.setBroadcast(true);
//        ds.setSoTimeout(SOCKETTIMEOUT);//设置超时时间
    }
    public synchronized static UDPUtil  getInstance() throws IOException {
        if (udpUtil==null) udpUtil=new UDPUtil();
        return udpUtil;
    }
    public void sendUDP(final byte[] sendBuf){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    DatagramPacket sendDp = new DatagramPacket(sendBuf, sendBuf.length, InetAddress.getByName(IP), SENDPORT);
                    ds.send(sendDp);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    public void startUDPService(){
        if(ds==null)return;
        Log.i("wsy","UDP Service star");
        new Thread(new Runnable() {
            @Override
            public void run() {
                byte[] receiveBuf = new byte[1024];
                DatagramPacket receiveDp = new DatagramPacket(receiveBuf, receiveBuf.length);
                while (true){
                    try {
                        ds.receive(receiveDp); // 该方法为阻塞式的方法
                        String ip = receiveDp.getAddress().getHostAddress();
                        int port = receiveDp.getPort();
                        byte[] text = receiveDp.getData();
//                        for (int i=0;i<receiveDp.getLength();i++) {
//                            Log.d("wsy","接收到第"+i+"byte的值="+ text[i]);
//                        }
                        Log.d("wsy", ip + ":" + port + ":" + byteToString(text, 0, receiveDp.getLength()));
                        if(onResolveDataListener!=null) onResolveDataListener.resolveData(text,receiveDp.getLength());
                    } catch (IOException e) {
                        e.printStackTrace();
                        Log.e("wsy","UDP Service error end");
                    }
                }
            }
        }).start();

    }
    /**
     * 将Byte转为String(按照ASCLL码表将10进制的byte值转成字符)
     * @param b
     * @param start
     * @param len
     * @return
     */
    public String byteToString(byte[] b,int start,int len){
        try {
            byte[] bytes=new byte[len];
            for (int i=start;i<start+len;i++){
                bytes[(i-start)]=b[i];
            }
            return new String(bytes,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "转化出错";
    }
    public interface OnResolveDataListener{
        void resolveData(byte[] bytes,int len);
    }

    public void setOnResolveDataListener(OnResolveDataListener onResolveDataListener) {
        this.onResolveDataListener = onResolveDataListener;
    }
}

这方面知识点文章很多,直接上代码,笔者做了一个单例类来管理这个UDPUtil,实现了UDP的发送和接收,以及byte和String的转化方法。默认广播IP“255.255.255.255”,发送端口10061(YodarUDP协议监听的这个端口),接收端口10062(不是10061就好,否则会接收到自己发的UDP包)

编写API,发送与接收

首先做这一步,得阅读Yodar的协议文档。发送则按照要求构建btye[]数据。下面是获取主机信息的例子

 public void getHostInfo() {
        byte[] sendBuf = new byte[3];
        sendBuf[0] = (byte) 0xce;
        sendBuf[1] = 0x00;
        sendBuf[2] = (byte) (0xce ^ 0x00);
        udpUtil.sendUDP(sendBuf);
    }

接收的话,笔者通过协议的信息对不同的数据做了判断,截取获取到的JSON数据,构建对应的Bean类,然后通过广播发送给监听了对应Action的使用者。 这么做的目的是,UDP获取数据的时间不是我们控制的,也不是一定是发一条就接收一条,并且在Yodar切换歌曲等的时候,发主动发送UDP包给我们,我们需要一直去接收,自己判断属于什么数据,然后分发。

    public void resolveData(byte[] bytes,int len) {
        Map<String, Object> maps=new HashMap<>();
        Intent intent=new Intent();
        intent.setAction(BROADCASTMESSAGE);
        switch (bytes[0]){
            case (byte) 0xef://主机信息返回
                maps.put("Command",bytes[0]);
                maps.put("Address",bytes[5]);
                maps.put("Model",bytes[4]);//通道数
                maps.put("iphoneId",bytes[5]);//编号
                maps.put("NameId",bytes[6]);
                maps.put("Name",udpUtil.byteToString(bytes,8,bytes[7]));
                hostInfo=maps;
                intent.putExtra("bean",gson.fromJson(gson.toJson(maps), HostInfoBean.class));
                break;
            case (byte)0x0f:
                String josnStr=udpUtil.byteToString(bytes,4,len-5);
                JSONObject jsonObject=null;
                String ack="";
                String notify="";
                JSONObject arg=null;
                try {
                    jsonObject=new JSONObject(josnStr);
                    try{
                        ack=jsonObject.getString("ack");
                    }catch (JSONException e){ }
                    try{
                        notify=jsonObject.getString("notify");
                    }catch (JSONException e){ }
                    try{
                        arg=jsonObject.getJSONObject("arg");
                    }catch (JSONException e){ }
                    if(!ack.equals("")){
                        switch (ack){
                            case "player.info"://歌曲信息---请求的
                                processMusicInfo(arg.toString(),intent);
                                break;
                            case "list.dirNodeList":
                                TableBean tableBean=new Gson().fromJson(josnStr, TableBean.class);
                                intent.putExtra("bean",tableBean);
                                break;
                        }
                    }
                    if(!notify.equals("")){
                        switch (notify){
                            case "player.info"://歌曲信息---通知的
                                processMusicInfo(arg.toString(),intent);
                                break;
                            case "player.time":
                                TimeNotifyBean timeNotifyBean=new Gson().fromJson(arg.toString(), TimeNotifyBean.class);
                                intent.putExtra("bean",timeNotifyBean);
                                break;
                            case "player.state":
                                StateNotifyBean stateNotifyBean=new Gson().fromJson(arg.toString(), StateNotifyBean.class);
                                intent.putExtra("bean",stateNotifyBean);
                                break;
                            case "player.mode":
                                ModeNotifyBean modeNotifyBean=new Gson().fromJson(arg.toString(), ModeNotifyBean.class);
                                intent.putExtra("bean",modeNotifyBean);
                                break;
                            case "player.volume":
                                VolumeNotifyBean volumeNotifyBean=new Gson().fromJson(arg.toString(), VolumeNotifyBean.class);
                                intent.putExtra("bean",volumeNotifyBean);
                                break;
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return;
                }
                break;
        }
        mC.sendBroadcast(intent);
    }

几分钟不操作之后,发送的非主机获取UDP包,Yodar不再响应问题

笔者做Android版本的Yodar遥控器时碰到了这个问题,据IOS说没有这个问题。笔者只好20S间隔一次,去给Yodar发送一个获取主机信息的UDP包,就不会出现Yodar不响应的问题。感觉就像发心跳包,需要一直保持联系。笔者也没搞明白为什么,这UDP协议按道理是不需要这样的牵手的。

源码

Yodar遥控器测试项目源码 YodarUDP协议在源码根目录中!!!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.06.11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • UDP的发送和接收
  • 编写API,发送与接收
  • 几分钟不操作之后,发送的非主机获取UDP包,Yodar不再响应问题
  • 源码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档