美文网首页
Android 安卓Bonjour(NSD服务) 客户端基本使用

Android 安卓Bonjour(NSD服务) 客户端基本使用

作者: 微风_10a5 | 来源:发表于2021-09-17 16:20 被阅读0次

对Bonjour完全小白的同学,推荐一篇文章iOS开发 Bonjour的使用, 在前一篇文章本人也分享了在iOS中如何使用Bonjour,请前往iOS Bonjour 基本使用及完美解决各种坑~

此篇文章完全是从零基础的角度出发,详细地记录bonjour使用过程,希望能帮助到您

好了,下面回到正题,从新建项目开始
第一步,在Android Studio新建一个空activity项目

如下图


image.png image.png

后面就是给项目取一个名称,然后构建项目了


image.png
第二步,增加一个网络权限

在安卓中,要使用Bonjour,是使用NSD服务的,在使用NSD服务之前需要申请网络权限. 因为要在局域网去发现设备,用到了网络.
/项目名/app/src/main/AndroidManifest.xml中增加权限, 增加权限代码需要放在<application />标签的前面;如下图:

image.png
第三步,完整代码及运行后的结果
package com.example.androidforbonjour;

import androidx.appcompat.app.AppCompatActivity;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private static  NsdManager nsdManager;
    private static String SERVICE_TYPE = "_x5_gw._tcp";
    private NsdManager.DiscoveryListener mDiscoveryListener;
    private NsdManager.ResolveListener mResolverListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        nsdManager = (NsdManager) getSystemService(NSD_SERVICE);
        createDiscoverListener();
    }

    private void createDiscoverListener() {
        mResolverListener = new NsdManager.ResolveListener() {
            @Override
            public void onResolveFailed(NsdServiceInfo nsdServiceInfo, int i) {
                Toast.makeText(MainActivity.this, "onResolveFailed", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onServiceResolved(NsdServiceInfo nsdServiceInfo) {
//                mNsdServiceInfo = nsdServiceInfo;
                Toast.makeText(MainActivity.this, "onServiceResolved", Toast.LENGTH_SHORT).show();
                Log.d("xm_owon--------",nsdServiceInfo.toString());

            }
        };


        mDiscoveryListener = new NsdManager.DiscoveryListener() {
            private NsdServiceInfo mmNsdServiceInfo;

            @Override
            public void onStartDiscoveryFailed(String s, int i) {
                Toast.makeText(MainActivity.this, "onStartDiscoveryFailed", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onStopDiscoveryFailed(String s, int i) {
                Toast.makeText(MainActivity.this, "onStopDiscoveryFailed", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDiscoveryStarted(String s) {
                Toast.makeText(MainActivity.this, "onDiscoveryStarted", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDiscoveryStopped(String s) {
                Toast.makeText(MainActivity.this, "onDiscoveryStopped", Toast.LENGTH_SHORT).show();
            }


            @Override
            public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
                mmNsdServiceInfo = nsdServiceInfo;
                //这里的nsdServiceInfo只能获取到名字,ip和端口都不能获取到,要想获取到需要调用NsdManager.resolveService方法
                Toast.makeText(MainActivity.this, "onServiceFound", Toast.LENGTH_SHORT).show();


                nsdManager.resolveService(mmNsdServiceInfo, mResolverListener);
            }

            @Override
            public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
                Toast.makeText(MainActivity.this, "onServiceLost", Toast.LENGTH_SHORT).show();
            }
        };


        nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);

    }
}

运行的结果如下, 一旦找到了局域网中设备的ip地址与port端口, 后面就是使用它们,建立起连接,来与设备进行通讯了.


image.png
第四步,说明一下使用过程

1)第一步,初始化NsdManager

image.png

2)第二步,在局域网中去发现服务,在mDiscoveryListener方法中,去监听已发现的服务了

 nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);

3)mDiscoveryListener中方法,有好几个,其中onServiceFound这个方法里面就是找到服务了, 后面就是根据服务去解析设备信息出来

            @Override
            public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
                mmNsdServiceInfo = nsdServiceInfo;
                //这里的nsdServiceInfo只能获取到名字,ip和端口都不能获取到,要想获取到需要调用NsdManager.resolveService方法
                Toast.makeText(MainActivity.this, "onServiceFound", Toast.LENGTH_SHORT).show();
                
                nsdManager.resolveService(mmNsdServiceInfo, mResolverListener);
            }

4),第三步,就是根据已经找到的服务去解析设备信息出来,具体代码如下:

nsdManager.resolveService(mmNsdServiceInfo, mResolverListener);

5)第四步,在mResolverListener,方法里面监听设备信息,这样就到达了在局域网中发现设备了.

            @Override
            public void onServiceResolved(NsdServiceInfo nsdServiceInfo) {
//                mNsdServiceInfo = nsdServiceInfo;
                Toast.makeText(MainActivity.this, "onServiceResolved", Toast.LENGTH_SHORT).show();
                Log.d("xm_owon--------",nsdServiceInfo.toString());

            }

结尾

