Android WiFi P2P
WiFi P2P (Peer-to-Peer) 是 Android 提供的一種允許設備之間直接通過 WiFi 進行通信的技術,無需接入傳統的 WiFi 網絡或互聯網。這種技術也被稱為 WiFi Direct。
一、WiFi P2P 基本概念
1. 核心組件
-
P2P 設備:支持 WiFi P2P 的 Android 設備
-
P2P 組:由一個組所有者(Group Owner, GO)和多個客戶端組成
-
組所有者(GO):相當于傳統 WiFi 網絡中的接入點(AP)
-
客戶端:連接到 GO 的設備
2. 工作流程
-
發現附近設備
-
請求連接
-
建立 P2P 組
-
數據傳輸
-
斷開連接
二、WiFi P2P API 詳解
1. 主要類
-
WifiP2pManager
:主管理類,提供發現、連接等方法 -
WifiP2pManager.Channel
:與 WiFi P2P 框架通信的通道 -
WifiP2pDevice
:表示一個 P2P 設備 -
WifiP2pInfo
:包含 P2P 連接信息 -
WifiP2pGroup
:表示 P2P 組信息 -
WifiP2pConfig
:用于配置 P2P 連接
2. 權限要求
在 AndroidManifest.xml 中添加:
<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.NEARBY_WIFI_DEVICES" android:usesPermissionFlags="neverForLocation" />
注意:從 Android 12 開始,需要 NEARBY_WIFI_DEVICES 權限來發現和連接附近的設備
三、WiFi P2P 實現代碼
1. 初始化
// 獲取 WifiP2pManager 和 Channel
WifiP2pManager manager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
Channel channel = manager.initialize(this, getMainLooper(), null);// 創建廣播接收器
private final IntentFilter intentFilter = new IntentFilter();@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 添加必要的 Intent 過濾器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);// 注冊廣播接收器receiver = new WiFiDirectBroadcastReceiver(manager, channel, this);registerReceiver(receiver, intentFilter);
}
2. 廣播接收器實現
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {private WifiP2pManager manager;private Channel channel;private Activity activity;public WiFiDirectBroadcastReceiver(WifiP2pManager manager, Channel channel, Activity activity) {this.manager = manager;this.channel = channel;this.activity = activity;}@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION.equals(action)) {// 檢查 WiFi P2P 是否啟用int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {// WiFi P2P 已啟用} else {// WiFi P2P 未啟用}} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {// 發現對等設備列表已更新if (manager != null) {manager.requestPeers(channel, peerListListener);}} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {// P2P 連接狀態改變if (manager != null) {NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);if (networkInfo.isConnected()) {// 已連接,請求連接信息manager.requestConnectionInfo(channel, connectionInfoListener);}}} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION.equals(action)) {// 本設備信息已更新}}
}
3. 發現附近設備
// 開始發現設備
manager.discoverPeers(channel, new WifiP2pManager.ActionListener() {@Overridepublic void onSuccess() {// 發現過程成功啟動}@Overridepublic void onFailure(int reason) {// 發現過程啟動失敗}
});// 處理發現的設備列表
private WifiP2pManager.PeerListListener peerListListener = new WifiP2pManager.PeerListListener() {@Overridepublic void onPeersAvailable(WifiP2pDeviceList peers) {// 處理發現的設備列表List<WifiP2pDevice> devices = new ArrayList<>(peers.getDeviceList());// 更新UI或進行其他處理}
};
4. 連接設備
// 選擇要連接的設備
WifiP2pDevice device = ...; // 從發現的設備列表中選擇// 創建連接配置
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC; // 使用按鈕配置方式// 發起連接
manager.connect(channel, config, new WifiP2pManager.ActionListener() {@Overridepublic void onSuccess() {// 連接請求成功發送}@Overridepublic void onFailure(int reason) {// 連接失敗}
});
5. 獲取連接信息
private WifiP2pManager.ConnectionInfoListener connectionInfoListener = new WifiP2pManager.ConnectionInfoListener() {@Overridepublic void onConnectionInfoAvailable(WifiP2pInfo info) {// 處理連接信息InetAddress groupOwnerAddress = info.groupOwnerAddress;if (info.groupFormed && info.isGroupOwner) {// 當前設備是組所有者(GO)// 需要在此處設置服務器套接字} else if (info.groupFormed) {// 當前設備是客戶端// 需要連接到GO的IP地址}}};
6. 數據傳輸
建立連接后,可以使用套接字進行數據傳輸:
組所有者(GO)端代碼
// 創建服務器套接字
ServerSocket serverSocket = new ServerSocket(8888);
Socket client = serverSocket.accept();// 獲取輸入輸出流
DataInputStream inputStream = new DataInputStream(client.getInputStream());
DataOutputStream outputStream = new DataOutputStream(client.getOutputStream());// 讀取數據
String message = inputStream.readUTF();// 發送數據
outputStream.writeUTF("Hello from GO!");
客戶端端代碼
// 連接到GO
Socket socket = new Socket(groupOwnerAddress.getHostAddress(), 8888);// 獲取輸入輸出流
DataInputStream inputStream = new DataInputStream(socket.getInputStream());
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());// 發送數據
outputStream.writeUTF("Hello from client!");// 讀取數據
String message = inputStream.readUTF();
7. 斷開連接
manager.removeGroup(channel, new WifiP2pManager.ActionListener() {@Overridepublic void onSuccess() {// 成功斷開連接}@Overridepublic void onFailure(int reason) {// 斷開連接失敗}
});
四、常見問題與解決方案
-
設備無法發現其他設備
-
確保兩臺設備都啟用了 WiFi P2P
-
檢查是否授予了必要權限(特別是位置權限)
-
確保設備之間距離足夠近(通常不超過10米)
-
-
連接失敗
-
檢查設備是否支持 WiFi Direct
-
嘗試不同的 WPS 配置方法(PBC 或 KEYPAD)
-
重啟設備的 WiFi
-
-
數據傳輸問題
-
確保在獲取連接信息后才嘗試建立套接字連接
-
檢查防火墻設置是否阻止了端口通信
-
確保正確處理了網絡操作線程(不要在UI線程執行網絡操作)
-
-
Android 12 及更高版本的兼容性
-
添加 NEARBY_WIFI_DEVICES 權限
-
如果不需要位置信息,設置 usesPermissionFlags="neverForLocation"
-
考慮添加 ACCESS_COARSE_LOCATION 或 ACCESS_FINE_LOCATION 權限以獲得更好的兼容性
-
五、最佳實踐
-
用戶體驗優化
-
在發現和連接過程中顯示適當的進度指示
-
提供明確的連接狀態反饋
-
處理各種錯誤情況并提供恢復選項
-
-
性能考慮
-
發現過程消耗較多電量,應在必要時才啟動
-
連接建立后及時停止發現過程
-
考慮使用服務來處理長時間運行的網絡操作
-
-
安全性
-
驗證連接設備的身份(如設備名稱或其他標識)
-
考慮在應用層添加加密機制
-
敏感數據傳輸使用安全協議
-
-
兼容性處理
-
檢查設備是否支持 WiFi P2P:
manager != null && channel != null
-
為舊版本 Android 提供備用方案
-
處理不同廠商設備的兼容性問題
-