📶 WiFi P2P (Direct)Destekleyen cihazlar için android WiFi P2P bağlantısı
🏗️ WiFi P2P Activity Oluşturma
👨💻 WiFi P2P Sınıfını Kodlama
Kotlin Java
Copy class WifiP2pActivity : AppCompatActivity () {
/**
* WiFi değişikliklerinde receiver'ı çalıştırma
*/
private lateinit var manager: WifiP2pManager
/**
* WiFi P2P Framework'ü ile uygulamamıza bağlanmayı sağlayacak obje
*/
private lateinit var channel: WifiP2pManager .Channel
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
setContentView (R.layout.activity_wi_fi_direct)
initWifiP2p ()
}
private fun initWifiP2p (): Unit {
manager = getSystemService (Context.WIFI_P2P_SERVICE) as WifiP2pManager
channel = manager. initialize ( this , mainLooper, null )
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// getWifiP2PPermissions()
}
}
// ...
}
Copy public class WiFiDirectActivity extends AppCompatActivity {
public static final String TAG = WiFiDirectActivity . class . getSimpleName ();
private static final int PRC_ACCES_FINE_LOCATION = 1 ;
private final IntentFilter wifiFilter = new IntentFilter() ;
WifiP2pManager manager;
Channel channel;
BroadcastReceiver wifiReceiver;
@ Override
protected void onCreate ( Bundle savedInstanceState) {
super . onCreate (savedInstanceState);
setContentView( R . layout . activity_wifi_direct ) ;
initWifiFilter() ;
initDependences() ;
}
/**
* Wifi alıcısına aktarılacak broadcast türlerini belirleme
*/
private void initWifiFilter () {
wifiFilter . addAction ( WifiP2pManager . WIFI_P2P_STATE_CHANGED_ACTION );
wifiFilter . addAction ( WifiP2pManager . WIFI_P2P_PEERS_CHANGED_ACTION );
wifiFilter . addAction ( WifiP2pManager . WIFI_P2P_CONNECTION_CHANGED_ACTION );
wifiFilter . addAction ( WifiP2pManager . WIFI_P2P_THIS_DEVICE_CHANGED_ACTION );
}
private void initDependences () {
// WiFi değişikliklerinde reciever'ı çalıştırma
manager = (WifiP2pManager) getSystemService( Context . WIFI_P2P_SERVICE ) ;
Objects . requireNonNull (manager);
// Wi-Fi P2P Frameworkü ile uygulamamıza bağlanmayı sağlayacak obje
channel = manager . initialize ( this , getMainLooper() , null );
// İzinleri alma alanı bir alt aşamadadır
if ( Build . VERSION . SDK_INT >= Build . VERSION_CODES . M ) {
getRequiredPermissions() ;
}
}
// ...
}
💎 WiFi P2P Durumları
🧐 Intent Filter 🕊️ Tetiklenme Sebebi Cihazın WiFi bağlantısının durumu değişikliği
Bağlanacak cihazları discoverPeers()
metodu ile keşfetmek istediğimiz zaman tetiklenir. Genellikle requestPeers()
metodu ile eşleşen cihazları alma amaçlı kullanılır.
WiFi P2P'in aktifliğinin değişmesi (açık / kapalı)
Cihaz detaylarının (örn: ismi) değişmesi
👮♂️ Gerekli İzinlerin Alınması
📝 Manifest Dosyasına İzinleri Kaydetme
Manifest dosyasının içeriğine aşağıdaki kodu ekleyin
Copy <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.nsdchat"
...
<!-- Wi-Fi Direct (P2P) bağlantısı için eklendi -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
...
🧰 API için Ek Olarak Gereken İzinler
Aşağıdaki metotlar Location Mode (Konum Hizmeti) özelliğinin aktif olmasına da ihtiyaç duyar
👨💻 Kod Tarafında İzin İsteme
Kotlin Java
Copy val PRC_ACCESS_FINE_LOCATION = 1
@RequiresApi (Build.VERSION_CODES.M)
private fun getWifiP2PPermissions () {
if ( ! hasWifiP2PPermission ()) {
requestWifiP2PPermissions ()
}
}
@RequiresApi (Build.VERSION_CODES.M)
private fun hasWifiP2PPermission (): Boolean {
return checkSelfPermission (Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED
}
@RequiresApi (Build.VERSION_CODES.M)
private fun requestWifiP2PPermissions () {
requestPermissions (
arrayOf (Manifest.permission.ACCESS_FINE_LOCATION),
PRC_ACCESS_FINE_LOCATION
)
}
override fun onRequestPermissionsResult (
requestCode: Int ,
permissions: Array < out String >,
grantResults: IntArray
) {
super . onRequestPermissionsResult (requestCode, permissions, grantResults)
when (requestCode) {
PRC_ACCESS_FINE_LOCATION ->
if (grantResults[ 0 ] != PackageManager.PERMISSION_GRANTED) {
Toast. makeText ( this , "Konum izni gereklidir" , Toast.LENGTH_SHORT). show ()
finish ()
}
}
}
Copy /**
* Wi-Fi P2P için gerekli izinleri alma
*/
@ RequiresApi (api = Build . VERSION_CODES . M )
private void getRequiredPermissions() {
if ( ! hasPermission( Manifest . permission . ACCESS_FINE_LOCATION ) ) {
requestPermissions(
new String []{ Manifest . permission . ACCESS_FINE_LOCATION } ,
WiFiDirectActivity . PRC_ACCES_FINE_LOCATION
) ;
}
}
@ RequiresApi (api = Build . VERSION_CODES . M )
public boolean hasPermission( String permission) {
return checkSelfPermission(permission) == PackageManager . PERMISSION_GRANTED ;
}
@ Override
public void onRequestPermissionsResult(
int requestCode ,
@ NonNull String [] permissions ,
@ NonNull int [] grantResults
) {
if (requestCode == PRC_ACCES_FINE_LOCATION) {
if (grantResults[ 0 ] != PackageManager . PERMISSION_GRANTED ) {
Log . w (TAG , "onRequestPermissionsResult:Fine location izni gereklidir" );
Toast . makeText ( this , "İzinler gereklidir 😥" , Toast . LENGTH_SHORT )
. show ();
}
}
}
📻 WiFi için Broadcast Receiver Tanımlama
📡 Broadcast Alıcısı Tanımlama
Kotlin Java
Copy open class WifiP2PBroadcastReceiver (
var manager: WifiP2pManager ,
var channel: WifiP2pManager .Channel,
var wifiP2pActivity: WifiP2pActivity
) : BroadcastReceiver () {
companion object {
val TAG = this :: class .java.simpleName
}
@RequiresPermission (Manifest.permission.ACCESS_FINE_LOCATION)
override fun onReceive (context: Context , intent: Intent ) {
when (intent.action) {
WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION -> onStateChanged (intent)
WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION -> onPeerChanged ()
WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION ->
onConnectionChanged ()
WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION ->
onThisDeviceChanged ()
}
}
private fun onStateChanged (intent: Intent ): Unit {
Log. d (TAG, "onStateChanged: Wifi P2P durumu değişti" )
}
private fun onPeerChanged (): Unit {
Log. d (TAG, "onPeerChanged: WiFi eşleri değişti" )
}
private fun onConnectionChanged (): Unit {
Log. d (TAG, "onConnectionChanged: WiFi P2P bağlantısı değişti" )
}
private fun onThisDeviceChanged (): Unit {
Log. d (TAG, "onThisDeviceChanged: Cihazın WiFi P2P durumu değişti" )
}
}
Copy /**
* Önemli WiFi olaylarını yayınlayan sınıf
* https://developer.android.com/guide/topics/connectivity/wifip2p.html#create-br
*/
public class WiFiDirectBroadcastReciever extends BroadcastReceiver {
public static final String TAG = WiFiDirectBroadcastReciever
. class . getSimpleName ();
public static final int reconnect = 1 ;
private static final String DEVICE_PATTERN = "HUAWEI P20 lite" ;
WifiP2pManager manager;
Channel channel;
WiFiDirectActivity wifiDirectActivity;
/**
* Eşleşilen cihazların bilgileri
*/
private List < WifiP2pDevice > peers = new ArrayList <>();
public WiFiDirectBroadcastReciever (
WifiP2pManager manager ,
Channel channel ,
WiFiDirectActivity wifiDirectActivity
) {
super();
this . manager = manager;
this . channel = channel;
this . wifiDirectActivity = wifiDirectActivity;
}
@ Override
public void onReceive ( Context context , Intent intent) {
String action = intent . getAction ();
// TODO
if (action != null ) {
switch (action) {
// ...
}
}
}
}
🎫 Broadcast Alıcısını Kaydetme
▶️ Uygulama çalıştığında alıcının kayıt edilmesi
🧹 Durdurulduğunda kaydın silinmesi gerekir
🎳 Kayıt silinmezse gereksiz yere sistemi yorar ve alıcı kayıtları defalarca kaydedilebilir
Kotlin Java
Copy class WifiP2pActivity : AppCompatActivity () {
// ...
/**
* WiFi alıcısı için filtreleme
*/
private val wifiFilter = 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)
}
/**
* Wifi alıcısı
*/
private lateinit var wifiReceiver: WifiDirectBroadcastReceiver
// ...
private fun initWifiP2p (): Unit {
manager = getSystemService (Context.WIFI_P2P_SERVICE) as WifiP2pManager
channel = manager. initialize ( this , mainLooper, null )
// Yeni kısım 👇
wifiReceiver = WifiDirectBroadcastReceiver (manager, channel, this )
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getPermissions (Manifest.permission.ACCESS_FINE_LOCATION)
}
}
private fun registerWifiReceiver (): Unit {
registerReceiver (wifiReceiver, wifiFilter)
}
private fun unregisterWifiReceiver (): Unit {
unregisterReceiver (wifiReceiver)
}
override fun onResume () {
super . onResume ()
registerWifiReceiver ()
}
override fun onPause () {
super . onPause ()
unregisterWifiReceiver ()
}
}
Copy @ Override
protected void onResume() {
super . onResume ();
registerWifiFilter() ;
}
@ Override
protected void onPause() {
super . onPause ();
unregisterWifiFilter() ;
}
/**
* Broadcast alıcısını oluşturma ve sisteme kaydetme
*/
private void registerWifiFilter() {
wifiReceiver = new WiFiDirectBroadcastReciever(manager , channel , this ) ;
registerReceiver(wifiReceiver , wifiFilter) ;
}
/**
* Broadcast alıcısının kaydını silme
*/
private void unregisterWifiFilter() {
unregisterReceiver(wifiReceiver) ;
}
👷♂️ P2P Durum Değişikliklerini Algılama
👮♂️ Keşfetme işlemlerine başlamadan önce WiFi durumu kontrol edilmelidir
✖️ Eğer WiFi P2P aktif değilse keşif yapılamaz
Copy <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".WifiP2pActivity">
<TextView
android:id="@+id/tvP2pStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.053" />
</androidx.constraintlayout.widget.ConstraintLayout>
Copy /**
* WiFi P2P aktiflik durumu
*/
var p2pEnable: Boolean = false
set ( value ) {
field = value
tvP2pStatus.text = value . toString ()
}
WifiP2PBroadcastReceiver.java
Copy /**
* Wifi P2P durum değişikliklerinde tetiklenir
*/
private fun onStateChanged (intent: Intent ): Unit {
Log. d (TAG, "onStateChanged: Wifi P2P durumu değişti" )
wifiP2pActivity.p2pEnable = when (
intent. getIntExtra (WifiP2pManager.EXTRA_WIFI_STATE, - 1 )
) {
WifiP2pManager.WIFI_P2P_STATE_ENABLED -> true
else -> false
}
}
🔍 Eşleşebilecek Cihazları Arama
🔍 Keşif işlemi manager.discover
metodu ile yapılır
✔️ Keşif başarılı olursa, WIFI_P2P_PEERS_CHANGED_ACTION
haberi salınır
🕵️♂️ BroadcastReceiver
üzerinden haber durumunda ne yapılacağına karar verilir
💁♂️ onPeerChanged
metodu tetiklenecektir
Copy <androidx.constraintlayout.widget.ConstraintLayout>
<!-- ... -->
<Button
android:id="@+id/btnDiscover"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Discover"
android:onClick="onDiscoverButtonClicked"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvP2pStatus" />
</androidx.constraintlayout.widget.ConstraintLayout>
Copy @RequiresPermission (Manifest.permission.ACCESS_FINE_LOCATION)
fun onDiscoverButtonClicked (view: View ) {
Log. d (TAG, "onDiscoverButtonClicked: Butona tıklandı" )
manager. discoverPeers (channel, P2pActionListener ( "Keşif" ))
}
class P2pActionListener ( private val purpose: String ) : WifiP2pManager . ActionListener {
override fun onSuccess () {
Log. d (TAG, "onSuccess: $purpose başarılı" )
}
override fun onFailure (reason: Int ) {
val reasonMsg = when (reason) {
WifiP2pManager.P2P_UNSUPPORTED -> "P2P desteklenmiyor"
WifiP2pManager.ERROR -> "hata oluştur"
WifiP2pManager.BUSY -> "cihaz başka bir bağlantı ile meşgul"
else -> ""
}
Log. e (TAG, "onDiscoverButtonClick: $purpose başarısız, $reasonMsg" )
}
}
🧾 Eşleşilebilir Cihazları Listeleme
🔍 Keşfetme (discover) işlemi başarıyla yapıldıktan sonra çalışır
🙆♂️ Eşleşilebilir cihazların listesi requestPeers
ile talep edilir
💾 Talep edilen liste onPeerAvailable
metodu içerisinde peerList
objesine kaydedilir
Copy /**
* Eşleşilebilir cihazların listesi
*/
val peerList = ArrayList < WifiP2pDevice >()
// ...
/**
* Cihazları peerList objesine kaydetme
*/
fun storePeers (peers: WifiP2pDeviceList ) {
peers. apply {
Log. v (TAG, "onPeersAvailable: $deviceList" )
peerList. apply {
if ( this != deviceList) {
clear ()
addAll (deviceList)
}
}
}
}
WifiP2PBroadcastReceiver.java
Copy // ...
@RequiresPermission (Manifest.permission.ACCESS_FINE_LOCATION)
private fun onPeerChanged (): Unit {
Log. d (TAG, "onPeerChanged: WiFi eşleri değişti" )
manager. requestPeers (channel, wifiP2pActivity:: storePeers )
}
// ...
📶 Eşlerden Birine Bağlanma
🔗 Bağlanma işlemi için manager.connect
metodu kullanılır
💡 Bağlantı bilgisi almak için manager.requestConnectionInfo
metodu ile istekte bulunur
🕊️ Bilgi doğrultusunda sunucu ve istemci türüne karar verilir
Copy @RequiresPermission (Manifest.permission.ACCESS_FINE_LOCATION)
fun connectPeer (peer: WifiP2pDevice ) {
val config = WifiP2pConfig (). apply {
deviceAddress = peer.deviceAddress
}
manager. connect (channel, config, P2pActionListener ( "Bağlantı" ))
}
WifiP2PBroadcastReceiver.java
Copy // WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION -> onConnectionChanged()
private fun onConnectionChanged (): Unit {
Log. d (TAG, "onConnectionChanged: WiFi P2P bağlantısı değişti" )
manager. requestConnectionInfo (channel, wifiP2pActivity:: createSocket )
}
Copy fun createSocket (info: WifiP2pInfo ) {
when {
isServer (info) -> { /* createServerSocket() */ }
isClient (info) -> { /* createClientSocket() */ }
}
}
private fun isServer (info: WifiP2pInfo ): Boolean {
return info. run {
groupFormed && isGroupOwner
}
}
private fun isClient (info: WifiP2pInfo ): Boolean {
return info. run {
groupFormed && ! isGroupOwner
}
}
🕳️ TCP Socket Oluşturma
🏹 Veri aktarımı Socket üzerinden yapılmaktadır
🏗️ Aktarılmadan önce Client veya Server Socket oluşturulmalıdır
📶 Server Socket'in IP adresi Client'e aktarılmalıdır
📢 Socket işlemleri Thread içerisinde yapılmalıdır, UI Thread'in engellenmemesi gerekir
Copy inner class ServerClass : Thread () {
private lateinit var serverSocket: ServerSocket
private lateinit var socket: Socket
override fun run () {
serverSocket = ServerSocket ( 8888 )
socket = serverSocket. accept ()
socketCreated = true
sendReceive = SendReceive (socket)
sendReceive. start ()
Log. i (TAG, "run: ServerClass başarılı" )
}
}
Copy inner class ClientClass (inetAddress: InetAddress ) : Thread () {
private val socket = Socket ()
private val hostAddress: String = inetAddress.hostAddress
override fun run () {
try {
socket. apply {
bind ( null )
connect ( InetSocketAddress (hostAddress, 8888 ), 500 )
}
sendReceive = SendReceive (socket)
sendReceive. start ()
Log. i (TAG, "run: ClientClass başarılı" )
} catch (e: Exception ) {
Log. e (TAG, "run" , e)
}
}
}
Copy val MESSAGE_READ = 1
val handler = Handler {
when (it.what) {
MESSAGE_READ -> {
(it.obj as ByteArray). run {
// tvMsg layout dosyasına sonradan tanımlanacak
tvMsg.text = String ( this , 0 , it.arg1)
}
}
}
return @Handler true
}
Copy inner class SendReceive ( private val socket: Socket ) : Thread () {
private val inputStream = socket. getInputStream ()
private val outputStream = socket. getOutputStream ()
override fun run () {
val buffer = ByteArray ( 1024 )
var bytes = 0
while (socket != null ) {
inputStream !! . read (buffer). let {
if (it > 0 ) {
handler. obtainMessage (
MESSAGE_READ, it, - 1 , buffer
). sendToTarget ()
}
}
Log. d (TAG, "SendReceive: Socket kontrol edildi" )
}
}
fun write (byteArray: ByteArray ) {
GlobalScope. launch {
withContext (Dispatchers.IO) {
outputStream. write (byteArray)
Log. i (TAG, "write: Yazma başarılı" )
}
}
}
fun write (fileUri: Uri ) {
Log. i (TAG, "SendReceive: Dosya gönderme işlemine başlandı" )
val cr = contentResolver
val inputStream = cr. openInputStream (fileUri)
var result: Boolean ? = null
if (inputStream != null ) {
result = SocketAPI. copyFile (inputStream, outputStream !! )
}
when (result) {
null -> Log. d (
TAG,
"write: Sockete yazacak dosya yok ${ fileUri.path } "
)
true -> Log. d (
TAG,
"write: Sockete yazma başarılı ${ fileUri.path } "
)
else -> Log. e (
TAG,
"write: Sockete yazma başarısız ${ fileUri.path } "
)
}
}
}
Copy fun onSendButtonClick (view: View ) {
Log. d (TAG, "onSendButtonClick: Send butonuna tıklandı}" )
sendReceive. write ( "hello ${ Random. nextInt ( 10 ) } " . toByteArray ())
}
Copy <androidx.constraintlayout.widget.ConstraintLayout>
<!-- ... -->
<Button
android:id="@+id/btnSend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send"
android:onClick="onSendButtonClick"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvMsg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="17dp"
android:text="Message"
app:layout_constraintBottom_toTopOf="@+id/btnSend"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Copy fun createServerSocket () {
ServerClass (). also { it. start () }
tvMsg.text = "Server"
}
fun createClientSocket () {
ClientClass (). also { it. start () }
tvMsg.text = "Client"
}
🕳️ UDP Socket Oluşturma
🐞 Hata Çözümleri
🚫 Peers objesinin boş dönmesi
👮♂️ İlk olarak ACCESS_FINE_LOCATION
iznini cihaza vermelisin
📍 Ardından cihazda konum hizmetini açmalısın
🎉 Eşleşen cihazlar belirmeye başlayacaktır
🔗 Faydalı Bağlantılar
Last updated 9 months ago