一、开发中所需要的权限
二、Wifi基础知识
1、开发中常用类和常量
类名 | 功能 |
---|---|
WifiManager | wifi统一管理类,进行各种wifi操作 |
WifiInfo | 描述当前连接的wifi热点信息 |
WifiConfiguration | wifi网络配置信息 |
除此之外还有一个类:
类名 | 功能 |
---|---|
ScanResult | 描述扫描出的wifi热点的信息,每个wifi对应一个ScanResult |
常用wifi名词解释:
名称 | 功能 |
---|---|
SSID | 描述wifi热点的名称,就是大家搜索到的直接名称,如ChinaNet |
BSSID | 姑且理解成热点的mac地址,但实际有所不同 |
networkID | 数字型的id |
RSSI | 描述wifi信号强弱的值,官方叫做level |
手机输入wifi密码连上后,下一次可以不用输入密码甚至是自动连接,就是因为手机中其实已经存储了wifi的信息,每个wifi有对应的networkid
打开和关闭wifi的总开关:
WifiManager mWifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
// 打开wifi开关
mWifiManager.setWifiEnabled(true);
// 关闭wifi开关
mWifiManager.setWifiEnabled(false);
wifi打开和关闭的状态常量,都封装在WifiManager中:
可以在任何时候通过mWifiManager.getWifiState()来获取当前wifi的状态:
mWifiManager.setWifiEnabled(true);
if (mWifiManager.getWifiState() == WifiManager.WIFI_STATE_DISABLING) {
// wifi正在关闭
} else if (mWifiManager.getWifiState() == WifiManager.WIFI_STATE_DISABLED) {
// wifi已关闭
} else if (mWifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLING) {
// wifi正在开启
} else if (mWifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
// wifi已开启
} else if (mWifiManager.getWifiState() == WifiManager.WIFI_STATE_UNKNOWN) {
// 未知状态
}
wifi状态的变化会发出下列的广播事件:
WifiManager.WIFI_STATE_CHANGED_ACTION wifi开关变化通知
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION wifi扫描结果通知
WifiManager.SUPPLICANT_STATE_CHANGED_ACTION wifi连接结果通知
WifiManager.NETWORK_STATE_CHANGED_ACTION 网络状态变化通知
2、Wifi搜索和连接
(1)、搜索周围wifi信号:
// 搜索wifi热点
private void searchWifi() {
if (!wifiManager.isWifiEnabled()) {
//开启wifi
wifiManager.setWifiEnabled(true);
}
wifiManager.startScan();
}
(2)、获取扫描到的wifi
调用上述方法以后就开始搜索wifi了,当搜到wifi以后系统会发广播出来,监听广播来获取搜索到的wifi:因为wifiManager.getScanResults()每次获取的都是左右的扫到的wifi,所以不能直接把数据加到列表上,要清除列表上的数据,然后把扫到的数据放上来,不然会造成数据重复
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
// wifi已成功扫描到可用wifi(包含之前扫到的)
List scanResults = wifiManager.getScanResults();
wifiListAdapter.clear();
wifiListAdapter.addAll(scanResults);
}
};
(3)、wifi连接
private static final int WIFICIPHER_NOPASS = 1;
private static final int WIFICIPHER_WEP = 2;
private static final int WIFICIPHER_WPA = 3;
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
wifiManager.disconnect();
final ScanResult scanResult = wifiListAdapter.getItem(position);
String capabilities = scanResult.capabilities;
int type = WIFICIPHER_WPA;
if (!TextUtils.isEmpty(capabilities)) {
if (capabilities.contains("WPA") || capabilities.contains("wpa")) {
type = WIFICIPHER_WPA;
} else if (capabilities.contains("WEP") || capabilities.contains("wep")) {
type = WIFICIPHER_WEP;
} else {
type = WIFICIPHER_NOPASS;
}
}
config = isExsits(scanResult.SSID);
if (config == null) {
if (type != WIFICIPHER_NOPASS) {//需要密码
final EditText editText = new EditText(MainActivity.this);
final int finalType = type;
new AlertDialog.Builder(MainActivity.this).setTitle("请输入Wifi密码").setIcon(
android.R.drawable.ic_dialog_info).setView(
editText).setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.w("AAA", "editText.getText():" + editText.getText());
config = createWifiInfo(scanResult.SSID, editText.getText().toString(), finalType);
connect(config);
}
})
.setNegativeButton("取消", null).show();
return;
} else {
config = createWifiInfo(scanResult.SSID, "", type);
connect(config);
}
} else {
connect(config);
}
}
});
/**
* 判断当前wifi是否有保存
*
* @param SSID
* @return
* 当没有获取到所要连接WiFi的配置信息时,我们就需要用到前面获取到的加密方式判断是否需
* 要输入密码。如果加密方式为WAP或WEP时,则弹出提示框提示用户输入WiFi密码。用户输入密
* 码后再调用connect(WifiConfiguration config)方法
*/
private WifiConfiguration isExsits(String SSID) {
List existingConfigs = wifiManager.getConfiguredNetworks();
for (WifiConfiguration existingConfig : existingConfigs) {
if (existingConfig.SSID.equals("\"" + SSID + "\"")) {
return existingConfig;
}
}
// 返回空表示没保存过
return null;
}
private void connect(WifiConfiguration config) {
text_state.setText("连接中...");
// 获取的networkId 如果为-1表示失败
int networkID = wifiManager.addNetwork(config);
wifiManager.enableNetwork(networkID, true);
}
(4)、注册广播,监听连接的状态
if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if (info.getState().equals(NetworkInfo.State.DISCONNECTED)) {
text_state.setText("连接已断开");
} else if (info.getState().equals(NetworkInfo.State.CONNECTED)) {
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
final WifiInfo wifiInfo = wifiManager.getConnectionInfo();
text_state.setText("已连接到网络:" + wifiInfo.getSSID());
} else {
NetworkInfo.DetailedState state = info.getDetailedState();
if (state == state.CONNECTING) {
text_state.setText("连接中...");
} else if (state == state.AUTHENTICATING) {
text_state.setText("正在验证身份信息...");
} else if (state == state.OBTAINING_IPADDR) {
text_state.setText("正在获取IP地址...");
} else if (state == state.FAILED) {
text_state.setText("连接失败");
}
}
}
以上代码只是监听了部分详细状态,还有很多其他的状态:
IDLE:空闲
SCANNING:正在扫描
CONNECTING:连接中
AUTHENTICATING:正在进行身份验证
OBTAINING_IPADDR:正在获取Ip地址
CONNECTED:已连接
SUSPENDED:已暂停
DISCONNECTING:正在断开连接
DISCONNECTED:已断开
FAILED:失败
BLOCKED:已阻止
VERIFYING_POOR_LINK:暂时关闭(网络状况不佳)
CAPTIVE_PORTAL_CHECK:判断是否需要浏览器二次登录
三、Wifi Direct Connection介绍
1、简介
蓝牙有Ble轻量连接,Wifi当然也一样,WiFi Direct Connection点对点传输不用连接网络和热点,可进行近距离通信,比蓝牙传输要远,速度更快。
Wifi Direct API主要包括以下几个部分:
(1)WifiP2pManager,该类允许用户发现、请求和连接其他支持Wifi Direct的设备
(2)监听Wifi Direct请求的广播接收器
(3)被Wifi Direct框架检测到的时间通知,例如终止连接、发现新的Wifi Direct设备等
2、Wifi Direct广播接收器, 基本都是异步的,通过发广播来通知界面
WIFI_P2P_STATE_CHANGED_ACTION 检测Wifi Direct Connection是否可用,并且将检测结果通过此广播通知界面
WIFI_P2P_PEERS_CHANGED_ACTION 搜索可用的Wifi列表完成,收到此广播可以通过mWifiP2pManager.requestPeers方法获取所有的可用的wifi列表
WIFI_P2P_CONNECTION_CHANGED_ACTION 表示Wi-Fi连接状态发生了改变
WIFI_P2P_THIS_DEVICE_CHANGED_ACTION 响应设备的wifi状态变化
public class WiFiDirectBroadcastReceiver extends BroadcastReceiver {
private WifiP2pManager.Channel channel;
private WifiP2pManager manager;
private MainActivity activity;
public WiFiDirectBroadcastReceiver(WifiP2pManager.Channel channel, WifiP2pManager manager) {
super();
this.channel = channel;
this.manager = manager;
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {
// 确定Wi-Fi Direct模式是否可用
int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
// 可用
} else {
// 不可用
}
} else if (WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION.equals(action)) {
// 发现设备后会发此通知来获取数据
if (manager != null) {
// 获取扫到的设备列表也是异步,因此需要回调
manager.requestPeers(channel, new WifiP2pManager.PeerListListener() {
@Override
public void onPeersAvailable(WifiP2pDeviceList peers) {
// 参数就是扫到的wifi设备列表
Collection deviceList = peers.getDeviceList();
}
});
}
} else if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
// 当用户点击扫描到的列表上某个设备去连接的时候,连接状态会发此广播来通知,连接成功以后就可以获取WifiP2pInfo获取连接设备的具体信息了
if (manager != null) {
NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
// 请求连接设备的信息,拿到WifiP2pInfo后就可以跟别的设备通信了
manager.requestConnectionInfo(channel, new WifiP2pManager.ConnectionInfoListener() {
@Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
}
});
} else {
// ...
}
}
} else if (WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION
.equals(action)) {
//连接设备信息改变
// ...
}
}
}
3、初始化
WifiP2pManager mManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
WifiP2pManager.Channel mChannel = mManager.initialize(this, getMainLooper(), null);
WiFiDirectBroadcastReceiver mReceiver = new WiFiDirectBroadcastReceiver(mManager, mChannel);
4、查找设备
mManager.discoverPeers(channel, new WifiP2pManager.ActionListener() {
@Override
public void onSuccess() {
...
}
@Override
public void onFailure(int reasonCode) {
...
}
});
Wifi扫到设备后会统一回调,这里只是提示用户已经查找成功,具体获取扫到的列表数据要去广播(WIFI_P2P_PEERS_CHANGED_ACTION)中通过mManager.requestPeers()获取
5、连接设备
WifiP2pDevice device;
WifiP2pConfig config = new WifiP2pConfig();
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
mManager.connect(mChannel, config, new ActionListener() {
@Override
public void onSuccess() {
// 连接成功的通知
}
@Override
public void onFailure(int reason) {
// 连接失败的通知
}
});
第4部的时候已经获取到了所有wifi列表,是一个Collection
6、获取连接设备的信息
NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
manager.requestConnectionInfo(channel, new WifiP2pManager.ConnectionInfoListener() {
@Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
}
});
}
7、通信
得到WifiP2pInfo以后,我们就可以获取到连接设备的地址,这样就可以通过Socket和它通信了,我们手机端一般是连接端,所以是客户端,其他wifi设备一般是被连接端,所以是服务端,获取到流以后就可以传文件/文本等数据了
if (WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION.equals(action)) {
// 当用户点击扫描到的列表上某个设备去连接的时候,连接状态会发此广播来通知,连接成功以后就可以获取WifiP2pInfo获取连接设备的具体信息了
NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
mManager.requestConnectionInfo(channel, new WifiP2pManager.ConnectionInfoListener() {
@Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
try {
Socket socket = new Socket();
InetSocketAddress inetSocketAddress = new InetSocketAddress(info.groupOwnerAddress.getHostAddress(), 5330);
socket.connect(inetSocketAddress);
OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();
}
catch (Exception e) {
}
}
});
}
}