Wi-Fi directo (P2P) permite que los dispositivos con el hardware adecuado se conecten directamente a cada a través de Wi-Fi sin un punto de acceso intermedio. Con estas APIs, puedes descubrir otros dispositivos y conectarse a ellos cuando cada uno de ellos admita Wi-Fi P2P, entonces se comunican a través de una conexión rápida a distancias mucho más largas que una Conexión Bluetooth. Esto es útil para las aplicaciones que comparten datos entre como un juego multijugador o una aplicación para compartir fotos.
Las API P2P Wi-Fi constan de las siguientes partes principales:
- Los métodos que te permiten descubrir, solicitar y conectarte a pares, que son
definidos en las
WifiP2pManager
. - Los objetos de escucha que te permiten recibir notificaciones sobre el éxito o fracaso de
Llamadas al método
WifiP2pManager
Cuando se llama a los métodosWifiP2pManager
, cada puede recibir un objeto de escucha específico que se pasa como parámetro. - Intents que te notifican sobre eventos específicos detectados por el P2P Wi-Fi como una conexión caída o un par recién descubierto.
A menudo, usarás estos tres componentes principales de las APIs juntos. Para
ejemplo, puedes proporcionar una
WifiP2pManager.ActionListener
a una llamada a
discoverPeers()
para que
ActionListener.onSuccess()
y
ActionListener.onFailure()
pueden enviarte notificaciones. R
WIFI_P2P_PEERS_CHANGED_ACTION
el intent también se transmite si el método discoverPeers()
descubre que el
la lista de apps similares cambió.
Descripción general de la API
La clase WifiP2pManager
proporciona métodos que te permiten interactuar con el
Hardware Wi-Fi en tu dispositivo para realizar acciones como detectar pares y conectarse a ellos
Las siguientes acciones están disponibles:
Tabla 1: Métodos P2P Wi-Fi
Método | Descripción |
initialize()
|
Registra la aplicación con el marco de trabajo Wi-Fi. Llamar antes llamando a cualquier otro método P2P Wi-Fi. |
connect()
|
Inicia una conexión entre pares con un dispositivo que tiene la configuración especificada. |
cancelConnect()
|
Cancela cualquier negociación de grupo entre pares que haya en curso. |
requestConnectInfo()
|
Solicita la información de conexión de un dispositivo. |
createGroup()
|
Crea un grupo entre pares con el dispositivo actual como propietario del grupo. |
removeGroup()
|
Elimina el grupo entre pares actual. |
requestGroupInfo()
|
Solicita información del grupo entre pares. |
discoverPeers()
|
Inicia el descubrimiento de pares. |
requestPeers()
|
Solicita la lista actual de pares detectados. |
Los métodos WifiP2pManager
permiten
pasas un objeto de escucha, de modo que el framework Wi-Fi P2P pueda notificar
del estado de una llamada. Las interfaces de los objetos de escucha disponibles y sus respectivos
Llamadas de método WifiP2pManager
que usan los objetos de escucha se describen en la tabla 2.
Tabla 2: Objetos de escucha P2P Wi-Fi
Interfaz de objeto de escucha | Acciones asociadas |
WifiP2pManager.ActionListener
|
connect() , cancelConnect() ,
createGroup() , removeGroup() y
discoverPeers()
|
WifiP2pManager.ChannelListener
|
initialize()
|
WifiP2pManager.ConnectionInfoListener
|
requestConnectInfo()
|
WifiP2pManager.GroupInfoListener
|
requestGroupInfo()
|
WifiP2pManager.PeerListListener
|
requestPeers()
|
Las APIs de P2P Wi-Fi definen los intents que se transmiten cuando se cumplen suceden eventos, como cuando se descubre un nuevo par o cuando la conexión Wi-Fi de un dispositivo cambios de estado. Puedes registrarte para recibir estos intents en tu aplicación crear un receptor de emisión que maneje estos intents:
Tabla 3: Intents de P2P Wi-Fi
Intent | Descripción |
WIFI_P2P_CONNECTION_CHANGED_ACTION
|
Transmite cuando cambia el estado de la conexión Wi-Fi del dispositivo. |
WIFI_P2P_PEERS_CHANGED_ACTION
|
Anunciar cuando llames a discoverPeers() Por lo general,
llama a requestPeers() para obtener una lista actualizada de los pares si
manejar este intent en tu aplicación.
|
WIFI_P2P_STATE_CHANGED_ACTION
|
Transmite cuando P2P Wi-Fi se habilita o se inhabilita en el dispositivo. |
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
|
Transmite cuando los detalles de un dispositivo cambian, como el nombre. |
Crear un receptor de emisión para intents P2P Wi-Fi
Un receptor de emisión te permite recibir los intents que transmite el dispositivo Android para que tu aplicación pueda responder a los eventos que te interesan en el que te etiquetaron. Pasos básicos para crear un receptor de emisión que controle P2P Wi-Fi intents son la siguiente:
Crea una clase que extienda el
BroadcastReceiver
. Para la clase constructor, usarás parámetros para elWifiP2pManager
,WifiP2pManager.Channel
y la actividad que este receptor de emisión realizará en los que debe registrarse. Esto permite que el receptor de emisión envíe actualizaciones al así como al hardware de Wi-Fi y una conexión canal si es necesario.En el receptor de emisión, busca los intents que te interesan. en la
onReceive()
. Realiza las acciones necesarias según el intent que se recibidos. Por ejemplo, si el receptor de emisión recibe unWIFI_P2P_PEERS_CHANGED_ACTION
, puedes llamar alrequestPeers()
para obtener una lista de los pares detectados actualmente.
El siguiente código muestra cómo crear un receptor de emisión típico. El
El receptor de emisión toma un objeto WifiP2pManager
y una actividad como argumentos
y usa estas dos clases para llevar a cabo adecuadamente las acciones necesarias cuando
el receptor de emisión recibe un intent:
Kotlin
/** * A BroadcastReceiver that notifies of important Wi-Fi p2p events. */ class WiFiDirectBroadcastReceiver( private val manager: WifiP2pManager, private val channel: WifiP2pManager.Channel, private val activity: MyWifiActivity ) : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val action: String = intent.action when (action) { WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> { // Check to see if Wi-Fi is enabled and notify appropriate activity } WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> { // Call WifiP2pManager.requestPeers() to get a list of current peers } WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> { // Respond to new connection or disconnections } WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION -> { // Respond to this device's wifi state changing } } } }
Java
/** * A BroadcastReceiver that notifies of important Wi-Fi p2p events. */ public class WiFiDirectBroadcastReceiver extends BroadcastReceiver { private WifiP2pManager manager; private Channel channel; private MyWiFiActivity activity; public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel, MyWifiActivity activity) { super(); this.manager = manager; this.channel = channel; this.activity = activity; } @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { // Check to see if Wi-Fi is enabled and notify appropriate activity } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) { // Call WifiP2pManager.requestPeers() to get a list of current peers } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) { // Respond to new connection or disconnections } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) { // Respond to this device's wifi state changing } } }
En dispositivos que ejecutan Android 10 y versiones posteriores, se aplica lo siguiente a los siguientes intents de transmisión: antiadherente:
WIFI_P2P_CONNECTION_CHANGED_ACTION
- Las aplicaciones pueden usar
requestConnectionInfo()
,requestNetworkInfo()
: orequestGroupInfo()
para recuperar la información de la conexión actual. WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
- Las aplicaciones pueden usar
requestDeviceInfo()
para recuperar la información de la conexión actual.
Crear una aplicación P2P Wi-Fi
Crear una aplicación P2P Wi-Fi implica crear y registrar una transmisión para tu aplicación, descubrir pares, conectarte con un par y la transferencia de datos a un par. En las siguientes secciones, se describe cómo hacerlo.
Configuración inicial
Antes de usar las APIs P2P Wi-Fi, debes asegurarte de que tu aplicación pueda
acceder al hardware y que el dispositivo sea compatible con el protocolo Wi-Fi P2P. Si
Wi-Fi P2P es compatible, puedes obtener una instancia de WifiP2pManager
, crear
registrar tu receptor de emisión
y comenzar a usar las APIs Wi-Fi P2P.
Solicitar permiso para usar el hardware de Wi-Fi en el dispositivo y declarar que tu aplicación tenga la versión de SDK mínima correcta en el SDK manifiesto:
<uses-sdk android:minSdkVersion="14" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 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: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" />
Además de los permisos anteriores, las siguientes APIs también requieren los permisos de ubicación Modo que se habilitará:
Verifica que P2P Wi-Fi esté activado y sea compatible. Un buen lugar para comprobarlo es en tu receptor de emisión cuando recibe el
WIFI_P2P_STATE_CHANGED_ACTION
. Cómo notificar tu actividad del P2P Wi-Fi y reaccionar en consecuencia:Kotlin
override fun onReceive(context: Context, intent: Intent) { ... val action: String = intent.action when (action) { WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> { val state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1) when (state) { WifiP2pManager.WIFI_P2P_STATE_ENABLED -> { // Wifi P2P is enabled } else -> { // Wi-Fi P2P is not enabled } } } } ... }
Java
@Override public void onReceive(Context context, Intent intent) { ... String action = intent.getAction(); if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) { int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1); if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) { // Wifi P2P is enabled } else { // Wi-Fi P2P is not enabled } } ... }
En la sección de
onCreate()
obtén una instancia deWifiP2pManager
y registra tu aplicación con el framework Wi-Fi P2P llamando ainitialize()
. Este método devuelve unWifiP2pManager.Channel
, que se usa para conectar tu aplicación al Framework P2P Wi-Fi. También debes crear una instancia de tu transmisión con los objetosWifiP2pManager
yWifiP2pManager.Channel
junto con una referencia a tu actividad. Esto permite que tu receptor de emisión notificar a tu actividad sobre eventos interesantes y actualizarla en consecuencia. También te permite manipular el estado de Wi-Fi del dispositivo, si es necesario:Kotlin
val manager: WifiP2pManager? by lazy(LazyThreadSafetyMode.NONE) { getSystemService(Context.WIFI_P2P_SERVICE) as WifiP2pManager? } var channel: WifiP2pManager.Channel? = null var receiver: BroadcastReceiver? = null override fun onCreate(savedInstanceState: Bundle?) { ... channel = manager?.initialize(this, mainLooper, null) channel?.also { channel -> receiver = WiFiDirectBroadcastReceiver(manager, channel, this) } }
Java
WifiP2pManager manager; Channel channel; BroadcastReceiver receiver; ... @Override protected void onCreate(Bundle savedInstanceState){ ... manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE); channel = manager.initialize(this, getMainLooper(), null); receiver = new WiFiDirectBroadcastReceiver(manager, channel, this); ... }
Crea un filtro de intents y agrega los mismos intents que tu receptor de emisión comprueba lo siguiente:
Kotlin
val intentFilter = IntentFilter().apply { addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION) addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION) addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION) addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION) }
Java
IntentFilter intentFilter; ... @Override protected void onCreate(Bundle savedInstanceState){ ... intentFilter = new IntentFilter(); intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION); ... }
Registra el receptor de emisión en
onResume()
de tu actividad y cancelar su registro en el métodoonPause()
de tu actividad:Kotlin
/* register the broadcast receiver with the intent values to be matched */ override fun onResume() { super.onResume() receiver?.also { receiver -> registerReceiver(receiver, intentFilter) } } /* unregister the broadcast receiver */ override fun onPause() { super.onPause() receiver?.also { receiver -> unregisterReceiver(receiver) } }
Java
/* register the broadcast receiver with the intent values to be matched */ @Override protected void onResume() { super.onResume(); registerReceiver(receiver, intentFilter); } /* unregister the broadcast receiver */ @Override protected void onPause() { super.onPause(); unregisterReceiver(receiver); }
Cuando recibes un
WifiP2pManager.Channel
y configuras una transmisión receptor, tu aplicación puede realizar llamadas de método P2P Wi-Fi y recibir Wi-Fi Intents de P2P.Implementa tu aplicación usando las funciones P2P Wi-Fi llamando al métodos en
WifiP2pManager
.
Las siguientes secciones describen cómo realizar acciones comunes, como descubrir y de conectarse con pares.
Cómo detectar pares
Llama a discoverPeers()
para detectar pares disponibles que se encuentren dentro del rango y disponibles.
para la conexión. La llamada a esta función es asíncrona y una llamada exitosa o
se comunica una falla a tu aplicación con onSuccess()
y onFailure()
si creaste un WifiP2pManager.ActionListener
. Solo el método onSuccess()
te notifica que el proceso de descubrimiento se realizó correctamente y no proporciona ninguna
información sobre los pares reales que descubrió, si los hubiera. Lo siguiente
de muestra de código muestra cómo configurarlo.
Kotlin
manager?.discoverPeers(channel, object : WifiP2pManager.ActionListener { override fun onSuccess() { ... } override fun onFailure(reasonCode: Int) { ... } })
Java
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() { @Override public void onSuccess() { ... } @Override public void onFailure(int reasonCode) { ... } });
Si el proceso de detección se realiza correctamente y detecta pares, el sistema transmite
WIFI_P2P_PEERS_CHANGED_ACTION
, que puedes escuchar en una transmisión
para obtener una lista de pares. Cuando tu aplicación recibe el
WIFI_P2P_PEERS_CHANGED_ACTION
, puedes solicitar una lista de los métodos
intercambio de tráfico con requestPeers()
. En el siguiente código, se muestra cómo realizar esta configuración.
Kotlin
override fun onReceive(context: Context, intent: Intent) { val action: String = intent.action when (action) { ... WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> { manager?.requestPeers(channel) { peers: WifiP2pDeviceList? -> // Handle peers list } } ... } }
Java
PeerListListener myPeerListListener; ... 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 (manager != null) { manager.requestPeers(channel, myPeerListListener); } }
El método requestPeers()
también es asíncrono y puede notificar tu actividad.
cuando una lista de pares está disponible con
onPeersAvailable()
,
que se define en la interfaz WifiP2pManager.PeerListListener
. El
El método onPeersAvailable()
te proporciona un
WifiP2pDeviceList
, que
puedes iterar para encontrar el intercambio
de tráfico al que debes conectarte.
Cómo conectarse a pares
Una vez que tengas una lista de posibles apps similares y selecciones un dispositivo para conectar
al método connect()
para conectarte al dispositivo. Esta llamada de método
requiere un WifiP2pConfig
que contiene información sobre el dispositivo al que se conectará.
WifiP2pManager.ActionListener
puede notificarte si la conexión se estableció correctamente o
falla. En el siguiente código, se muestra cómo crear una conexión con un dispositivo.
Kotlin
val device: WifiP2pDevice = ... val config = WifiP2pConfig() config.deviceAddress = device.deviceAddress channel?.also { channel -> manager?.connect(channel, config, object : WifiP2pManager.ActionListener { override fun onSuccess() { //success logic } override fun onFailure(reason: Int) { //failure logic } } )}
Java
//obtain a peer from the WifiP2pDeviceList WifiP2pDevice device; WifiP2pConfig config = new WifiP2pConfig(); config.deviceAddress = device.deviceAddress; manager.connect(channel, config, new ActionListener() { @Override public void onSuccess() { //success logic } @Override public void onFailure(int reason) { //failure logic } });
Cómo transferir datos
Una vez establecida la conexión, puedes transferir datos entre los dispositivos con enchufes. A continuación, se muestran los pasos básicos para transferir datos:
- Crea un
ServerSocket
. Este enchufe espera una conexión de un cliente en un puerto especificado y bloquea hasta que hazlo en un subproceso en segundo plano. - Crea un cliente
Socket
. El cliente usa el Dirección IP y puerto del socket de servidor para conectarse al dispositivo de servidor. - Envía datos desde el cliente al servidor. Cuando el socket del cliente se ejecuta correctamente se conecta al socket de servidor, puedes enviar datos del cliente al un servidor web con flujos de bytes.
- El socket de servidor espera una conexión del cliente (con el
accept()
). Esta llamada hasta que se conecte un cliente, así que llama a este método en otro subproceso. Cuando un elemento de conexión, el dispositivo del servidor puede recibir los datos del cliente.
El siguiente ejemplo, modificado del P2P Wi-Fi Demostración, se muestra cómo crear una comunicación de socket cliente-servidor y transferir Imágenes JPEG de un cliente a un servidor con un servicio. Para obtener una experiencia ejemplo, compile y ejecute la demostración.
Kotlin
class FileServerAsyncTask( private val context: Context, private var statusText: TextView ) : AsyncTask<Void, Void, String?>() { override fun doInBackground(vararg params: Void): String? { /** * Create a server socket. */ val serverSocket = ServerSocket(8888) return serverSocket.use { /** * Wait for client connections. This call blocks until a * connection is accepted from a client. */ val client = serverSocket.accept() /** * If this code is reached, a client has connected and transferred data * Save the input stream from the client as a JPEG file */ val f = File(Environment.getExternalStorageDirectory().absolutePath + "/${context.packageName}/wifip2pshared-${System.currentTimeMillis()}.jpg") val dirs = File(f.parent) dirs.takeIf { it.doesNotExist() }?.apply { mkdirs() } f.createNewFile() val inputstream = client.getInputStream() copyFile(inputstream, FileOutputStream(f)) serverSocket.close() f.absolutePath } } private fun File.doesNotExist(): Boolean = !exists() /** * Start activity that can handle the JPEG image */ override fun onPostExecute(result: String?) { result?.run { statusText.text = "File copied - $result" val intent = Intent(android.content.Intent.ACTION_VIEW).apply { setDataAndType(Uri.parse("file://$result"), "image/*") } context.startActivity(intent) } } }
Java
public static class FileServerAsyncTask extends AsyncTask { private Context context; private TextView statusText; public FileServerAsyncTask(Context context, View statusText) { this.context = context; this.statusText = (TextView) statusText; } @Override protected String doInBackground(Void... params) { try { /** * Create a server socket and wait for client connections. This * call blocks until a connection is accepted from a client */ ServerSocket serverSocket = new ServerSocket(8888); Socket client = serverSocket.accept(); /** * If this code is reached, a client has connected and transferred data * Save the input stream from the client as a JPEG file */ final File f = new File(Environment.getExternalStorageDirectory() + "/" + context.getPackageName() + "/wifip2pshared-" + System.currentTimeMillis() + ".jpg"); File dirs = new File(f.getParent()); if (!dirs.exists()) dirs.mkdirs(); f.createNewFile(); InputStream inputstream = client.getInputStream(); copyFile(inputstream, new FileOutputStream(f)); serverSocket.close(); return f.getAbsolutePath(); } catch (IOException e) { Log.e(WiFiDirectActivity.TAG, e.getMessage()); return null; } } /** * Start activity that can handle the JPEG image */ @Override protected void onPostExecute(String result) { if (result != null) { statusText.setText("File copied - " + result); Intent intent = new Intent(); intent.setAction(android.content.Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse("file://" + result), "image/*"); context.startActivity(intent); } } }
En el cliente, conéctate al socket de servidor con un socket de cliente y realiza la transferencia de datos no estructurados. En este ejemplo, se transfiere un archivo JPEG en el sistema de archivos del dispositivo del cliente.
Kotlin
val context = applicationContext val host: String val port: Int val len: Int val socket = Socket() val buf = ByteArray(1024) ... try { /** * Create a client socket with the host, * port, and timeout information. */ socket.bind(null) socket.connect((InetSocketAddress(host, port)), 500) /** * Create a byte stream from a JPEG file and pipe it to the output stream * of the socket. This data is retrieved by the server device. */ val outputStream = socket.getOutputStream() val cr = context.contentResolver val inputStream: InputStream = cr.openInputStream(Uri.parse("path/to/picture.jpg")) while (inputStream.read(buf).also { len = it } != -1) { outputStream.write(buf, 0, len) } outputStream.close() inputStream.close() } catch (e: FileNotFoundException) { //catch logic } catch (e: IOException) { //catch logic } finally { /** * Clean up any open sockets when done * transferring or if an exception occurred. */ socket.takeIf { it.isConnected }?.apply { close() } }
Java
Context context = this.getApplicationContext(); String host; int port; int len; Socket socket = new Socket(); byte buf[] = new byte[1024]; ... try { /** * Create a client socket with the host, * port, and timeout information. */ socket.bind(null); socket.connect((new InetSocketAddress(host, port)), 500); /** * Create a byte stream from a JPEG file and pipe it to the output stream * of the socket. This data is retrieved by the server device. */ OutputStream outputStream = socket.getOutputStream(); ContentResolver cr = context.getContentResolver(); InputStream inputStream = null; inputStream = cr.openInputStream(Uri.parse("path/to/picture.jpg")); while ((len = inputStream.read(buf)) != -1) { outputStream.write(buf, 0, len); } outputStream.close(); inputStream.close(); } catch (FileNotFoundException e) { //catch logic } catch (IOException e) { //catch logic } /** * Clean up any open sockets when done * transferring or if an exception occurred. */ finally { if (socket != null) { if (socket.isConnected()) { try { socket.close(); } catch (IOException e) { //catch logic } } } }