为什么要用Bonjour呢,因为如果不用Bonjour的话,很多公司就会用到udp来做发现设备,但在实际使用中,会有概率性的找不到设备,也就是说有时候发现不了设备,找不到设备就没有办法与设备进行通讯.

好了,今天的分享就到这里了,看到这里的小伙们,或者觉得文章对你有点帮助的话,请点赞加关注喽,您的反馈就是我们前进的动力。后续会分享更多关于移动端的干货。谢谢~~

以下是更新

填坑记录

上面的代码,勉强是可以用的,不过要是反复测试的话,可能会有下面的报错信息

java.lang.IllegalArgumentException: listener already in use at android.net.nsd.NsdManager.resolveService(NsdManager.java:613) at com.example.miguel.broadcast.LocalService$2.onServiceFound(LocalService.java:145)

对,就是上面的错误,后面在StackOverFlow上找到了答案:
解决思路,mResolverListener 这个不要用全局变量,把它变为局部变量,每次都去创建它,就不会报上面的错了。下面本人封装了一个工具类,也解决了此问题。工具类使用起来 更爽,更方便。

进阶 工具类FindBonjourTool

第一步:定义一个接口BonjourInfoCallBack,用作回调,方便Activity里使用

package com.example.androidforbonjour;

import android.net.nsd.NsdServiceInfo;

import java.util.List;

public interface BonjourInfoCallBack {

    public void msgCallback(List<NsdServiceInfo> list);

}

第二步:定义一个工具类FindBonjourTool,专门用于处理Bonjour

package com.example.androidforbonjour;

import static android.content.Context.NSD_SERVICE;

import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.content.Context;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class FindBonjourTool {
    private static NsdManager nsdManager;
    private static String SERVICE_TYPE = "_x5_cfg._tcp";
    private NsdManager.DiscoveryListener mDiscoveryListener;
    BonjourInfoCallBack bonjourInfoCallBack;
    List<NsdServiceInfo> desList = new ArrayList<>();

    FindBonjourTool(Context context, BonjourInfoCallBack bonjourInfoCallBack) {
        if (nsdManager == null) {
            nsdManager = (NsdManager) context.getSystemService(NSD_SERVICE);
        }
        this.bonjourInfoCallBack = bonjourInfoCallBack;
    }


    public void start() {
        desList.clear();
        Log.d("xm--------", "start");
        createDiscoverListener();
    }

    public void stop() {

        nsdManager.stopServiceDiscovery(mDiscoveryListener);
        mDiscoveryListener = null;
        Log.d("xm--------", "stop");
        desList.clear();

    }

    private void createDiscoverListener() {


        if (mDiscoveryListener == null) {
            mDiscoveryListener = new NsdManager.DiscoveryListener() {
                private NsdServiceInfo mmNsdServiceInfo;

                @Override
                public void onStartDiscoveryFailed(String s, int i) {
                    Log.d("xm--------", "onStartDiscoveryFailed");
                }

                @Override
                public void onStopDiscoveryFailed(String s, int i) {
                    Log.d("xm--------", "onStopDiscoveryFailed");
                }

                @Override
                public void onDiscoveryStarted(String s) {
                    Log.d("xm--------", "onDiscoveryStarted");
                }

                @Override
                public void onDiscoveryStopped(String s) {
                    Log.d("xm--------", "onDiscoveryStopped");
                }

                @Override
                public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
                    mmNsdServiceInfo = nsdServiceInfo;
                    //这里的nsdServiceInfo只能获取到名字,ip和端口都不能获取到,要想获取到需要调用NsdManager.resolveService方法

                    Log.d("xm--------", "onServiceFound");
                    Log.d("xm--------", nsdServiceInfo.getServiceName());


                    nsdManager.resolveService(mmNsdServiceInfo, new NsdManager.ResolveListener() {
                        @Override
                        public void onResolveFailed(NsdServiceInfo nsdServiceInfo, int i) {
                            Log.d("xm--------", "onResolveFailed");
                        }

                        @Override
                        public void onServiceResolved(NsdServiceInfo nsdServiceInfo) {
                            Log.d("xm--------", "onServiceResolved");

                            boolean didHad = false;
                            for (int i = 0; i < desList.size(); i++) {
                                NsdServiceInfo info = desList.get(i);
                                if (info.getServiceName().equals(nsdServiceInfo.getServiceName())) {
                                    didHad = true;
                                    break;
                                }
                            }
                            if (!didHad) {
                                desList.add(nsdServiceInfo);
                            }

                            bonjourInfoCallBack.msgCallback(desList);

                        }
                    });

                }

                @Override
                public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
                    Log.d("xm--------", "onServiceLost");
                }
            };
        }

        nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);

    }

}

第三步:在MainActivity中使用工具类FindBonjourTool,其中List<NsdServiceInfo> list就是Bonjour发现的所有设备相关信息的数组

package com.example.androidforbonjour;

import androidx.appcompat.app.AppCompatActivity;

