📶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
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()
}
}
// ...
}
💎 WiFi P2P Durumları
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
<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
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()
}
}
}
📻 WiFi için Broadcast Receiver Tanımlama
📡 Broadcast Alıcısı Tanımlama
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")
}
}
🎫 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
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()
}
}
👷♂️ 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
<?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>
/**
* WiFi P2P aktiflik durumu
*/
var p2pEnable: Boolean = false
set(value) {
field = value
tvP2pStatus.text = value.toString()
}
/**
* 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
<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>
@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çerisindepeerList
objesine kaydedilir
/**
* 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)
}
}
}
}
// ...
@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ı değiştiğinde
WIFI_P2P_CONNECTION_CHANGED_ACTION
haberi salınır💡 Bağlantı bilgisi almak için
manager.requestConnectionInfo
metodu ile istekte bulunur🕊️ Bilgi doğrultusunda sunucu ve istemci türüne karar verilir
@RequiresPermission(Manifest.permission.ACCESS_FINE_LOCATION)
fun connectPeer(peer: WifiP2pDevice) {
val config = WifiP2pConfig().apply {
deviceAddress = peer.deviceAddress
}
manager.connect(channel, config, P2pActionListener("Bağlantı"))
}
// 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)
}
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
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ı")
}
}
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)
}
}
}
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
}
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}"
)
}
}
}
fun onSendButtonClick(view: View) {
Log.d(TAG, "onSendButtonClick: Send butonuna tıklandı}")
sendReceive.write("hello ${Random.nextInt(10)}".toByteArray())
}
<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>
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
Was this helpful?