「Android」WiFiP2P入门

使用 WiFi 直连 (WiFiP2P) 技术,可以让具备相应硬件的 Android 4.0(API 级别 14)或更高版本设备在没有中间接入点的情况下,通过 WiFi 进行直接互联。使用这些 API,可以实现支持 WiFi P2P 的设备间相互发现和连接,从而获得比蓝牙连接更远距离的高速连接通信效果。

为了实现一个基础的WiFiP2P,大致分为如下部分:

  • 权限申请
  • 初始化WiFiP2P的相关对象
  • 定义监听WiFiP2P的广播接收器
  • 连接设备

关于WiFiP2P中的群组,大致分为如下部分:

  • 创建群组
  • 连接群组
  • 移除群组

权限申请

首先,在AndroidManifest.xml中,对WiFi相关权限进行静态申请:

    <uses-sdk android:minSdkVersion="14" />
    <!-- WiFi相关权限 -->
    <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.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

若后续还需要读写权限则添加:

    <!-- 读写权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

然后,在 Android 6.0 及更高版本中,部分危险权限(Dangerous Permissions)权限需要在运行时请求用户批准(动态申请):

    private void checkPermission() {
        String[] permissions = new String[]{
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.ACCESS_FINE_LOCATION
        };
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
                Log.i(TAG, permission + " granted.");
            } else {
                ActivityCompat.requestPermissions(this, permissions, 0);
                Log.w(TAG, permission + " not granted.");
            }
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == 0) {
            for (int result : grantResults) {
                if (result == PackageManager.PERMISSION_GRANTED) {
                    continue;
                } else {
                    Toast.makeText(this, "权限未获取", Toast.LENGTH_SHORT).show();
                }
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

初始化

首先,需要创建:

  • WifiP2pManager 对象
  • WifiP2pManager.Channel 对象
  • WiFiDirectBroadcastReceiver 对象(稍后介绍该广播接收器的定义)
  • IntentFilter 对象
    private WifiP2pManager mManager;
    private WifiP2pManager.Channel mChannel;
    private WiFiDirectBroadcastReceiver mReceiver;
    private IntentFilter mIntentFilter;

    private void initWifip2pHelper() {
        // 创建 WifiP2pManager 对象
        mManager = (WifiP2pManager) getSystemService(WIFI_P2P_SERVICE);
        // 创建 WifiP2pManager.Channel 对象
        mChannel = mManager.initialize(this, Looper.getMainLooper(), new WifiP2pManager.ChannelListener() {
            @Override
            public void onChannelDisconnected() {
                Log.i(TAG, "onChannelDisconnected: ");
            }
        });
        // 创建 WiFiDirectBroadcastReceiver 对象
        mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel, this);
        // 创建 IntentFilter 对象
        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
    }
其中, WiFiDirectBroadcastReceiver 对象想要监听的广播,与 IntentFilter 对象添加的action相同。

然后,在 Activity 的onResume()方法中注册广播接收器,在 Activity 的onPause()方法中取消注册该广播接收器:

    /* register the broadcast receiver with the intent values to be matched */
    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(mReceiver, mIntentFilter);
    }

    /* unregister the broadcast receiver */
    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mReceiver);
    }

定义监听WiFiP2P的广播接收器

监听WiFiP2P的广播接收器 WiFiDirectBroadcastReceiver 类具体定义如下:

