Omówienie Wi-Fi Direct (peer-to-peer lub P2P)

Wi-Fi Direct (P2P) pozwala urządzeniom z odpowiednim sprzętem łączyć się ze sobą bezpośrednio przez Wi-Fi bez pośredniego punktu dostępu. Dzięki tym interfejsom API możesz wykrywać inne urządzenia i łączyć się z nimi, gdy każde z nich obsługuje Wi-Fi P2P, a następnie komunikować się za pomocą szybkiego połączenia na odległość znacznie większą niż w przypadku połączenia Bluetooth. Przydaje się to w aplikacjach, które udostępniają dane między użytkownikami, np. w grach wieloosobowych lub aplikacjach do udostępniania zdjęć.

Interfejsy API sieci Wi-Fi P2P składają się z tych głównych części:

  • Metody umożliwiające wykrywanie połączeń równorzędnych, wysyłanie żądań dotyczących ich i łączenie się z nimi, które są zdefiniowane w klasie WifiP2pManager.
  • Detektory, które pozwalają otrzymywać powiadomienia o udanych lub nieudanych wywołaniach metod WifiP2pManager. Gdy wywołują metody WifiP2pManager, każda z nich może otrzymywać określony detektor przekazywany jako parametr.
  • Intencje powiadamiające o konkretnych zdarzeniach wykrytych przez strukturę P2P sieci Wi-Fi, takich jak utrata połączenia lub nowo wykryte połączenie równorzędne.

Tych 3 głównych komponentów interfejsów API często używa się razem. Możesz na przykład dodać WifiP2pManager.ActionListener do wywołania discoverPeers(), aby metody ActionListener.onSuccess() i ActionListener.onFailure() mogły Cię powiadomić. Intencja WIFI_P2P_PEERS_CHANGED_ACTION jest też wysyłana, gdy metoda discoverPeers() wykryje, że lista elementów porównawczych uległa zmianie.

Omówienie interfejsu API

Klasa WifiP2pManager udostępnia metody, które umożliwiają korzystanie ze sprzętu Wi-Fi na urządzeniu w takich sytuacjach jak wykrywanie rówieśników i łączenie się z nimi. Dostępne są te opcje:

Tabela 1. Metody Wi-Fi P2P

Metoda Opis
initialize() Rejestruje aplikację w platformie Wi-Fi. Wywołaj go przed wywołaniem jakiejkolwiek innej metody P2P Wi-Fi.
connect() Uruchamia połączenie peer-to-peer z urządzeniem o określonej konfiguracji.
cancelConnect() Anuluje wszystkie trwające negocjacje typu „peer-to-peer”.
requestConnectInfo() Żąda informacji o połączeniu urządzenia.
createGroup() Tworzy grupę równorzędną, której właścicielem jest obecne urządzenie.
removeGroup() Usuwa bieżącą grupę równorzędną.
requestGroupInfo() Żąda informacji o grupie peer-to-peer.
discoverPeers() Rozpoczyna odkrywanie aplikacji równorzędnych.
requestPeers() Pyta o bieżącą listę wykrytych elementów równorzędnych.

Metody WifiP2pManager umożliwiają przekazanie detektora, dzięki czemu platforma P2P sieci Wi-Fi może powiadamiać Twoją aktywność o stanie wywołania. Dostępne interfejsy detektorów oraz odpowiadające im wywołania metod WifiP2pManager, które używają detektorów, zostały opisane w tabeli 2.

Tabela 2. Detektory Wi-Fi P2P

Interfejs słuchacza Powiązane działania
WifiP2pManager.ActionListener connect(), cancelConnect(), createGroup(), removeGroup() i discoverPeers()
WifiP2pManager.ChannelListener initialize()
WifiP2pManager.ConnectionInfoListener requestConnectInfo()
WifiP2pManager.GroupInfoListener requestGroupInfo()
WifiP2pManager.PeerListListener requestPeers()

Interfejsy Wi-Fi API P2P definiują intencje, które są transmitowane po wystąpieniu określonych zdarzeń Wi-Fi P2P, na przykład po wykryciu nowego połączenia równorzędnego lub zmianie stanu połączenia Wi-Fi urządzenia. Aby zarejestrować się do otrzymywania tych intencji w aplikacji, utwórz odbiornik, który obsługuje te intencje:

Tabela 3. Intencje P2P w sieci Wi-Fi

