Wifi P2P服务发现工作不稳定

31
Wifi P2P服务发现的表现不符合预期。我看到有间歇性问题,DNSSD侦听器并非总是被调用,因此我不知道附近是否有运行相同应用程序的设备。我正在使用以下两个API之一来注册要被其他设备发现的服务,另一个用于发现在其他设备上运行的附近服务。请问我是否做错了什么,或者在调用这些API之前是否需要进行某些特定序列的其他Android API调用以确保每当有新服务注册或即使在我们调用API之前注册服务时,侦听器总是被调用。

用于注册本地服务的API:

private void registerService() {
    Map<String, String> values = new HashMap<String, String>();
    values.put("name", "Steve");
    values.put("port", "8080");
    WifiP2pServiceInfo srvcInfo = WifiP2pDnsSdServiceInfo.newInstance(mMyDevice.deviceName, "_http._tcp", values);

    manager.addLocalService(channel, srvcInfo, new WifiP2pManager.ActionListener() {

        @Override
        public void onSuccess() {
            Toast.makeText(WiFiDirectActivity.this, "Local service added successfully", 
                Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onFailure(int reasonCode) {
            Toast.makeText(WiFiDirectActivity.this, "Local service addition failed : " + reasonCode,
                    Toast.LENGTH_SHORT).show();
        }
    });
}

发现本地服务的API:

public void discoverService() {

    manager.clearServiceRequests(channel, null);

    DnsSdTxtRecordListener txtListener = new DnsSdTxtRecordListener() {
        @Override
        /* Callback includes:
         * fullDomain: full domain name: e.g "printer._ipp._tcp.local."
         * record: TXT record data as a map of key/value pairs.
         * device: The device running the advertised service.
         */
        public void onDnsSdTxtRecordAvailable(String fullDomain, Map record, WifiP2pDevice device) {
            Log.d(TAG, "DnsSdTxtRecord available -" + record.toString());
        }
    };

    DnsSdServiceResponseListener servListener = new DnsSdServiceResponseListener() {
        @Override
        public void onDnsSdServiceAvailable(String instanceName, String registrationType, WifiP2pDevice resourceType) {
            Log.d(TAG, "onBonjourServiceAvailable " + instanceName);
        }
    };

    manager.setDnsSdResponseListeners(channel, servListener, txtListener);

    WifiP2pDnsSdServiceRequest serviceRequest = WifiP2pDnsSdServiceRequest.newInstance();
    manager.addServiceRequest(channel, serviceRequest, new ActionListener() {

        @Override
        public void onSuccess() {
            // Success!
            Log.d(TAG, "addServiceRequest success");
        }

        @Override
        public void onFailure(int code) {
            // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
            Log.d(TAG, "addServiceRequest failure with code " + code);
        }

    });
    manager.discoverServices(channel, new ActionListener() {

        @Override
        public void onSuccess() {
            // Success!
            Log.d(TAG, "discoverServices success");
        }

        @Override
        public void onFailure(int code) {
            // Command failed.  Check for P2P_UNSUPPORTED, ERROR, or BUSY
            if (code == WifiP2pManager.P2P_UNSUPPORTED) {
                Log.d(TAG, "P2P isn't supported on this device.");
            } else {
                Log.d(TAG, "discoverServices failure");
            }
        }
    });
}

注意:manager和channel已被初始化为
WifiP2pManager manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
Channel channel = manager.initialize(this, getMainLooper(), null);

我也遇到了同样的问题,有时候会回调到DnsSdServiceResponseListener。你找出了原因吗?还是说这不是一个稳定的功能? - user331244
2个回答

39

WifiP2p(一般):

不久之前,我正在开发一个基于 WifiP2pService Broadcasting/Discovery 的相当复杂的网络连接系统的应用程序。根据那个经验,我已经在这里写了几篇关于它的帖子,讲述了使用 WifiP2pService Discovery 非常困难、耗时和问题重重。以下是其中两篇文章(它们充满了我对 WifiP2pService Discovery 的深入了解):

为什么 Android WifiDirect 的发现节点不可靠

Wi-fi P2P. 通知所有 Peers 某个事件的发生

我建议你阅读我的两个答案(即使它们更侧重于 WifiP2p)。当你使用 WifiP2p Service Discovery 时,它们应该会给你提供一些有用的信息。

WifiP2p Service Discovery

为了更好地回答您的确切问题,我将告诉您我所做的事情(与您不同)以使我的 Service Discovery 相当可靠。

1. 广播 Service

首先:在注册您的 Service(使用 addLocalService 方法)之前,您应该使用 WifiP2pManagerclearLocalServices 方法。而且很重要的一点是,在 clearLocalServices 的监听器返回带有 onSuccess 回调时,您应该调用 addLocalService 方法。

尽管这样设置广播非常好,但我发现其他节点并不总是能够检测到广播的服务(特别是当那些节点在注册本地服务时没有处于主动检测服务的状态,但它们后来加入了)。我无法找到一种方法来100%可靠地解决这个问题。相信我,我已经尝试了可能与WifiP2p相关的一切东西。不,clearLocalServices-addLocalService序列并没有给出令人满意的结果。更确切地说:做一些不同的事情效果要好得多。我决定在成功添加本地服务(addLocalService的onSuccess回调)后,启动一个线程,定期调用WifiP2pManager的discoverPeers方法。这似乎迫使重新广播所有服务信息。
因此...基本上您的广播代码应该看起来像这样(请记住,我将发布的每一行代码都剥离了所有“检查”网络连接系统是否处于正确状态的内容,您应该自己设计它们以最好地适合您的解决方案):
public void startBroadcastingService(){
    mWifiP2pManager.clearLocalServices(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
        @Override
        public void onSuccess() {
            mWifiP2pManager.addLocalService(mWifiP2pChannel, mWifiP2pServiceInfo,
                    new WifiP2pManager.ActionListener() {

                        @Override
                        public void onSuccess() {
                            // service broadcasting started
                            mServiceBroadcastingHandler
                                    .postDelayed(mServiceBroadcastingRunnable,
                                            SERVICE_BROADCASTING_INTERVAL);
                        }

                        @Override
                        public void onFailure(int error) {
                            // react to failure of adding the local service
                        }
                    });
        }

        @Override
        public void onFailure(int error) {
            // react to failure of clearing the local services
        }
    });
}

mServiceBroadcastingRunnable应该放在哪里:

private Runnable mServiceBroadcastingRunnable = new Runnable() {
    @Override
    public void run() {
        mWifiP2pManager.discoverPeers(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
            }

            @Override
            public void onFailure(int error) {
            }
        });
        mServiceBroadcastingHandler
                .postDelayed(mServiceBroadcastingRunnable, SERVICE_BROADCASTING_INTERVAL);
    }
};