public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {

    private static final String TAG = "WiFiDirectBroadcastReceiver";

    private WifiP2pManager mManager;
    private WifiP2pManager.Channel mChannel;
    private Wifip2pActivity mActivity;

    private List<WifiP2pDevice> mWifiP2pDeviceList = new ArrayList<>();

    WifiP2pManager.PeerListListener mPeerListListener = new WifiP2pManager.PeerListListener() {
        @Override
        public void onPeersAvailable(WifiP2pDeviceList wifiP2pDeviceList) {
            mWifiP2pDeviceList.clear();
            mWifiP2pDeviceList.addAll(wifiP2pDeviceList.getDeviceList());
        }
    };

    /**
     * 构造方法
     *
     * @param manager      WifiP2pManager对象
     * @param channel      WifiP2pManager.Channel对象
     * @param activity     Wifip2pActivity 对象
     */
    public WiFiDirectBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel, Wifip2pActivity activity) {
        super();
        this.mManager = manager;
        this.mChannel = channel;
        this.mActivity = activity;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        switch (action) {
            case WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION:
                // Check to see if Wi-Fi is enabled and notify appropriate activity
                Log.i(TAG, "onReceive: WIFI_P2P_STATE_CHANGED_ACTION");
                int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
                if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                    // Wifi P2P is enabled
                    Log.i(TAG, "onReceive: Wifi P2P is enabled");
                    mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
                        @Override
                        public void onSuccess() {
                            Log.d(TAG, "onSuccess: ");
                        }

                        @Override
                        public void onFailure(int i) {
                            Log.d(TAG, "onFailure: ");
                        }
                    });
                } else {
                    // Wi-Fi P2P is not enabled
                    Log.i(TAG, "onReceive: Wi-Fi P2P is not enabled");
                }
                break;
            case WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION:
                // Call WifiP2pManager.requestPeers() to get a list of current peers
                Log.i(TAG, "onReceive: WIFI_P2P_PEERS_CHANGED_ACTION");
                if (mManager == null) {
                    return;
                }
                mManager.requestPeers(mChannel, mPeerListListener);
                break;
            case WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION:
                // Respond to new connection or disconnections
                Log.i(TAG, "onReceive: WIFI_P2P_CONNECTION_CHANGED_ACTION");
                // NetworkInfo
                NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
                // WifiP2pInfo
                WifiP2pInfo wifiP2pInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
                // WifiP2pGroup
                WifiP2pGroup wifiP2pGroup = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
                if (networkInfo.isConnected()) {
                    if (wifiP2pInfo.isGroupOwner) {
                        Toast.makeText(mActivity, "设备连接,本设备为GO", Toast.LENGTH_LONG).show();
                    } else {
                        Toast.makeText(mActivity, "设备连接,本设备非GO", Toast.LENGTH_LONG).show();
                    }
                } else {
                    Toast.makeText(mActivity, "设备断开", Toast.LENGTH_LONG).show();
                }
                break;
            case WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:
                // Respond to this device's wifi state changing
                Log.i(TAG, "onReceive: WIFI_P2P_THIS_DEVICE_CHANGED_ACTION");
                WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
                Log.d(TAG, "onReceive: " +device.deviceAddress);
                break;
            case WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION:
                Log.i(TAG, "onReceive: WIFI_P2P_DISCOVERY_CHANGED_ACTION");
                break;
        }
    }
}

上述广播接收器用于监听系统关于WiFiP2P相关的广播。通常在onReceive()方法中,通过intent.getAction()方法获取到action,并根据action去匹配不同的关于WiFiP2P相关的广播,分别为:

  • WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION
  • WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION
  • WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION
  • WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
  • WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION

WIFI_P2P_STATE_CHANGED_ACTION:WiFiP2P状态发生改变时的广播

WiFiP2P具体有两个状态:

  • WifiP2pManager.WIFI_P2P_STATE_ENABLED:可用
  • WifiP2pManager.WIFI_P2P_STATE_DISABLED:不可用

而该状态的获取是由:

    int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);

当WiFiP2P状态为可用时,调用discoverPeers()方法开始搜索附近WiFiP2P设备:

    mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
        @Override
        public void onSuccess() {
            Log.d(TAG, "onSuccess: ");
        }

        @Override
        public void onFailure(int i) {
            Log.d(TAG, "onFailure: ");
        }
    });

WIFI_P2P_PEERS_CHANGED_ACTION:发现附近WiFiP2P设备时的广播