Zamiar Opis
WIFI_P2P_CONNECTION_CHANGED_ACTION Rozgłaszaj po zmianie stanu połączenia Wi-Fi na urządzeniu.
WIFI_P2P_PEERS_CHANGED_ACTION Przesyłaj powiadomienia, gdy dzwonisz pod numer discoverPeers(). Jeśli obsługujesz tę intencję w aplikacji, zwykle wywołujesz requestPeers(), aby uzyskać zaktualizowaną listę elementów równorzędnych.
WIFI_P2P_STATE_CHANGED_ACTION Przesyłaj, gdy na urządzeniu jest włączona lub wyłączona sieć Wi-Fi P2P.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION Wysyłaj po zmianie szczegółów urządzenia (np. jego nazwy).

Utwórz odbiornik dla intencji Wi-Fi P2P

Odbiornik transmisji umożliwia odbieranie intencji transmitowanych przez system Android, dzięki czemu aplikacja może odpowiadać na zdarzenia, które Cię interesują. Podstawowe kroki, które trzeba wykonać, by utworzyć odbiornik do obsługi intencji P2P sieci Wi-Fi:

  1. Utwórz klasę, która rozszerza klasę BroadcastReceiver. W konstruktorze klasy użyjesz parametrów WifiP2pManager i WifiP2pManager.Channel oraz działania, w którym ten odbiornik będzie zarejestrowany. Dzięki temu odbiornik może wysyłać aktualizacje o aktywności, a w razie potrzeby mieć dostęp do sprzętu Wi-Fi i kanału komunikacyjnego.

  2. W odbiorniku wybierz interesujące Cię intencje w przypadku metody onReceive(). Wykonaj odpowiednie działania zależnie od otrzymanej intencji. Jeśli na przykład odbiornik odbiera intencję WIFI_P2P_PEERS_CHANGED_ACTION, możesz wywołać metodę requestPeers(), by uzyskać listę obecnie wykrytych elementów równorzędnych.

Poniższy kod pokazuje, jak utworzyć typowy odbiornik. Odbiornik sygnału przyjmuje obiekt WifiP2pManager i działanie jako argumenty, a potem używa tych 2 klas do odpowiedniego wykonywania niezbędnych działań, gdy odbiornik odbiera intencję:

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
       }
   }
}

Na urządzeniach z Androidem 10 lub nowszym te intencje przesyłania nie są przyklejone:

WIFI_P2P_CONNECTION_CHANGED_ACTION
Aplikacje mogą pobierać informacje o bieżącym połączeniu za pomocą requestConnectionInfo(), requestNetworkInfo() lub requestGroupInfo().
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
Aplikacje mogą używać requestDeviceInfo() do pobierania bieżących informacji o połączeniu.

Utwórz aplikację Wi-Fi P2P

Utworzenie aplikacji Wi-Fi P2P obejmuje utworzenie i zarejestrowanie odbiornika dla aplikacji, wykrycie peera, nawiązanie połączenia z peerem i przesłanie do niego danych. Jak to zrobić, dowiesz się w sekcjach poniżej.

Konfiguracja początkowa

Przed użyciem interfejsów API sieci Wi-Fi P2P upewnij się, że aplikacja ma dostęp do sprzętu, a urządzenie obsługuje protokół Wi-Fi P2P. Jeśli sieć Wi-Fi P2P jest obsługiwana, możesz uzyskać instancję WifiP2pManager, utworzyć i zarejestrować odbiornik, a następnie zacząć korzystać z interfejsów API sieci Wi-Fi P2P.

  1. Poproś o pozwolenie na korzystanie ze sprzętu Wi-Fi na urządzeniu i zadeklaruj, że aplikacja ma prawidłową minimalną wersję pakietu SDK w pliku manifestu Androida:

    <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" />
    

    Oprócz poprzednich uprawnień te interfejsy API wymagają też włączenia trybu lokalizacji:

  2. Sprawdź, czy sieć Wi-Fi P2P jest włączona i obsługiwana. Możesz to sprawdzić w odbiorniku, gdy otrzyma intencję WIFI_P2P_STATE_CHANGED_ACTION. Powiadomi Cię o aktywności stanu P2P sieci Wi-Fi i zareaguj odpowiednio:

    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
       }
    }
    ...
    }
    
  3. W metodzie onCreate() aktywności uzyskaj instancję WifiP2pManager i zarejestruj aplikację za pomocą platformy Wi-Fi P2P, wywołując initialize(). Ta metoda zwraca WifiP2pManager.Channel, który służy do połączenia aplikacji ze platformą Wi-Fi P2P. Należy też utworzyć instancję odbiornika z obiektami WifiP2pManager i WifiP2pManager.Channel wraz z odwołaniem do Twojej aktywności. Dzięki temu Twój odbiornik będzie mógł powiadamiać Cię o interesujących wydarzeniach i odpowiednio aktualizować te informacje. W razie potrzeby umożliwia też manipulowanie stanem Wi-Fi urządzenia:

    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);
       ...
    }
    
  4. Utwórz filtr intencji i dodaj te same intencje, które sprawdza odbiornik:

    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);
       ...
    }
    
  5. Zarejestruj odbiornik w metodzie onResume() swojej aktywności i wyrejestruj go w metodzie onPause() swojej aktywności:

    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);
    }
    
  6. Gdy otrzymasz WifiP2pManager.Channel i skonfigurujesz odbiornik, aplikacja będzie mogła nawiązywać połączenia metodą P2P Wi-Fi i odbierać intencje Wi-Fi.

  7. Wdróż aplikację, korzystając z funkcji Wi-Fi P2P, wywołując te metody w WifiP2pManager.

