Я полагал, что любое изменение в списке пиров (включение или исключение пира в диапазоне Wifi Direct) может вызвать срабатывание BroadcastReceiver.
Да, я думаю, это должно произойти.
В моем приложении при обнаружении нового однорангового узла его имя правильно включается в ListView, но если одноранговый узел покидает зону действия беспроводной сети (или если я отключу его интерфейс Wi-Fi), BroadcastReceiver не вызывается (точнее, событие WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION не запускается), а имя однорангового узла остается в ListView.
Мы можем попытаться покопаться в исходном коде и посмотреть, сможем ли мы объяснить такое поведение.
Примечание. У меня нет решения, которое я мог бы предложить. Но следующее исследование может помочь вам лучше понять «проблему».
Мы начнем здесь: WifiP2pSettings Ссылка
Это фрагмент, который вы видите, когда выбираете Настройки > Wifi > Wifi-Direct. Если вы просмотрите код, вы заметите, что реализация очень похожа на проект WifiDirectDemo — BroadcastReceiver прослушивает те же четыре действия (и еще два — одно для обновления пользовательского интерфейса). Причина, по которой мы смотрим на этот фрагмент, состоит в том, чтобы проверить, не является ли сама демонстрация ошибочной. Но, похоже, демо в порядке.
Двигаемся дальше — давайте посмотрим, кто транслирует действие WIFI_P2P_PEERS_CHANGED_ACTION — в конечном счете, нам интересно выяснить, почему это не происходит, когда устройство отключается или выходит за пределы диапазона. Это приводит нас к WifiP2pService Ссылка.
Метод WifiP2pService # sendPeersChangedBroadcast() выдает широковещательную рассылку:
private void sendPeersChangedBroadcast() {
final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intent.putExtra(WifiP2pManager.EXTRA_P2P_DEVICE_LIST, new WifiP2pDeviceList(mPeers));
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
Глядя на WifiP2pService, вы можете сказать, что sendPeersChangedBroadcast() вызывается в ответ на несколько событий. Нас интересует вот это: WifiMonitor.P2P_DEVICE_LOST_EVENT Ссылка:
....
case WifiMonitor.P2P_DEVICE_LOST_EVENT:
device = (WifiP2pDevice) message.obj;
// Gets current details for the one removed
device = mPeers.remove(device.deviceAddress);
if (device != null) {
sendPeersChangedBroadcast();
}
break;
....
WifiMonitor # handleP2pEvents(String) отвечает за отправку сообщения, которое попадает в вышеупомянутый case. Поднимитесь по цепочке, чтобы найти MonitorThread — статический внутренний класс в WifiMonitor. MonitorThread # dispatchEvent(String) вызывает метод handleP2pEvents(String).
Наконец, что-то интересное. Посмотрите на метод run() MonitorThread:
private static class MonitorThread extends Thread {
....
public void run() {
//noinspection InfiniteLoopStatement
for (;;) {
String eventStr = mWifiNative.waitForEvent();
....
}
}
....
}
- во-первых, мы внутри бесконечного цикла.
- во-вторых,
mWifiNative.waitForEvent() говорит мне, что это вероятно блокирующий вызов.
Эти два пункта вместе взятые указывают мне на то, что я не получу от этого быстрого ответа — ну, определенно ничего «мгновенного». Метод, которого мы достигли, поднявшись по цепочке — MonitorThread # dispatchEvent(String) — вызывается изнутри этого бесконечного цикла.
Давайте проверим, может ли что-то подтвердить наше обоснованное предположение:
Взгляните на класс WifiNative Ссылка, особенно метод setScanInterval(int). Этот метод вызывается из класса WifiStateMachine Ссылка. Пройдите метод processMessage(Message):
....
case WifiP2pService.P2P_CONNECTION_CHANGED:
NetworkInfo info = (NetworkInfo) message.obj;
mP2pConnected.set(info.isConnected());
if (mP2pConnected.get()) {
int defaultInterval = mContext.getResources().getInteger(
R.integer.config_wifi_scan_interval_p2p_connected);
long scanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),
Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS,
defaultInterval);
// ====>> Interval defined here
mWifiNative.setScanInterval((int) scanIntervalMs/1000);
} else if (mWifiConfigStore.getConfiguredNetworks().size() == 0) {
if (DBG) log("Turn on scanning after p2p disconnected");
sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,
++mPeriodicScanToken, 0), mSupplicantScanIntervalMs);
}
....
Обратите внимание на defaultInterval в первом if block. Он запрашивает R.integer.config_wifi_scan_interval_p2p_connected, который определен в config.xml Link как:
<!-- Integer indicating wpa_supplicant scan interval when p2p is connected in milliseconds -->
<integer translatable="false" name="config_wifi_scan_interval_p2p_connected">60000</integer>
60000 мс. Это 1 минута. Таким образом, если Settings.Global.WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS не задано, интервал сканирования будет составлять 1 минуту.
Поскольку WIFI_SCAN_INTERVAL_WHEN_P2P_CONNECTED_MS находится в глобальных настройках, мы не можем его изменить. На самом деле, это даже невозможно прочитать. Это означает, что единственная гарантия, которую мы здесь имеем, заключается в том, что список одноранговых узлов будет обновлен менее чем за минуту. Интервал, конечно, может варьироваться от бренда к бренду.
Чтобы убедиться в этом, я запустил демоверсию на планшетах asus и dell. Как вы сказали, подключение устройства к сети было замечено довольно быстро (на этапе обнаружения). При выключении Wi-Fi я синхронизировал ответ. Автономное устройство было удалено из списка автоматически со значительной задержкой. Планшету Dell потребовалось около 60 seconds, чтобы заметить, что asus находится в автономном режиме. Asus, с другой стороны, взял 45 seconds.
Мне кажется, что это ограничение, налагаемое Android. Я не могу сказать, почему. Я надеюсь, что кто-то здесь может предложить вам решение - возможно, проведите это исследование и изучите его дальше. Но я не удивлюсь, если решения не существует (в настоящее время).
person
Vikram
schedule
06.08.2014
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION(это событие вызывается системой асинхронно после вызова методаdiscoverPeers(channel, actionListener)). Другой проблемой может быть: как долго должен работать этот сборщик мусора? - person Rafael Fernandes Lopes   schedule 12.01.2014requestPeers()? - person cHao   schedule 12.01.2014Handlerдля выполнения периодического вызоваrequestPeers(), но он не выявил никаких изменений (в данном случае, отключение однорангового узла). - person Rafael Fernandes Lopes   schedule 12.01.2014WiFiDirectDemo. Вы можете воспроизвести эту проблему, просто запустив пример. После запуска Discover с обоих устройств на экране появятся соответствующие имена устройств. Если вы отключите Wi-Fi одного из устройств и снова нажмете кнопку «Обнаружение», устройство не исчезнет! Кроме того, показанный диалог никогда не будет автоматически отменен, так как методonPeersAvailable()из интерфейсаPeerListListenerникогда не вызывается. - person Rafael Fernandes Lopes   schedule 13.01.2014