当搜索发现附近存在WiFiP2P设备时,调用requestPeers()方法开始获取附近WiFiP2P设备列表:

    mManager.requestPeers(mChannel, mPeerListListener);

当成功获取附近WiFiP2P设备列表后,会回调侦听器 WifiP2pManager.PeerListListener 中的onPeersAvailable()方法,并传递一个 WifiP2pDeviceList 对象作为参数,可以用一个 List<WifiP2pDevice> 对象接收并保存该参数:

    WifiP2pManager.PeerListListener mPeerListListener = new WifiP2pManager.PeerListListener() {
        @Override
        public void onPeersAvailable(WifiP2pDeviceList wifiP2pDeviceList) {
            mWifiP2pDeviceList.clear();
            mWifiP2pDeviceList.addAll(wifiP2pDeviceList.getDeviceList());
        }
    };

WIFI_P2P_CONNECTION_CHANGED_ACTION:连接状态发生改变时的广播

当连接状态发生改变时(如连接了一个设备,断开了一个设备),都会接收到该广播。当接收到该广播后,可以使用intent.getParcelableExtra()方法分别获取到 NetworkInfo , WifiP2pInfo , WifiP2pGroup 对象:

    // 获取 NetworkInfo 对象
    NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
    // 获取 WifiP2pInfo 对象
    WifiP2pInfo wifiP2pInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO);
    // 获取 WifiP2pGroup 对象
    WifiP2pGroup wifiP2pGroup = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);

获取到上述对象后,可以使用networkInfo.isConnected()方法来判断连接状态具体是“设备连接”还是“设备断开”,还可以根据wifiP2pInfo.isGroupOwner的值来判断设备是否为GroupOwner:

    if (networkInfo.isConnected()) {
        if (wifiP2pInfo.isGroupOwner) {
            Toast.makeText(mActivity, "设备连接,本设备为GroupOwner", Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(mActivity, "设备连接,本设备非GroupOwner", Toast.LENGTH_LONG).show();
        }
    } else {
        Toast.makeText(mActivity, "设备断开", Toast.LENGTH_LONG).show();
    }

WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:当前设备状态发生改变时的广播

通常可以在这个广播中获取到当前设备的信息:

    WifiP2pDevice device = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);

WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION:搜索状态发生改变时的广播

启动搜索:

    mManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
        @Override
        public void onSuccess() {
            Log.d(TAG, "onSuccess: ");
        }

        @Override
        public void onFailure(int i) {
            Log.d(TAG, "onFailure: ");
        }
    });

停止搜索:

    mManager.stopPeerDiscovery(mChannel, new WifiP2pManager.ActionListener() {
        @Override
        public void onSuccess() {
            Log.d(TAG, "onSuccess: ");
        }

        @Override
        public void onFailure(int i) {
            Log.d(TAG, "onFailure: ");
        }
    });

连接设备

首先,选择一个需要连接的设备,并获取到该设备的 WifiP2pDevice 对象。然后,判断该设备的状态,设备状态通常为三种:

  • WifiP2pDevice.AVAILABLE:可连接
  • WifiP2pDevice.CONNECTED:已连接
  • WifiP2pDevice.INVITED:已请求连接

