Android上的Bonjour实现

30

我正在尝试在我的安卓应用程序中实现Bonjour/Zeroconf功能。我正在使用jmDns库搜索所有可用设备。以下是我用于在同一网络中搜索设备的代码:

public class ListDevices extends ListActivity {
    JmDNS jmdns;
    JmDNSImpl impl;
    MulticastLock lock;
    protected ServiceListener listener;
    protected ServiceInfo info;
    public ListView lv;
    public ArrayList<String> deviceList;
    public int cancel = 0;
    public final static String TAG = "ListDevices";

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        deviceList = new ArrayList<String>();
        showAllPrinters();

        setListAdapter(new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, deviceList));

        lv = getListView();
        lv.setTextFilterEnabled(true);

        lv.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                // When clicked, show a toast with the TextView text
                Toast.makeText(getApplicationContext(),
                       ((TextView) view).getText(), Toast.LENGTH_SHORT).show();
            }
        });
        this.listener = new ServiceListener() {
            public void serviceAdded(ServiceEvent event) {
                deviceList.add("Service added   : " + event.getName() + "."
                        + event.getType());
                Log.v(TAG, "Service added   : " + event.getName() + "."
                        + event.getType());
            }

            public void serviceRemoved(ServiceEvent event) {
                deviceList.add("Service removed : " + event.getName() + "."
                        + event.getType());
                Log.v(TAG, "Service removed : " + event.getName() + "."
                        + event.getType());
            }

            public void serviceResolved(ServiceEvent event) {
                deviceList.add("Service resolved: " + event.getInfo());
                Log.v(TAG, "Service resolved: " + event.getInfo());
            }
        };
    }

    public void showAllPrinters() {
        Log.d("ListDevices", "in showAllPrinters");
        try {

            WifiManager wifi = (WifiManager)
                               getSystemService(Context.WIFI_SERVICE);
            lock = wifi.createMulticastLock("fliing_lock");
            lock.setReferenceCounted(true);
            lock.acquire();

            InetAddress inetAddress = getLocalIpAddress();
            jmdns = JmDNS.create(inetAddress, "TEST");

            ServiceInfo[] infos = jmdns.list("_http._tcp.local.");

            if (infos != null && infos.length > 0) {
                for (int i = 0; i < infos.length; i++) {
                    deviceList.add(infos[i].getName());
                }
            } else {
                deviceList.add("No device found.");
            }
            impl = (JmDNSImpl) jmdns;

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public InetAddress getLocalIpAddress() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface
                    .getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = (NetworkInterface) en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf
                        .getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = (InetAddress) enumIpAddr
                            .nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
                        return inetAddress;
                    }
                }
            }
        } catch (SocketException ex) {
            Log.e("ListDevices", ex.toString());
        }
        return null;
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (isFinishing()) {
            lock.release();
        }
    }
}

基本上,我正在将它们添加到列表中,以便可以显示所有可用设备的列表。 现在当我运行此代码时,我没有得到任何异常/错误之类的东西。但另一方面,我的列表中没有添加任何内容[附注:至少有5-6个PC和Mac在网络中。

我还尝试从这段代码获取列表:

jmdns.addServiceListener("_http._tcp.local.", listener);

listener在活动的onCreate中定义。但是这样也没有返回任何设备。

请帮忙,建议我在这里做错了什么。非常感谢您的任何帮助!


你不应该使用这段代码吗:if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress() && inetAddress.isSiteLocalAddress()) - Radu
@mudit。我知道你遇到这个问题已经有一段时间了。你尝试过使用“_http._tcp”而不是“_http._tcp.local.”进行列表吗?我无法解释为什么,但在我的情况下确实有所不同。到目前为止,只有在使用NsdManager(标准Android发现)时才会有所不同。当我们的用户尝试后,我将知道它如何影响JmDNS查找... - vladimir
5个回答

20

Android 4.1增加了网络服务发现功能,似乎只是用不同的术语包装Bonjour堆栈。我还看到一个名为android.net.wifi.p2p.WifiP2pManager的低级API,直接公开DNS-SD(以及UPnP)。

请注意,潜在的mDNSResponder守护程序似乎并不总是运行,并且据我目前所知,它不用于系统范围的DNS查找(例如从浏览器)。


网络服务发现和Bonjour都是Zeroconf的实现,它们并不包装彼此。 - Trisped
5
请勿使用此功能直到安卓解决与网络服务发现相关的问题。这些问题可参考以下链接:1) http://code.google.com/p/android/issues/detail?id=39583 2) https://code.google.com/p/android/issues/detail?id=35585 3) http://code.google.com/p/android/issues/detail?id=39750 - Darshit Patel
我发现在使用Android 5时,Android NDS比一些其他评论中提到的mdnsjava更好。不幸的是,它仍然不能给我与Bonjour浏览器应用程序中看到的相同结果。 - Bill
Android API仅适用于获取服务名称、IP地址和端口。虽然API在技术上支持其他信息(文本),但您会发现它在99%的设备上无法正常工作。 - bremen_matt

5

4

你是否确认你的手机启用了组播?可以查看 http://home.heeere.com/tech-androidjmdns.html

而且,你应该寻找"_ipp._tcp.local"(或类似的)而不是"_http.tcp"服务。但这只是用于测试,对吧? :-)


4

如上方评论所述,原生 Android 支持尚未完全实现以允许检索 TXT 记录(截至 Android v5.1)。我也无法让 jmDns 库用于发现。最终我找到了 mdnsjava 项目,它对我来说非常容易使用。请注意,其示例代码不正确。以下是我用于同步查找所有 IPP 打印机的一些示例代码:

    String type[] = {"_ipp._tcp.local."};
    Lookup resolve = null;
    try {
        resolve = new Lookup(type, Type.ANY, DClass.IN);
        ServiceInstance[] records = resolve.lookupServices();
        for (ServiceInstance record : records) {
            Log.d(TAG, record.toString());
            Map txtMap = record.getTextAttributes();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

请注意,您需要将dnsjava库添加到libs文件夹和build.gradle中。我使用的版本是2.1.7。


3

您可以使用Android Play商店中的现有工具首先扫描本地网络,例如“Bonjour浏览器”,以确保存在您想要扫描的服务。然后您可以检查jmDNS关键字来扫描网络。

但是已知问题是,jmDns在某些Android 4.x设备上无法正常工作。


1
你好@vladimir,在开发过程中,我们确认JmDNS在某些Android 4.1设备上无法工作,它只能在4.1版本以下的设备上运行。原因可能是JmDNS在Android平台上的实现,因为Bonjour的原始实现是在iOS平台上完成的,但不像iOS那样易用。顺便说一下,早期的4.x版本上,Android本地NSD也不太可靠,经常会崩溃。 - Zephyr
mDNS在哪些设备上无法正常工作?根据我的经验,我在一些三星平板电脑上遇到了问题。 - gregm
@gregm,那是一款三星平板电脑,型号为GT-P5113。我猜测这可能是Android框架内部出了一些问题。 - Zephyr
我这里有一台三星SM-T110平板电脑。仍然无法找出问题所在。这可能也与我的Belkin路由器有关。 - gregm
@gregm,你可以使用iOS设备进行交叉比对,也许你可以把Belkin路由器从嫌疑清单中排除掉。 - Zephyr
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接