W kolejnych sekcjach dowiesz się, jak wykonywać typowe czynności, takie jak znajdowanie elementów równorzędnych i łączenie się z nimi.

Znajdź aplikacje z grupy porównawczej

Wywołaj discoverPeers(), aby wykryć dostępne połączenia równorzędne, które znajdują się w zasięgu i można nawiązać połączenie. Wywołanie tej funkcji jest asynchroniczne, a o sukcesie lub niepowodzeniu aplikacji za pomocą funkcji onSuccess() i onFailure(), jeśli został utworzony WifiP2pManager.ActionListener. Metoda onSuccess() informuje tylko o tym, że proces wykrywania się udał, i nie zawiera żadnych informacji o wykrytych elementach równorzędnych (jeśli takie wystąpiły). Poniższy przykładowy kod pokazuje, jak to skonfigurować.

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) {
       ...
   }
});

Jeśli proces wykrywania się powiedzie i wykryje elementy równorzędne, system rozgłasza intencję WIFI_P2P_PEERS_CHANGED_ACTION, której możesz nasłuchiwać w odbiorniku, aby uzyskać listę takich elementów. Gdy aplikacja otrzyma intencję WIFI_P2P_PEERS_CHANGED_ACTION, możesz zażądać listy wykrytych elementów równorzędnych za pomocą requestPeers(). Poniższy kod pokazuje, jak to skonfigurować.

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);
   }
}

Metoda requestPeers() jest też asynchroniczna i może powiadamiać Twoją aktywność o dostępności listy elementów równorzędnych za pomocą funkcji onPeersAvailable() zdefiniowanej w interfejsie WifiP2pManager.PeerListListener. Metoda onPeersAvailable() udostępnia tabelę WifiP2pDeviceList, którą możesz iterować, aby znaleźć element równorzędny, z którym chcesz się połączyć.

Połącz się z innymi wydawcami

Po uzyskaniu listy możliwych elementów równorzędnych i wybraniu urządzenia, z którym chcesz się połączyć, wywołaj metodę connect(), aby nawiązać połączenie. To wywołanie metody wymaga obiektu WifiP2pConfig zawierającego informacje o urządzeniu, z którym ma się połączyć. WifiP2pManager.ActionListener może powiadamiać Cię o udanej lub nieudanej próbie połączenia. Ten kod ilustruje, jak utworzyć połączenie z urządzeniem.

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
   }
});

Przenoszenie danych

Po nawiązaniu połączenia będzie można przesyłać dane między urządzeniami z gniazdkami. Podstawowe kroki przenoszenia danych:

  1. Utwórz ServerSocket. To gniazdo czeka na połączenie z klientem na określonym porcie i blokuje, dopóki nie wystąpi, więc zrób to w wątku w tle.
  2. Utwórz klienta Socket. Klient łączy się z serwerem przy użyciu adresu IP i portu gniazda serwera.
  3. Wysyłanie danych z klienta na serwer. Gdy gniazdo klienta połączy się z gniazdem serwera, możesz wysyłać dane z klienta do serwera za pomocą strumieni bajtów.
  4. Gniazdo serwera czeka na połączenie z klientem (w przypadku metody accept()). To wywołanie blokuje się do czasu połączenia z klientem, więc wywołaj je w innym wątku. W przypadku połączenia serwer może odebrać dane od klienta.

Poniższy przykład, modyfikowany na podstawie prezentacji Wi-Fi P2P, pokazuje, jak utworzyć komunikację typu klient-serwer i przesłać obrazy JPEG z klienta na serwer z usługą. Aby uzyskać pełny przykład, skompiluj i uruchom wersję demonstracyjną.

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);
       }
   }
}

Na kliencie połącz się z gniazdem serwera za pomocą tego gniazda i prześlij dane. W tym przykładzie plik JPEG zostanie przesłany do systemu plików urządzenia klienckiego.

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
           }
       }
   }
}