Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีใช้ WiFi Direct ใน Android: เชื่อมต่ออุปกรณ์โดยตรง

บทความนี้สาธิตการใช้ WiFi Direct (Wi-Fi P2P) ใน Android เพื่อเชื่อมต่ออุปกรณ์โดยตรงโดยไม่ต้องมี Access Point (Router) ใช้ WifiP2pManager และ BroadcastReceiver

🔧 ข้อกำหนดเบื้องต้น

  • Android 4.0+ (API 14) รองรับ WiFi Direct
  • Permission: ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, ACCESS_FINE_LOCATION (API 23+)
  • อุปกรณ์ 2 เครื่องขึ้นไป ที่รองรับ WiFi Direct

ขั้นตอนที่ 1: สร้างโปรเจกต์

Android Studio → New Project → Empty Activity

ขั้นตอนที่ 2: Layout (res/layout/activity_main.xml)

<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    android:gravity="center">

    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="WiFi Direct: OFF"
        android:textSize="18sp"
        android:layout_marginBottom="24dp"/>

    <Button
        android:id="@+id/btnToggle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="เปิด WiFi Direct"
        android:padding="16dp 32dp"/>

    <Button
        android:id="@+id/btnDiscover"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ค้นหาอุปกรณ์"
        android:padding="16dp 32dp"
        android:layout_marginTop="16dp"
        android:enabled="false"/>

    <ListView
        android:id="@+id/lvPeers"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_marginTop="16dp"/>
</LinearLayout>

ขั้นตอนที่ 3: WifiDirectBroadcastReceiver.java

package com.example.wifidirect;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.p2p.WifiP2pManager;
import android.widget.Toast;

public class WifiDirectBroadcastReceiver extends BroadcastReceiver {

    private final WifiP2pManager manager;
    private final WifiP2pManager.Channel channel;
    private final MainActivity activity;

    public WifiDirectBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel, MainActivity activity) {
        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)) {
            // สถานะ WiFi Direct เปลี่ยน (เปิด/ปิด)
            int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
            activity.updateWifiDirectState(state == WifiP2pManager.WIFI_P2P_STATE_ENABLED);

        } else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
            // รายการอุปกรณ์เพื่อน (Peers) เปลี่ยนแปลง
            if (manager != null && channel != null) {
                manager.requestPeers(channel, activity.peerListListener);
            }

        } else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
            // สถานะการเชื่อมต่อเปลี่ยน
            // TODO: ดึง NetworkInfo และจัดการเชื่อมต่อ

        } else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {
            // ข้อมูลอุปกรณ์ตัวเองเปลี่ยน
        }
    }
}

ขั้นตอนที่ 4: MainActivity.java (Logic หลัก)

package com.example.wifidirect;

