Wi-Fi Direct (inaczej: peer-to-peer lub P2P) umożliwia aplikacji szybkie znajdowanie w pobliżu i interakcję z nimi wykracza poza możliwości zapewniane przez Bluetooth.
Interfejsy API Wi-Fi Direct (P2P) pozwalają aplikacjom łączyć się z urządzeniami w pobliżu bez Potrzebne jest połączenie z siecią lub hotspotem. Jeśli aplikacja została zaprojektowana jako część bezpiecznej sieci Wi-Fi w pobliżu Bezpośrednie połączenie to bardziej odpowiednie rozwiązanie niż tradycyjna sieć Wi-Fi, doraźna sieci z następujących powodów:
- Wi-Fi Direct obsługuje szyfrowanie WPA2. (Niektóre sieci doraźne obsługują tylko szyfrowania WEP).
- Urządzenia mogą przesyłać informacje o udostępnianych przez nie usługach, co pomaga innym urządzenia z łatwością wykrywają odpowiednie podobne urządzenia.
- Podczas ustalania, które urządzenie powinno być właścicielem grupy w sieci, Wi-Fi Direct sprawdza zarządzanie zasilaniem, interfejs i usługi każdego urządzenia i wykorzystuje te informacje, aby wybrać urządzenie z odpowiedzialnością serwera.
- Android nie obsługuje trybu ad-hoc Wi-Fi.
Z tej lekcji dowiesz się, jak znajdować urządzenia w pobliżu i łączyć się z nimi za pomocą sieci Wi-Fi P2P.
Konfigurowanie uprawnień aplikacji
Aby użyć Wi-Fi Direct, dodaj
ACCESS_FINE_LOCATION
,
CHANGE_WIFI_STATE
,
ACCESS_WIFI_STATE
i
Uprawnienia (INTERNET
) do Twojego pliku manifestu.
Jeśli Twoja aplikacja jest kierowana na Androida 13 (poziom interfejsu API 33) lub nowszego, dodaj też
NEARBY_WIFI_DEVICES
dostęp do pliku manifestu. Wi-Fi,
Bezpośrednio nie wymaga połączenia z internetem, ale wymaga użycia standardowej wersji Java.
które wymaga INTERNET
uprawnienia. Aby korzystać z Wi-Fi Direct, potrzebujesz tych uprawnień:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.nsdchat" ... <!-- If your app targets Android 13 (API level 33) or higher, you must declare the NEARBY_WIFI_DEVICES permission. --> <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" <!-- If your app derives location information from Wi-Fi APIs, don't include the "usesPermissionFlags" attribute. --> android:usesPermissionFlags="neverForLocation" /> <uses-permission android:required="true" android:name="android.permission.ACCESS_FINE_LOCATION" <!-- If any feature in your app relies on precise location information, don't include the "maxSdkVersion" attribute. --> android:maxSdkVersion="32" /> <uses-permission android:required="true" android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:required="true" android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:required="true" android:name="android.permission.INTERNET"/> ...
Oprócz poprzednich uprawnień te interfejsy API wymagają też włączenia trybu lokalizacji:
Konfigurowanie odbiornika i menedżera połączeń peer-to-peer
Aby korzystać z Wi-Fi Direct, musisz nasłuchiwać intencji transmisji, które informują
aplikacji, gdy wystąpiły określone zdarzenia. Utwórz w swojej aplikacji instancję
IntentFilter
i ustaw nasłuchiwanie tych zdarzeń:
WIFI_P2P_STATE_CHANGED_ACTION
- Wskazuje, czy funkcja Wi-Fi Direct jest włączona
WIFI_P2P_PEERS_CHANGED_ACTION
- Oznacza, że dostępna lista podobnych wydawców uległa zmianie.
WIFI_P2P_CONNECTION_CHANGED_ACTION
-
Wskazuje, że stan połączenia Wi-Fi Direct uległ zmianie. Od
Android 10. Jeśli aplikacja korzystała z tych wiadomości,
transmisji podczas rejestracji, ponieważ były przyklejone, użyj odpowiedniego pola
get
, aby uzyskać informacje. WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
-
Wskazuje, że szczegóły konfiguracji urządzenia uległy zmianie. Od
Android 10. Jeśli aplikacja korzystała z tych wiadomości,
transmisji podczas rejestracji, ponieważ były przyklejone, użyj odpowiedniego pola
get
, aby uzyskać informacje.
Kotlin
private val intentFilter = IntentFilter() ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) // Indicates a change in the Wi-Fi Direct status. intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION) // Indicates a change in the list of available peers. intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION) // Indicates the state of Wi-Fi Direct connectivity has changed. intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION) // Indicates this device's details have changed. intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION) ... }
Java
private final IntentFilter intentFilter = new IntentFilter(); ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Indicates a change in the Wi-Fi Direct status. intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); // Indicates a change in the list of available peers. intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); // Indicates the state of Wi-Fi Direct connectivity has changed. intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); // Indicates this device's details have changed. intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); ... }
Na końcu metody onCreate()
pobierz instancję WifiP2pManager
i wywołaj jej initialize()
. Ta metoda zwraca obiekt WifiP2pManager.Channel
, którego użyjesz później do
połączyć aplikację z platformą Wi-Fi Direct.
Kotlin
private lateinit var channel: WifiP2pManager.Channel private lateinit var manager: WifiP2pManager override fun onCreate(savedInstanceState: Bundle?) { ... manager = getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager channel = manager.initialize(this, mainLooper, null) }
Java
Channel channel; WifiP2pManager manager; @Override public void onCreate(Bundle savedInstanceState) { ... manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); channel = manager.initialize(this, getMainLooper(), null); }
Teraz utwórz nową klasę BroadcastReceiver
, której będziesz używać do nasłuchiwania zmian
z siecią Wi-Fi systemu. W: onReceive()
dodaj warunek obsługi każdej zmiany stanu wymienionej powyżej.
Kotlin
override fun onReceive(context: Context, intent: Intent) { when(intent.action) { WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> { // Determine if Wi-Fi Direct mode is enabled or not, alert // the Activity. val state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1) activity.isWifiP2pEnabled = state == WifiP2pManager.WIFI_P2P_STATE_ENABLED } WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> { // The peer list has changed! We should probably do something about // that. } WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> { // Connection state changed! We should probably do something about // that. } WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> { (activity.supportFragmentManager.findFragmentById(R.id.frag_list) as DeviceListFragment) .apply { updateThisDevice( intent.getParcelableExtra( WifiP2pManager.EXTRA_WIFI_P2P_DEVICE) as WifiP2pDevice ) } } } }
Java
@Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { // Determine if Wi-Fi Direct mode is enabled or not, alert // the Activity. int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1); if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) { activity.setIsWifiP2pEnabled(true); } else { activity.setIsWifiP2pEnabled(false); } } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { // The peer list has changed! We should probably do something about // that. } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { // Connection state changed! We should probably do something about // that. } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) { DeviceListFragment fragment = (DeviceListFragment) activity.getFragmentManager() .findFragmentById(R.id.frag_list); fragment.updateThisDevice((WifiP2pDevice) intent.getParcelableExtra( WifiP2pManager.EXTRA_WIFI_P2P_DEVICE)); } }
Na koniec dodaj kod rejestrujący filtr intencji i odbiornik, gdy
Twoja główna aktywność jest aktywna i wyrejestruj ją, gdy jest wstrzymana.
Najlepiej zrobić to w onResume()
i
onPause()
metody.
Kotlin
/** register the BroadcastReceiver with the intent values to be matched */ public override fun onResume() { super.onResume() receiver = WiFiDirectBroadcastReceiver(manager, channel, this) registerReceiver(receiver, intentFilter) } public override fun onPause() { super.onPause() unregisterReceiver(receiver) }
Java
/** register the BroadcastReceiver with the intent values to be matched */ @Override public void onResume() { super.onResume(); receiver = new WiFiDirectBroadcastReceiver(manager, channel, this); registerReceiver(receiver, intentFilter); } @Override public void onPause() { super.onPause(); unregisterReceiver(receiver); }
Rozpocznij wyszukiwanie rówieśników
Aby zacząć wyszukiwać urządzenia w pobliżu z użyciem Wi-Fi P2P, zadzwoń pod numer discoverPeers()
. Ta metoda pobiera
następujące argumenty:
WifiP2pManager.Channel
, które Ty odebrany po zainicjowaniu menedżera mManagera typu peer-to-peer- Implementacja atrybutu
WifiP2pManager.ActionListener
z metodami który wywołuje system w przypadku udanego i nieudanego wykrywania.
Kotlin
manager.discoverPeers(channel, object : WifiP2pManager.ActionListener { override fun onSuccess() { // Code for when the discovery initiation is successful goes here. // No services have actually been discovered yet, so this method // can often be left blank. Code for peer discovery goes in the // onReceive method, detailed below. } override fun onFailure(reasonCode: Int) { // Code for when the discovery initiation fails goes here. // Alert the user that something went wrong. } })
Java
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { // Code for when the discovery initiation is successful goes here. // No services have actually been discovered yet, so this method // can often be left blank. Code for peer discovery goes in the // onReceive method, detailed below. } @Override public void onFailure(int reasonCode) { // Code for when the discovery initiation fails goes here. // Alert the user that something went wrong. } });
Pamiętaj, że to tylko inicjuje odkrywanie aplikacji porównawczych.
Metoda discoverPeers()
rozpoczyna proces wykrywania, a następnie
natychmiast zwraca. System powiadomi Cię, jeśli proces odkrywania aplikacji z grupy porównawczej jest
zostało zainicjowane przez metody wywoływania w podanym detektorze działań.
Wykrywanie pozostanie aktywne do czasu zainicjowania połączenia lub do czasu, aż zostanie zainicjowana grupa P2P.
.
Pobierz listę elementów z grupy porównawczej
Teraz napisz kod, który będzie pobierać i przetwarzać listę elementów równorzędnych. 1.
zastosuj WifiP2pManager.PeerListListener
interfejsu, który dostarcza informacje o peerach, o których ma dostęp Wi-Fi Direct.
– wykryto. Dzięki tym informacjom aplikacja może też określić, kiedy inne osoby dołączają do spotkania lub
opuszczą sieć. Operacje te ilustruje poniższy fragment kodu
podobne do grupy porównawczej:
Kotlin
private val peers = mutableListOf<WifiP2pDevice>() ... private val peerListListener = WifiP2pManager.PeerListListener { peerList -> val refreshedPeers = peerList.deviceList if (refreshedPeers != peers) { peers.clear() peers.addAll(refreshedPeers) // If an AdapterView is backed by this data, notify it // of the change. For instance, if you have a ListView of // available peers, trigger an update. (listAdapter as WiFiPeerListAdapter).notifyDataSetChanged() // Perform any other updates needed based on the new list of // peers connected to the Wi-Fi P2P network. } if (peers.isEmpty()) { Log.d(TAG, "No devices found") return@PeerListListener } }
Java
private List<WifiP2pDevice> peers = new ArrayList<WifiP2pDevice>(); ... private PeerListListener peerListListener = new PeerListListener() { @Override public void onPeersAvailable(WifiP2pDeviceList peerList) { List<WifiP2pDevice> refreshedPeers = peerList.getDeviceList(); if (!refreshedPeers.equals(peers)) { peers.clear(); peers.addAll(refreshedPeers); // If an AdapterView is backed by this data, notify it // of the change. For instance, if you have a ListView of // available peers, trigger an update. ((WiFiPeerListAdapter) getListAdapter()).notifyDataSetChanged(); // Perform any other updates needed based on the new list of // peers connected to the Wi-Fi P2P network. } if (peers.size() == 0) { Log.d(WiFiDirectActivity.TAG, "No devices found"); return; } } }
Teraz zmodyfikuj onReceive()
odbiornika
metoda wywołania requestPeers()
po otrzymaniu intencji z działaniem WIFI_P2P_PEERS_CHANGED_ACTION
. Ty
będzie trzeba w jakiś sposób przekazać ten detektor do odbiornika. Jednym ze sposobów jest wysłanie
jako argument w konstruktorze odbiornika.
Kotlin
fun onReceive(context: Context, intent: Intent) { when (intent.action) { ... WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> { // Request available peers from the wifi p2p manager. This is an // asynchronous call and the calling activity is notified with a // callback on PeerListListener.onPeersAvailable() mManager?.requestPeers(channel, peerListListener) Log.d(TAG, "P2P peers changed") } ... } }
Java
public void onReceive(Context context, Intent intent) { ... else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { // Request available peers from the wifi p2p manager. This is an // asynchronous call and the calling activity is notified with a // callback on PeerListListener.onPeersAvailable() if (mManager != null) { mManager.requestPeers(channel, peerListListener); } Log.d(WiFiDirectActivity.TAG, "P2P peers changed"); }... }
A teraz intencja z intencją działania WIFI_P2P_PEERS_CHANGED_ACTION
wysyła żądanie zaktualizowanej listy elementów równorzędnych.
Nawiąż połączenie
Aby połączyć się z peerem, utwórz nowy obiekt WifiP2pConfig
i skopiuj do niego dane z
WifiP2pDevice
oznacza urządzenie, które chcesz
nawiązać połączenie. Następnie zadzwoń pod numer connect()
.
Kotlin
override fun connect() { // Picking the first device found on the network. val device = peers[0] val config = WifiP2pConfig().apply { deviceAddress = device.deviceAddress wps.setup = WpsInfo.PBC } manager.connect(channel, config, object : WifiP2pManager.ActionListener { override fun onSuccess() { // WiFiDirectBroadcastReceiver notifies us. Ignore for now. } override fun onFailure(reason: Int) { Toast.makeText( this@WiFiDirectActivity, "Connect failed. Retry.", Toast.LENGTH_SHORT ).show() } }) }
Java
@Override public void connect() { // Picking the first device found on the network. WifiP2pDevice device = peers.get(0); WifiP2pConfig config = new WifiP2pConfig(); config.deviceAddress = device.deviceAddress; config.wps.setup = WpsInfo.PBC; manager.connect(channel, config, new ActionListener() { @Override public void onSuccess() { // WiFiDirectBroadcastReceiver notifies us. Ignore for now. } @Override public void onFailure(int reason) { Toast.makeText(WiFiDirectActivity.this, "Connect failed. Retry.", Toast.LENGTH_SHORT).show(); } }); }
Jeśli każde z urządzeń w grupie obsługuje Wi-Fi Direct, nie musisz
jawna prośba o podanie hasła do grupy podczas nawiązywania połączenia. Aby zezwolić na urządzenie
który nie obsługuje Wi-Fi Direct, aby dołączyć do grupy, musisz jednak
i odzyskaj to hasło, dzwoniąc
requestGroupInfo()
, jako
w tym fragmencie kodu:
Kotlin
manager.requestGroupInfo(channel) { group -> val groupPassword = group.passphrase }
Java
manager.requestGroupInfo(channel, new GroupInfoListener() { @Override public void onGroupInfoAvailable(WifiP2pGroup group) { String groupPassword = group.getPassphrase(); } });
Pamiętaj, że interfejs WifiP2pManager.ActionListener
zaimplementowany w
metoda connect()
powiadamia Cię tylko wtedy, gdy inicjacja
czy nam się uda, czy nie. Aby nasłuchiwać zmian w stanie połączenia, zaimplementuj funkcję
interfejsu WifiP2pManager.ConnectionInfoListener
.
Jego wywołanie zwrotne onConnectionInfoAvailable()
powiadamia Cię, gdy stan
zmian połączenia. W sytuacji, gdy chcesz połączyć więcej niż 1 urządzenie.
1 urządzenie (np. gra z co najmniej 3 graczami lub komunikator), jedno urządzenie
jest oznaczony jako „właściciel grupy”. Możesz oznaczyć konkretne urządzenie jako
właściciela grupy sieci, wykonując czynności opisane w
Utwórz grupę.
Kotlin
private val connectionListener = WifiP2pManager.ConnectionInfoListener { info -> // String from WifiP2pInfo struct val groupOwnerAddress: String = info.groupOwnerAddress.hostAddress // After the group negotiation, we can determine the group owner // (server). if (info.groupFormed && info.isGroupOwner) { // Do whatever tasks are specific to the group owner. // One common case is creating a group owner thread and accepting // incoming connections. } else if (info.groupFormed) { // The other device acts as the peer (client). In this case, // you'll want to create a peer thread that connects // to the group owner. } }
Java
@Override public void onConnectionInfoAvailable(final WifiP2pInfo info) { // String from WifiP2pInfo struct String groupOwnerAddress = info.groupOwnerAddress.getHostAddress(); // After the group negotiation, we can determine the group owner // (server). if (info.groupFormed && info.isGroupOwner) { // Do whatever tasks are specific to the group owner. // One common case is creating a group owner thread and accepting // incoming connections. } else if (info.groupFormed) { // The other device acts as the peer (client). In this case, // you'll want to create a peer thread that connects // to the group owner. } }
Teraz wróć do metody onReceive()
odbiornika i zmodyfikuj sekcję
który nasłuchuje intencji WIFI_P2P_CONNECTION_CHANGED_ACTION
.
Po otrzymaniu tej intencji wywołaj requestConnectionInfo()
. Jest to wywołanie asynchroniczne,
aby wyniki były odbierane przez podany przez Ciebie detektor informacji o połączeniu
.
Kotlin
when (intent.action) { ... WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> { // Connection state changed! We should probably do something about // that. mManager?.let { manager -> val networkInfo: NetworkInfo? = intent .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO) as NetworkInfo if (networkInfo?.isConnected == true) { // We are connected with the other device, request connection // info to find group owner IP manager.requestConnectionInfo(channel, connectionListener) } } } ... }
Java
... } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { if (manager == null) { return; } NetworkInfo networkInfo = (NetworkInfo) intent .getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO); if (networkInfo.isConnected()) { // We are connected with the other device, request connection // info to find group owner IP manager.requestConnectionInfo(channel, connectionListener); } ...
Tworzenie grupy
Jeśli chcesz, aby urządzenie, na którym działa Twoja aplikacja, było właścicielem grupy
sieć obejmującą starsze urządzenia, czyli urządzenia, które nie obsługują
Wi-Fi Direct – musisz wykonać tę samą sekwencję czynności, co w przypadku
Łączenie z grupą równorzędną, chyba że utworzysz nową
WifiP2pManager.ActionListener
za pomocą createGroup()
zamiast connect()
. Obsługa wywołania zwrotnego w tagu
WifiP2pManager.ActionListener
jest taki sam jak w tym fragmencie kodu:
Kotlin
manager.createGroup(channel, object : WifiP2pManager.ActionListener { override fun onSuccess() { // Device is ready to accept incoming connections from peers. } override fun onFailure(reason: Int) { Toast.makeText( this@WiFiDirectActivity, "P2P group creation failed. Retry.", Toast.LENGTH_SHORT ).show() } })
Java
manager.createGroup(channel, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { // Device is ready to accept incoming connections from peers. } @Override public void onFailure(int reason) { Toast.makeText(WiFiDirectActivity.this, "P2P group creation failed. Retry.", Toast.LENGTH_SHORT).show(); } });
Uwaga: jeśli wszystkie urządzenia w danej sieci obsługują Wi-Fi
Bezpośrednio, możesz użyć metody connect()
na każdym urządzeniu, ponieważ
tworzy grupę i automatycznie wybiera jej właściciela.
Po utworzeniu grupy możesz zadzwonić
requestGroupInfo()
, aby pobrać szczegółowe informacje o elementach równorzędnych
sieć, w tym nazwy urządzeń i stany połączeń.