Ответ 1
Я полагал, что любое изменение в списке одноранговых узлов (включение или исключение однорангового узла в диапазоне 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();
....
}
}
....
}
- во-первых, мы находимся внутри бесконечного цикла.
- second,
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
Ссылка как:
<!-- 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
.
Для меня это похоже на ограничение, введенное андроидом. Я не могу понять, почему. Я надеюсь, что кто-то здесь может предложить вам решение - возможно, возьмите это исследование и изучите его дальше. Но я не удивлюсь, если решение не существует (в настоящее время).