import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.ArrayAdapter;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private WifiP2pManager manager;
    private WifiP2pManager.Channel channel;
    private BroadcastReceiver receiver;
    private IntentFilter intentFilter;

    private TextView tvStatus;
    private Button btnToggle, btnDiscover;
    private ListView lvPeers;
    private ArrayAdapter peersAdapter;
    private List peerList = new ArrayList<>();

    // Listener สำหรับรับรายการ Peers
    public final WifiP2pManager.PeerListListener peerListListener = new WifiP2pManager.PeerListListener() {
        @Override
        public void onPeersAvailable(WifiP2pDeviceList peers) {
            peerList.clear();
            peerList.addAll(peers.getDeviceList());
            updatePeersList();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tvStatus = findViewById(R.id.tvStatus);
        btnToggle = findViewById(R.id.btnToggle);
        btnDiscover = findViewById(R.id.btnDiscover);
        lvPeers = findViewById(R.id.lvPeers);

        peersAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
        lvPeers.setAdapter(peersAdapter);

        // เริ่มต้น WifiP2pManager
        manager = (WifiP2pManager) getSystemService(WIFI_P2P_SERVICE);
        channel = manager.initialize(this, getMainLooper(), null);

        // สร้าง Receiver
        receiver = new WifiDirectBroadcastReceiver(manager, channel, this);

        // กำหนด Action ที่จะรับ
        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);

        btnToggle.setOnClickListener(v -> toggleWifiDirect());
        btnDiscover.setOnClickListener(v -> discoverPeers());
    }

    private void toggleWifiDirect() {
        // WiFi Direct ใช้ WiFi ปกติ ต้องเปิด WiFi ก่อน
        // หมายเหตุ: ไม่มี API ตรงๆ สำหรับ toggle P2P only ใช้ WifiManager แทน
        // แต่ WifiP2pManager จะทำงานเมื่อ WiFi เปิดอยู่
    }

    private void discoverPeers() {
        if (manager != null && channel != null) {
            manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    tvStatus.setText("กำลังค้นหาอุปกรณ์...");
                }
                @Override
                public void onFailure(int reason) {
                    tvStatus.setText("ค้นหาล้มเหลว: " + reason);
                }
            });
        }
    }

    public void updateWifiDirectState(boolean enabled) {
        runOnUiThread(() -> {
            if (enabled) {
                tvStatus.setText("WiFi Direct: ON");
                btnToggle.setText("ปิด WiFi Direct");
                btnDiscover.setEnabled(true);
            } else {
                tvStatus.setText("WiFi Direct: OFF");
                btnToggle.setText("เปิด WiFi Direct");
                btnDiscover.setEnabled(false);
                peersAdapter.clear();
            }
        });
    }

    private void updatePeersList() {
        runOnUiThread(() -> {
            peersAdapter.clear();
            for (WifiP2pDevice device : peerList) {
                peersAdapter.add(device.deviceName + "\n" + device.deviceAddress);
            }
            peersAdapter.notifyDataSetChanged();
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(receiver, intentFilter);
    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(receiver);
    }
}

ขั้นตอนที่ 5: AndroidManifest.xml

<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.example.wifidirect">

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

    <!-- Hardware Feature (Optional - สำหรับ Filter บน Play Store) -->
    <uses-feature android:name="android.hardware.wifi.direct" android:required="true" />

    <application ...>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

🔄 ขั้นตอนการเชื่อมต่อเต็มรูปแบบ (Connection Flow)

  1. Initialize: manager.initialize()
  2. Discover Peers: manager.discoverPeers()
  3. Get Peers: manager.requestPeers() → PeerListListener
  4. Connect: manager.connect() ด้วย WifiP2pConfig (ระบุ deviceAddress, wps.setup = PBC)
  5. Connection Info: manager.requestConnectionInfo() → ConnectionInfoListener (ได้ Group Owner IP)
  6. Transfer Data: ใช้ Socket ต่อที่ Group Owner IP (Port 8888 หรือที่กำหนด)

โค้ดเชื่อมต่อ (Connect)

WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = selectedDevice.deviceAddress;
config.wps.setup = WpsInfo.PBC; // Push Button Config

manager.connect(channel, config, new WifiP2pManager.ActionListener() {
    @Override public void onSuccess() { /* กำลังเชื่อมต่อ... */ }
    @Override public void onFailure(int reason) { /* ล้มเหลว */ }
});

รับข้อมูลการเชื่อมต่อ

manager.requestConnectionInfo(channel, new WifiP2pManager.ConnectionInfoListener() {
    @Override
    public void onConnectionInfoAvailable(WifiP2pInfo info) {
        if (info.groupFormed) {
            String hostAddress = info.groupOwnerAddress.getHostAddress();
            if (info.isGroupOwner) {
                // ตัวเองเป็น Server (Group Owner) - เปิด ServerSocket
                startServer(hostAddress);
            } else {
                // ตัวเองเป็น Client - ต่อ Socket ไปที่ hostAddress
                connectToServer(hostAddress);
            }
        }
    }
});

⚠️ ข้อควรระวังและข้อจำกัด

  • ต้องเปิด WiFi ก่อน - WiFi Direct ทำงานบน WiFi Radio เดียวกัน
  • Android 10+ (API 29): ต้องมี ACCESS_FINE_LOCATION และ Location Services เปิดอยู่
  • ไม่ทำงานบน Emulator - ต้องทดสอบบนอุปกรณ์จริง 2 เครื่อง
  • Battery Drain: การ Discover ต่อเนื่องใช้แบตเตอรี่สูง
  • Channel Conflict: อาจขัดแย้งกับ WiFi Infrastructure Mode

📱 การทดสอบ

  1. ติดตั้งแอปบนอุปกรณ์ 2 เครื่อง
  2. เปิด WiFi ทั้งคู่
  3. กด "เปิด WiFi Direct" → ควรเห็นสถานะ ON
  4. กด "ค้นหาอุปกรณ์" ทั้งคู่
  5. จะเห็นรายชื่ออุปกรณ์อีกเครื่องใน ListView
  6. คลิกเลือกอุปกรณ์ → เรียก connect() เพื่อจับคู่