对mdns完全小白的同学,推荐一篇文章iOS开发mdns之 Bonjour的使用, 在前一篇文章本人也分享了在iOS中如何使用Bonjour,请前往iOS mdns之Bonjour 基本使用及完美解决各种坑~
此篇文章完全是从零基础的角度出发,详细地记录Android中mdns的使用过程,希望能帮助到您
好了,下面回到正题,从新建项目开始
第一步,在Android Studio新建一个空activity项目
如下图
后面就是给项目取一个名称,然后构建项目了
第二步,增加一个网络权限
在安卓中,要使用mdns,是使用NSD服务的,在使用NSD服务之前需要申请网络权限. 因为要在局域网去发现设备,用到了网络.
在 /项目名/app/src/main/AndroidManifest.xml
中增加权限, 增加权限代码需要放在<application />
标签的前面;如下图:
第三步,完整代码及运行后的结果
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端口, 后面就是使用它们,建立起连接,来与设备进行通讯了.
第四步,说明一下使用过程
1)第一步,初始化NsdManager
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());
}
结尾
为什么要用mdns呢,因为如果不用mdns的话,很多公司就会用到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
就是mdns发现的所有设备相关信息的数组
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);
}
}