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

Wi-Fi Direct (P2P) umożliwia urządzeniom z odpowiednim sprzętem bezpośrednie łączenie się ze sobą przez Wi-Fi bez pośredniego punktu dostępu. Za pomocą tych interfejsów API możesz wykrywać inne urządzenia i łączyć się z nimi, jeśli każde z nich obsługuje Wi-Fi P2P, a następnie komunikować się przez szybkie połączenie na odległość znacznie większą niż w przypadku połączenia Bluetooth. Jest to przydatne w przypadku aplikacji, które udostępniają dane użytkownikom, takich jak gra wieloosobowa czy aplikacja do udostępniania zdjęć.

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

  • Metody, które umożliwiają wykrywanie połączeń równorzędnych, wysyłanie do nich żądań i łączenie się z nimi, zdefiniowane w klasie WifiP2pManager.
  • detektory, które umożliwiają otrzymywanie powiadomień o powodzeniu lub niepowodzeniu wywołań metody WifiP2pManager. Podczas wywoływania metod WifiP2pManager każda metoda może otrzymać konkretny obiekt nasłuchujący przekazany jako parametr.
  • Intencje, które powiadamiają o konkretnych zdarzeniach wykrytych przez platformę Wi-Fi P2P, takich jak zerwanie połączenia lub nowo wykryte urządzenie równorzędne.

Często będziesz używać tych 3 głównych komponentów interfejsów API razem. Możesz na przykład podać WifiP2pManager.ActionListener w wywołaniu discoverPeers(), aby metody ActionListener.onSuccess() i ActionListener.onFailure() mogły Cię powiadomić. Intencja WIFI_P2P_PEERS_CHANGED_ACTION jest też transmitowana, jeśli metoda discoverPeers() wykryje, że lista urządzeń równorzędnych uległa zmianie.

Przegląd interfejsów API

Klasa WifiP2pManager udostępnia metody umożliwiające interakcję ze sprzętem Wi-Fi na urządzeniu, np. wykrywanie innych urządzeń 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 tę metodę przed wywołaniem jakiejkolwiek innej metody Wi-Fi P2P.
connect() Rozpoczyna połączenie peer-to-peer z urządzeniem o określonej konfiguracji.
cancelConnect() Anuluje wszystkie trwające negocjacje w grupie połączeń równorzędnych.
requestConnectInfo() Wysyła prośbę o informacje o połączeniu urządzenia.
createGroup() Tworzy grupę połączeń równorzędnych, w której bieżące urządzenie jest właścicielem grupy.
removeGroup() Usuwa bieżącą grupę połączeń równorzędnych.
requestGroupInfo() Wysyła prośbę o informacje o grupie połączeń równorzędnych.
discoverPeers() Inicjuje wykrywanie węzłów równorzędnych.
requestPeers() Wysyła żądanie o aktualną listę wykrytych urządzeń równorzędnych.

Metody WifiP2pManager umożliwiają przekazanie detektora, dzięki czemu platforma Wi-Fi P2P może powiadomić Twoją aktywność o stanie połączenia. Dostępne interfejsy detektorów i odpowiadające im wywołania metod WifiP2pManager, które korzystają z detektorów, opisano w tabeli 2.

Tabela 2. Detektory Wi-Fi P2P

Interfejs detektora 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 P2P API definiują intencje, które są rozsyłane, gdy wystąpią określone zdarzenia Wi-Fi P2P, np. gdy zostanie wykryte nowe urządzenie równorzędne lub gdy zmieni się stan Wi-Fi urządzenia. Możesz zarejestrować się, aby otrzymywać te intencje w aplikacji, tworząc odbiornik komunikatów, który obsługuje te intencje:

Tabela 3. Intencje Wi-Fi P2P

Intencja Opis
WIFI_P2P_CONNECTION_CHANGED_ACTION Transmisja po zmianie stanu połączenia Wi-Fi urządzenia.
WIFI_P2P_PEERS_CHANGED_ACTION Transmisja, gdy dzwonisz do: discoverPeers(). Jeśli obsługujesz tę intencję w aplikacji, zwykle wywołujesz requestPeers(), aby uzyskać zaktualizowaną listę peerów.
WIFI_P2P_STATE_CHANGED_ACTION Transmisja, gdy Wi-Fi P2P jest włączone lub wyłączone na urządzeniu.
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION Transmisja, gdy zmienią się szczegóły urządzenia, np. jego nazwa.

Tworzenie odbiornika do obsługi intencji Wi-Fi P2P

Odbiornik komunikatów umożliwia odbieranie intencji wysyłanych przez system Android, dzięki czemu aplikacja może reagować na interesujące Cię zdarzenia. Oto podstawowe czynności, które należy wykonać, aby utworzyć odbiornik do obsługi intencji Wi-Fi P2P:

  1. Utwórz klasę, która rozszerza klasę BroadcastReceiver. W konstruktorze klasy użyjesz parametrów dla WifiP2pManager, WifiP2pManager.Channel i aktywności, w której zostanie zarejestrowany ten odbiornik. Dzięki temu odbiorca może wysyłać aktualizacje do aktywności, a także w razie potrzeby ma dostęp do sprzętu Wi-Fi i kanału komunikacyjnego.

  2. W odbiorniku komunikatów sprawdź intencje, które Cię interesują, w metodzie onReceive(). Wykonaj niezbędne działania w zależności od otrzymanej intencji. Jeśli na przykład odbiornik otrzyma intencję WIFI_P2P_PEERS_CHANGED_ACTION, możesz wywołać metodę requestPeers(), aby uzyskać listę obecnie wykrytych urządzeń równorzędnych.