import android.net.nsd.NsdServiceInfo;
import android.os.Bundle;
import android.util.Log;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity {
    FindBonjourTool findBonjourTool;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findBonjourTool = new FindBonjourTool(this, new BonjourInfoCallBack() {
            @Override
            public void msgCallback(List<NsdServiceInfo> list) {
                Log.d("xm--------list", String.valueOf(list.size()));

                for (int i = 0; i < list.size(); i++) {
                    NsdServiceInfo info = list.get(i);
                    info.getServiceName();
                    info.getHost();
                    info.getPort();
                    info.getAttributes();
                    Log.d("xm--------list", info.getServiceName());

                }
            }
        });

        findBonjourTool.start();

        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                findBonjourTool.stop();
            }
        }, 20000);

    }


}

祝君好运~

最终版本 工具类FindBonjourTool

在使用的过程中,如果局域网中有多台设备,在解析服务的过程发现会,解析失败onResolveFailed
原因是因为第一个服务在解析的过程 ,这个时候第二个服务进来了,也要开始解析,导致第一个服务解析过程中断,就导致会失败。所以我们得让解析过程,不要那么快,这样就可以做到,解析第一个服务 =====》解析完成 =====》解析第二个服务 =====》解析完成,以此类推。。
工具类完整代码如下:

package com.example.androidforbonjour;

import static android.content.Context.NSD_SERVICE;

import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.content.Context;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class FindBonjourTool {
    private static NsdManager nsdManager;
    private static String SERVICE_TYPE = "_x5_gw._tcp";
    private NsdManager.DiscoveryListener mDiscoveryListener;
    BonjourInfoCallBack bonjourInfoCallBack;
    List<NsdServiceInfo> desList = new ArrayList<>();
    List<NsdServiceInfo> tempServiceList = new ArrayList<>();

    FindBonjourTool(Context context, BonjourInfoCallBack bonjourInfoCallBack) {
        if (nsdManager == null) {
            nsdManager = (NsdManager) context.getSystemService(NSD_SERVICE);
        }
        this.bonjourInfoCallBack = bonjourInfoCallBack;
    }


    public void start() {
        desList.clear();
        Log.d("xm--------", "start");
        tempServiceList.clear();
        createDiscoverListener();
    }

    public void stop() {

        nsdManager.stopServiceDiscovery(mDiscoveryListener);
        mDiscoveryListener = null;
        nsdManager = null;
        Log.d("xm--------", "stop");
        desList.clear();

    }

    private void createDiscoverListener() {


        if (mDiscoveryListener == null) {
            mDiscoveryListener = new NsdManager.DiscoveryListener() {
//                private NsdServiceInfo mmNsdServiceInfo;

                @Override
                public void onStartDiscoveryFailed(String s, int i) {
                    Log.d("xm--------", "onStartDiscoveryFailed");
                }

                @Override
                public void onStopDiscoveryFailed(String s, int i) {
                    Log.d("xm--------", "onStopDiscoveryFailed");
                }

                @Override
                public void onDiscoveryStarted(String s) {
                    Log.d("xm--------", "onDiscoveryStarted");
                }

                @Override
                public void onDiscoveryStopped(String s) {
                    Log.d("xm--------", "onDiscoveryStopped");
                }

                @Override
                public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
//                    mmNsdServiceInfo = nsdServiceInfo;
                    //这里的nsdServiceInfo只能获取到名字,ip和端口都不能获取到,要想获取到需要调用NsdManager.resolveService方法

                    Log.d("xm--------", "onServiceFound");
                    Log.d("xm--------", nsdServiceInfo.getServiceName());
                    boolean didHad = false;
                    for (int i = 0; i < tempServiceList.size(); i++) {
                        NsdServiceInfo currentInfo = tempServiceList.get(i);
                        if (nsdServiceInfo.getServiceName().equals(currentInfo.getServiceName())) {
                            didHad = true;
                            break;
                        }
                    }
                    if (!didHad) {
                        tempServiceList.add(nsdServiceInfo);
                    }

                    for (NsdServiceInfo info : tempServiceList) {
                        nsdManager.resolveService(info, new NsdManager.ResolveListener() {
                            @Override
                            public void onResolveFailed(NsdServiceInfo nsdServiceInfo, int i) {
                                Log.d("xm--------", "onResolveFailed");
                            }

                            @Override
                            public void onServiceResolved(NsdServiceInfo nsdServiceInfo) {
                                Log.d("xm--------", "onServiceResolved");

                                boolean didHad = false;
                                for (int i = 0; i < desList.size(); i++) {
                                    NsdServiceInfo info = desList.get(i);
                                    if (info.getServiceName().equals(nsdServiceInfo.getServiceName())) {
                                        didHad = true;
                                        break;
                                    }
                                }
                                if (!didHad) {
                                    desList.add(nsdServiceInfo);
                                }

                                bonjourInfoCallBack.msgCallback(desList);

                            }
                        });
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }

                }

                @Override
                public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
                    Log.d("xm--------", "onServiceLost");
                }
            };
        }

        nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);

    }

}


相关文章

网友评论

      本文标题:Android 安卓Bonjour(NSD服务) 客户端基本使用

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