根据不同的设备状态,进行不同的具体逻辑:

    @Override
    public void onClick(View view) {
        // 获取到该设备的 WifiP2pDevice 对象
        WifiP2pDevice wifiP2pDevice = mWifiP2pDeviceList.get(viewHolder.getAdapterPosition());
        // 判断该设备的状态
        switch (wifiP2pDevice.status) {
            case WifiP2pDevice.AVAILABLE:
                // 请求连接
                WifiP2pConfig config = new WifiP2pConfig();
                config.deviceAddress = wifiP2pDevice.deviceAddress;
                mManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
                    @Override
                    public void onSuccess() {
                        Log.i(TAG, "connect success.");
                    }

                    @Override
                    public void onFailure(int i) {
                        Log.i(TAG, "connect failed.");
                    }
                });
                break;
            case WifiP2pDevice.CONNECTED:
                // 断开连接
                mManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
                    @Override
                    public void onSuccess() {
                        Log.i(TAG, "removeGroup success.");
                    }

                    @Override
                    public void onFailure(int i) {
                        Log.i(TAG, "removeGroup failed.");
                    }
                });
                break;
            case WifiP2pDevice.INVITED:
                // 关闭连接请求
                mManager.cancelConnect(mChannel, new WifiP2pManager.ActionListener() {
                    @Override
                    public void onSuccess() {
                        Log.i(TAG, "cancelConnect success.");
                    }

                    @Override
                    public void onFailure(int i) {
                        Log.i(TAG, "cancelConnect failed.");
                    }
                });
                break;
        }
    }

创建群组

GroupOwner创建Group:

    private static final String NETWORKNAME_P60 = "DIRECT-HUAWEI-P60";
    private static final String PASSPHRASE_P60 = "12345678";

    private static void createWiFiP2PGroup(final Activity context, final WifiP2pManager manager, final WifiP2pManager.Channel channel) {
        WifiP2pConfig config = new WifiP2pConfig.Builder()
                .setGroupOperatingBand(WifiP2pConfig.GROUP_OWNER_BAND_AUTO)
                .setNetworkName(NETWORKNAME_P60)
                .setPassphrase(PASSPHRASE_P60)
                .build();
        manager.createGroup(channel, config, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                Log.i(TAG, "onSuccess: createGroup");
                Toast.makeText(context, "onSuccess: createGroup", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailure(int i) {
                Log.i(TAG, "onFailure: createGroup " + i);
                Toast.makeText(context, "onFailure: createGroup " + i, Toast.LENGTH_SHORT).show();
            }
        });
    }

连接群组

Client连接GroupOwner:

    public static void connectWiFiP2P(final Activity context, final WifiP2pManager manager, final WifiP2pManager.Channel channel, final WifiP2pDevice wifiP2pDevice) {
        // 如果连接对象为群组GroupOwner
        if (wifiP2pDevice.isGroupOwner()) {
            Log.d(TAG, "connectWiFiP2P: " + wifiP2pDevice.deviceName);
            WifiP2pConfig config;
            if (needPassphrase) {
                // 以PIN的方式连接
                config = new WifiP2pConfig.Builder()
                        .setNetworkName(NETWORKNAME_iPhone)
                        .setPassphrase(PASSPHRASE_iPhone)
                        .build();
            } else {
                // 以默认的方式连接
                config = new WifiP2pConfig();
                config.deviceAddress = wifiP2pDevice.deviceAddress;
            }
            manager.connect(channel, config, new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    Log.i(TAG, "onSuccess: connect");
                }

                @Override
                public void onFailure(int i) {
                    Log.i(TAG, "onFailure: connect " + i);
                }
            });
        }
    }

移除群组

GroupOwner移除Group:

    manager.requestGroupInfo(channel, new WifiP2pManager.GroupInfoListener() {
        @Override
        public void onGroupInfoAvailable(WifiP2pGroup wifiP2pGroup) {
            if (wifiP2pGroup != null) {
                Log.i(TAG, "onGroupInfoAvailable: wifiP2pGroup != null");
                manager.removeGroup(channel, new WifiP2pManager.ActionListener() {
                    @Override
                    public void onSuccess() {
                        Log.i(TAG, "onSuccess: removeGroup");
                    }

                    @Override
                    public void onFailure(int i) {
                        Log.i(TAG, "onFailure: removeGroup");
                    }
                });
            } else {
                Log.i(TAG, "onGroupInfoAvailable: wifiP2pGroup == null");
            }
        }
    });

参考文档

WLAN 直连(对等连接或 P2P)概览

通过 Wi-Fi 直连创建点对点连接

Android WiFi P2P开发实践笔记


山庄的铁匠
15 声望11 粉丝