2. 发现服务:

我使用了类似的方法来发现您的服务。在设置发现和尝试强制“重新发现”服务时,都是如此。

设置是通过以下三个WifiP2pManager方法的顺序执行的:

removeServiceRequest, addServiceRequest, discoverServices

它们按照这个确切的顺序调用,并且只有在前一个方法通过onSuccess回调“返回”后才会调用特定方法(第二个或第三个方法)。

使用直观方法执行服务的重新发现(只需重复上述顺序:removeServiceRequest -> addServiceRequest -> discoverServices)。

我的代码基础看起来更或多或少像这样(要开始服务发现,我会先调用prepareServiceDiscovery(),然后调用startServiceDiscovery()):

public void prepareServiceDiscovery() {
    mWifiP2pManager.setDnsSdResponseListeners(mWifiP2pChannel,
            new WifiP2pManager.DnsSdServiceResponseListener() {

                @Override
                public void onDnsSdServiceAvailable(String instanceName,
                                                    String registrationType, WifiP2pDevice srcDevice) {
                    // do all the things you need to do with detected service
                }
            }, new WifiP2pManager.DnsSdTxtRecordListener() {

                @Override
                public void onDnsSdTxtRecordAvailable(
                        String fullDomainName, Map<String, String> record,
                        WifiP2pDevice device) {
                    // do all the things you need to do with detailed information about detected service
                }
            });

    mWifiP2pServiceRequest = WifiP2pDnsSdServiceRequest.newInstance();
}

private void startServiceDiscovery() {
    mWifiP2pManager.removeServiceRequest(mWifiP2pChannel, mWifiP2pServiceRequest,
            new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    mWifiP2pManager.addServiceRequest(mWifiP2pChannel, mWifiP2pServiceRequest,
                            new WifiP2pManager.ActionListener() {

                                @Override
                                public void onSuccess() {
                                    mWifiP2pManager.discoverServices(mWifiP2pChannel,
                                            new WifiP2pManager.ActionListener() {

                                                @Override
                                                public void onSuccess() {
                                                    //service discovery started

                                                    mServiceDiscoveringHandler.postDelayed(
                                                            mServiceDiscoveringRunnable,
                                                            SERVICE_DISCOVERING_INTERVAL);
                                                }

                                                @Override
                                                public void onFailure(int error) {
                                                    // react to failure of starting service discovery
                                                }
                                            });
                                }

                                @Override
                                public void onFailure(int error) {
                                    // react to failure of adding service request
                                }
                            });
                }

                @Override
                public void onFailure(int reason) {
                    // react to failure of removing service request
                }
            });
}

mServiceDiscoveringRunnable 就是:

private Runnable mServiceDiscoveringRunnable = new Runnable() {
    @Override
    public void run() {
        startServiceDiscovery();
    }
};

所有这些让我的系统表现得非常好。它还不完美,但考虑到这个主题缺乏文档,我认为我不能再做更多的改进。

如果你测试这种方法,请务必告诉我它对你有什么效果(或者说它是否对你有用;))。


说实话很难说...我记得它并不完美,有时用户需要等待几秒钟才能发现其他设备(服务)。我真的找不到解决这个问题的办法。你描述的问题可能也发生在我身上,只是我没有看到这种模式(服务->无->服务->无...)。 - Bartek Lipinski
我发现发布阶段应该始终在发现阶段之前进行。否则,发现将不会返回任何结果。 - Roberto Betancourt
@RichardWong,我认为你看到的是发现超时了,请查看这个Android问题:https://code.google.com/p/android/issues/detail?id=206123 .. 解决方法是每2分钟强制进行发现。 - Nonos
1
@BartekLipinski 感谢您提供的代码示例。我已经实现了类似的功能,它确实可以工作。但是,它会阻止我的手机连接到任何WiFi网络。您是否注意到了这样的情况? - vipul mittal
你用什么来定义你的WifiP2pServiceInfo?这是我在这里没有看到定义的唯一事物。 - Code Wiget
显示剩余8条评论

0
如果问题是服务的检测,我相信创建组是使设备和服务可检测的最佳方法,但如果在所有设备中创建了组,则无法直接连接,只能通过WiFi网络连接。我每天都这样做,它很有效。

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