Poniższy kod pokazuje, jak utworzyć typowy odbiornik komunikatów. Odbiornik komunikatów przyjmuje obiekt WifiP2pManager i aktywność jako argumenty i używa tych 2 klas do odpowiedniego wykonywania potrzebnych działań, gdy odbierze 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 rozgłaszania nie są trwałe:

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

Tworzenie aplikacji Wi-Fi P2P

Tworzenie aplikacji Wi-Fi P2P obejmuje utworzenie i zarejestrowanie odbiornika komunikatów dla aplikacji, wykrywanie urządzeń równorzędnych, łączenie się z urządzeniem równorzędnym i przesyłanie danych do urządzenia równorzędnego. W sekcjach poniżej opisujemy, jak to zrobić.

Konfiguracja początkowa

Zanim zaczniesz korzystać z interfejsów API Wi-Fi P2P, musisz się upewnić, że aplikacja ma dostęp do sprzętu i że urządzenie obsługuje protokół Wi-Fi P2P. Jeśli Wi-Fi P2P jest obsługiwane, możesz uzyskać instancję WifiP2pManager, utworzyć i zarejestrować odbiornik i zacząć korzystać z interfejsów API Wi-Fi P2P.

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

    <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 uprawnień wymienionych powyżej te interfejsy API wymagają również włączenia trybu lokalizacji:

  2. Sprawdź, czy połączenie Wi-Fi P2P jest włączone i obsługiwane. Dobrym miejscem do sprawdzenia tego jest odbiornik, gdy odbiera on intencję WIFI_P2P_STATE_CHANGED_ACTION. Powiadom aktywność o stanie Wi-Fi P2P i odpowiednio zareaguj:

    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 pobierz instancję WifiP2pManager i zarejestruj aplikację w platformie Wi-Fi P2P, wywołując initialize(). Ta metoda zwraca obiekt WifiP2pManager.Channel, który służy do łączenia aplikacji z platformą Wi-Fi P2P. Należy też utworzyć instancję odbiornika z obiektami WifiP2pManager i WifiP2pManager.Channel oraz odwołaniem do aktywności. Dzięki temu odbiornik może powiadamiać aktywność o interesujących zdarzeniach i odpowiednio ją aktualizować. Umożliwia też w razie potrzeby zmianę stanu 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 transmisji w metodzie onResume() aktywności i wyrejestruj go w metodzie onPause() 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 uzyskasz WifiP2pManager.Channel i skonfigurujesz odbiornik, aplikacja będzie mogła wykonywać wywołania metod Wi-Fi P2P i odbierać intencje Wi-Fi P2P.

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

W następnych sekcjach opisano, jak wykonywać typowe działania, takie jak wykrywanie połączeń i łączenie się z innymi urządzeniami.

Odkrywanie innych użytkowników

Wywołaj discoverPeers(), aby wykryć dostępne urządzenia, które są w zasięgu i z którymi można się połączyć. Wywołanie tej funkcji jest asynchroniczne, a informacja o powodzeniu lub niepowodzeniu jest przekazywana do aplikacji za pomocą onSuccess() i onFailure(), jeśli utworzono WifiP2pManager.ActionListener. Metoda onSuccess() tylko powiadamia o powodzeniu procesu wykrywania i nie podaje żadnych informacji o wykrytych urządzeniach równorzędnych. 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 urządzenia, system wyśle komunikat systemowy WIFI_P2P_PEERS_CHANGED_ACTION, który możesz nasłuchiwać w odbiorniku, aby uzyskać listę urządzeń. Gdy aplikacja otrzyma intencję WIFI_P2P_PEERS_CHANGED_ACTION, możesz poprosić o listę wykrytych urządzeń 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 również asynchroniczna i może powiadomić aktywność, gdy lista urządzeń peer-to-peer jest dostępna, za pomocą onPeersAvailable(), która jest zdefiniowana w interfejsie WifiP2pManager.PeerListListener. Metoda onPeersAvailable() udostępnia obiekt WifiP2pDeviceList, który można iterować, aby znaleźć urządzenie, z którym można się połączyć.

Łączenie z innymi użytkownikami

Po uzyskaniu listy możliwych urządzeń równorzędnych i wybraniu urządzenia, z którym chcesz się połączyć, wywołaj metodę connect(), aby połączyć się z urządzeniem. To wywołanie metody wymaga obiektu WifiP2pConfig, który zawiera informacje o urządzeniu, z którym ma zostać nawiązane połączenie. WifiP2pManager.ActionListener może powiadomić Cię o powodzeniu lub niepowodzeniu połączenia. Poniższy kod pokazuje, 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
   }
});

Przenieś dane

Po nawiązaniu połączenia możesz przesyłać dane między urządzeniami za pomocą gniazd. Podstawowe kroki przenoszenia danych:

  1. Utwórz ServerSocket. To gniazdo czeka na połączenie z klientem na określonym porcie i blokuje je, dopóki nie zostanie nawiązane, więc wykonaj tę czynność w wątku w tle.
  2. Utwórz klienta Socket. Klient używa adresu IP i portu gniazda serwera, aby połączyć się z urządzeniem 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 klienta (za pomocą metody accept()). To wywołanie blokuje się, dopóki klient się nie połączy, więc wywołaj je w innym wątku. Po nawiązaniu połączenia urządzenie serwera może odbierać dane od klienta.

Poniższy przykład, zmodyfikowany na podstawie Wi-Fi P2P Demo, pokazuje, jak utworzyć komunikację gniazda klient-serwer i przesyłać obrazy JPEG z klienta na serwer za pomocą usługi. Aby zobaczyć pełny, działający 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ą gniazda klienta i prześlij dane. W tym przykładzie przesyłany jest plik JPEG z systemu plików urządzenia klienta.

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