写这篇博客前,说一些题外话:楼主是在一家做嵌入式研发的公司当然也有软件研发,总之就是以软硬件通信为主把数据展示到前端,所
以不可避免的有 wifi 蓝牙 网口 串口 等这些通信媒介,网上的wifi通信千篇一律下面我来总结我的wifi通信 ,可以负责的告诉大家这是公
司目前在用的,起码稳定是可以维持住的,欢迎大家指证。
转载请附上本文链接squery的博客链接地址: http://blog.csdn.net/shentanweilan9
wifi通信设计到以下3点, 当然 肯定要建立一个工具类来对外暴露这三个以上的方法
- wifi列表获取展示
- wifi连接
- 获取热点ip建立socket
wifi连接通信的工具类WifiAdmin
这个工具类中主要方法如下:
- startScan() 扫描wifi
- getWifiList() 获取wifi列表
- connectToTarget() 连接wifi 这里包含两个一种是不知道加密方式的 另一种加密方式固定 两种都需要密码
- intToIp() 转换成ip地址
整个类代码如下:
package com.pswx.squery.wifi_phone.utils;
import android.content.Context;
import android.net.DhcpInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import java.lang.reflect.Method;
import java.util.List;
/**
* Created by squery on 2017/8/17.
*/
public class WifiAdmin {
public static final String SSID = "test";
public static final String PassWord = "12345678";
private final DhcpInfo mDhcpInfo;
private WifiManager mWifiManager;//wifimanager 对象
private WifiInfo mWifiInfo; // 定义WifiInfo对象
private List<ScanResult> mWifiList; // 扫描出的网络连接列表
private List<WifiConfiguration> mWifiConfiguration; // 网络连接列表
WifiManager.WifiLock mWifiLock; // 定义一个WifiLock
private static final int NOPASSWORD = 0;
private static final int PASSWORD_WPA = 1;
private static final int PASSWORD_WEP = 2;
private static final int PASSWORD_WPA2 = 3;
// 构造器
public WifiAdmin(Context context) {
// 取得WifiManager对象
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
// 取得WifiInfo对象
mWifiInfo = mWifiManager.getConnectionInfo();
mDhcpInfo = mWifiManager.getDhcpInfo();
}
//打开wifi
public void openWifi() {
if (!mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(true);
}
}
//关闭WIFI
public void closeWifi() {
if (mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(false);
}
}
//创建热点
public void createAp() {
if (mWifiManager.isWifiEnabled()) {
mWifiManager.setWifiEnabled(false);
}
try {
WifiConfiguration apConfiguration = new WifiConfiguration();
apConfiguration.SSID = WifiAdmin.SSID;
apConfiguration.preSharedKey = WifiAdmin.PassWord;
apConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
Method method = mWifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
method.invoke(mWifiManager, apConfiguration, true);
} catch (Exception e) {
e.printStackTrace();
}
}
//关闭WiFi热点
public void closeWifiAp() {
if (isWifiApEnabled()) {
try {
Method method = mWifiManager.getClass().getMethod("getWifiApConfiguration");
method.setAccessible(true);
WifiConfiguration config = (WifiConfiguration) method.invoke(mWifiManager);
Method method2 = mWifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
method2.invoke(mWifiManager, config, false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//热点开关是否打开
public boolean isWifiApEnabled() {
try {
Method method = mWifiManager.getClass().getMethod("isWifiApEnabled");
method.setAccessible(true);
return (Boolean) method.invoke(mWifiManager);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
// 检查当前WIFI状态
public int checkState() {
return mWifiManager.getWifiState();
}
// 锁定WifiLock
public void acquireWifiLock() {
mWifiLock.acquire();
}
// 解锁WifiLock
public void releaseWifiLock() {
// 判断时候锁定
if (mWifiLock.isHeld()) {
mWifiLock.acquire();
}
}
// 创建一个WifiLock
public void creatWifiLock() {
mWifiLock = mWifiManager.createWifiLock("Test");
}
// 得到配置好的网络
public List<WifiConfiguration> getConfiguration() {
return mWifiConfiguration;
}
// 指定配置好的网络进行连接
public void connectConfiguration(int index) {
// 索引大于配置好的网络索引返回
if (index > mWifiConfiguration.size()) {
return;
}
// 连接配置好的指定ID的网络
mWifiManager.enableNetwork(mWifiConfiguration.get(index).networkId, true);
}
/**
* 扫描WIFI
*/
public void startScan() {
mWifiManager.startScan();
// 得到扫描结果
mWifiList = mWifiManager.getScanResults();
// 得到配置好的网络连接
mWifiConfiguration = mWifiManager.getConfiguredNetworks();
}
// 得到网络列表
public List<ScanResult> getWifiList() {
return mWifiList;
}
// 查看扫描结果
public StringBuilder lookUpScan() {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < mWifiList.size(); i++) {
stringBuilder.append("Index_" + new Integer(i + 1).toString() + ":");
// 将ScanResult信息转换成一个字符串包
// 其中把包括:BSSID、SSID、capabilities、frequency、level
stringBuilder.append((mWifiList.get(i)).toString());
stringBuilder.append("/n");
}
return stringBuilder;
}
// 得到接入点的BSSID
public String getSSID() {
return (mWifiInfo == null) ? "NULL" : mWifiInfo.getSSID();
}
// 得到MAC地址
public String getMacAddress() {
return (mWifiInfo == null) ? "NULL" : mWifiInfo.getMacAddress();
}
// 得到接入点的BSSID
public String getBSSID() {
return (mWifiInfo == null) ? "NULL" : mWifiInfo.getBSSID();
}
// 得到IP地址
public int getIPAddress() {
return (mWifiInfo == null) ? 0 : mWifiInfo.getIpAddress();
}
public int getServerIPAddress() {
return (mWifiInfo == null) ? 0 : mDhcpInfo.serverAddress;
}
// 得到连接的ID
public int getNetworkId() {
return (mWifiInfo == null) ? 0 : mWifiInfo.getNetworkId();
}
// 得到WifiInfo的所有信息包
public String getWifiInfo() {
return (mWifiInfo == null) ? "NULL" : mWifiInfo.toString();
}
// 添加一个网络并连接
public int addNetwork(WifiConfiguration wcg) {
int wcgID = mWifiManager.addNetwork(wcg);
boolean b = mWifiManager.enableNetwork(wcgID, true);
mWifiManager.reassociate();
LogUtils.e("b--" + b);
return wcgID;
}
// 创建wificonfig
public WifiConfiguration createWifiConfig(String SSID, String Password, int Type) {
WifiConfiguration config = new WifiConfiguration();
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
config.SSID = "\"" + SSID + "\"";
//如果设备大于6.0配置的时候就不需要双引号,加了就连接不上了
if (Build.VERSION.SDK_INT >= 23) {
config.SSID = SSID;
} else {
config.SSID = "\"" + SSID + "\"";
}
WifiConfiguration tempConfig = isExsits(SSID);
if (tempConfig != null) {// 去除自动保存的 wifi
disconnectWifi(tempConfig.networkId);
}
if (Type == NOPASSWORD) // WIFICIPHER_NOPASS
{
config.hiddenSSID = true;
//config.wepKeys[0] = "";
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
//config.wepTxKeyIndex = 0;
}
if (Type == PASSWORD_WPA) // WIFICIPHER_WPA
{
config.preSharedKey = "\"" + Password + "\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms
.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
config.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
config.status = WifiConfiguration.Status.ENABLED;
}
if (Type == PASSWORD_WEP) // WIFICIPHER_WEP
{
config.hiddenSSID = true;
config.wepKeys[0] = "\"" + Password + "\"";
config.allowedAuthAlgorithms
.set(WifiConfiguration.AuthAlgorithm.SHARED);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
config.allowedGroupCiphers
.set(WifiConfiguration.GroupCipher.WEP104);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.wepTxKeyIndex = 0;
}
return config;
}
/**
* 判断wifi是否存在
*
* @param SSID
* @return
*/
public WifiConfiguration isExsits(String SSID) {
List<WifiConfiguration> existingConfigs = mWifiManager.getConfiguredNetworks();
if (!ArrayUtils.isEmpty(existingConfigs)) {
for (WifiConfiguration existingConfig : existingConfigs) {
if (existingConfig.SSID.equals("\"" + SSID + "\"")) {
return existingConfig;
}
}
}
return null;
}
/**
* 连接目标热点
*
* @param scanResult 热点的加密方式
*/
public int connectToTarget(ScanResult scanResult, String password) {
int mNetworkID = 0;
int password_type = 0;
WifiConfiguration mTargetWifiCfg;
if (scanResult != null) {
if (scanResult.capabilities.contains("WPA") || scanResult.capabilities.contains("wpa")) {
password_type = PASSWORD_WPA;
} else if (scanResult.capabilities.contains("WEP") || scanResult.capabilities.contains("wep")) {
password_type = PASSWORD_WEP;
} else if (scanResult.capabilities.contains("WPA2") || scanResult.capabilities.contains("wpa2")) {
password_type = PASSWORD_WPA2;
} else {
password_type = NOPASSWORD;
}
}
//LogUtils.e(scanResult.SSID+"::::::::::::::::::::" + password_type); //password_type=1 WPA
mTargetWifiCfg = createWifiConfig(scanResult.SSID, password, password_type);// 获得wificonfig
mNetworkID = addNetwork(mTargetWifiCfg);
return mNetworkID;
}
public int connectToTarget(String SSID, String password) {
int mNetworkID = 0;
int password_type = PASSWORD_WPA;
WifiConfiguration mTargetWifiCfg;
mTargetWifiCfg = createWifiConfig(SSID, password, password_type);// 获得wificonfig
mNetworkID = addNetwork(mTargetWifiCfg);
return mNetworkID;
}
// 断开指定ID的网络
public void disconnectWifi(int netId) {
mWifiManager.disableNetwork(netId);
mWifiManager.disconnect();
mWifiManager.removeNetwork(netId);
}
/**
* 转换IP地址
*
* @param i
* @return
*/
public String intToIp(int i) {
return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF)
+ "." + ((i >> 24) & 0xFF);
}
}
wifi列表获取与展示
wifi列表的获取,大致思路是:扫描附近wifi这个时候android系统会广播一条通知SCAN_RESULTS_AVAILABLE_ACTION
注意:** 每扫描一次会广播单条具体的通知,这个不同于wifi连接的通知(wifi连接通知 不同手机广播的通知条数不同 通知类型也不同) ** 然后就是在广播监听里面获取到wifi列表 并更新adapter 也就是更新ui
发送广播 可以在一进入页面就去扫描 也可以通过按钮点击事件来触发 代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
setLayoutId(R.layout.activity_wifi_list);
super.onCreate(savedInstanceState);
initTitle(R.mipmap.left_arrow, R.string.back, "wifi列表", R.string.showelctric, 0);
mWifiAdmin.openWifi();// 第一次进来时候显示列表
scanFlag = 1;
mWifiAdmin.startScan();//开始扫描 发送通知
AppUtils.getInstance().showLoading(this);
}
@OnClick({R.id.lear_left, R.id.btn_scan})
void onClicks(View v) {
int flag = 0;
switch (v.getId()) {
case R.id.lear_left:
mConnectThread = null;
finish();
break;
case R.id.btn_scan:
mWifiAdmin.openWifi();
mWifiAdmin.startScan();//开始扫描 发送通知
AppUtils.getInstance().showLoading(this);
scanFlag = 1;
break;
}
}
获取wifi列表 并更新adapter 当然要注册广播监听 页面销毁时候要注销广播监听 代码如下:
@Override
public void onResume() {// 注册广播
super.onResume();
registerReceiver(wifiScanReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
}
@Override
public void onPause() {// 注销广播
super.onPause();
unregisterReceiver(wifiScanReceiver);
}
private BroadcastReceiver wifiScanReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context arg0, Intent intent) {
if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {//扫描完毕 更新Ui
AppUtils.getInstance().dismissLoading(WifiListActivity.this);
mWifiList.clear();
for (ScanResult scanResult : mWifiAdmin.getWifiList()) {
if (scanResult.SSID.startsWith("orbis_")) // 过滤掉其他的,添加自己需要的wifi热点
mWifiList.add(new CustomScan(false, scanResult));
}
//LogUtils.e("扫描的wifi列表:::\t" + mWifiAdmin.lookUpScan().toString());
mCommonAdapter.notifyDataSetChanged();
}
}
};
wifi连接以及处理连接时延问题
wifi连接:就是连接具体的wifi热点 这个很好理解
连接时延: 执行了连接代码 但是系统反应需要一段时间并不是马上连接上wifi热点的,这个时间就是连接时延
注意:如果大家不去考虑这个时延,那样会出现很大的问题,那样我们通过ip获取建立socket时候会报一个socket untouched异常 具体异常名字我没有详记
- 首先我们不能通过
mWifiAdmin.connectToTarget(customScan.getScanResult(), "123456789");
发送的通知 以及捕
获具体通知进行wifi连接的处理 因为上述我已经阐明 通知的种类以及条数都不是固定的 - 我们不能认为
mWifiAdmin.connectToTarget(customScan.getScanResult(), "123456789");
执行完瞬间就可以连
接上wifi了不能单纯的只根据这个 返回值是不是-1 来判断连接wifi成功与否 当然返回-1 肯定是失败 但是返回!-1的时
候,由于连接是需要一段时间的,所以我们要加一个循环判断是否连接成功的线程 这样就可以在连接成功后进行我们自己的业务处理了. - 循环判断wifi连接成功的线程需要注意 1.循环次数 2.失败后的界面反馈 可以起线程 那样需要handler来传递
失败后的信息 也可以用异步任务
1和2 有两种可能 通过wifi列表连接具体wifi的 也可以 直接连接某个wifi热点的 代码如下:
@OnClick({R.id.btn_jump_temp, R.id.btn_scan})
void onClicks(View v) {
int flag = 0;
switch (v.getId()) {
case R.id.btn_scan:
mWifiAdmin.openWifi();
mWifiAdmin.startScan();//开始扫描 发送通知
AppUtils.getInstance().showLoading(this);
scanFlag = 1;
break;
case R.id.btn_jump_temp:
if (!ArrayUtils.isEmpty(mWifiList)) {
for (CustomScan customScan : mWifiList) {
if (customScan.isSelected()) {
flag = 1;
if (!TextUtils.isEmpty(mEdiPhoneNum.getText())) {
int i = mWifiAdmin.connectToTarget(customScan.getScanResult(), "123456789");// 发送通知
if (i != -1) {
AppUtils.getInstance().showLoading(WifiListActivity.this);
jumpNum = 1;
mConnectThread = new ConnectThread();
mConnectThread.start();
mLearRight.setClickable(true);
} else {
Toast.makeText(WifiListActivity.this, "连接失败", Toast.LENGTH_LONG).show();
mLearRight.setClickable(true);
}
}
}
}
if (flag == 0) {
Toast.makeText(this, "请先选择一个wifi热点", Toast.LENGTH_LONG).show();
mLearRight.setClickable(true);
return;
}
} else {
Toast.makeText(this, "没有wifi列表", Toast.LENGTH_LONG).show();
mLearRight.setClickable(true);
return;
}
break;
}
}
@OnClick({R.id.btn_jump_temp, R.id.btn_scan})
void onClicks(View v) {
int flag = 0;
switch (v.getId()) {
case R.id.btn_scan:
mWifiAdmin.openWifi();
mWifiAdmin.startScan();//开始扫描 发送通知
AppUtils.getInstance().showLoading(this);
scanFlag = 1;
break;
case R.id.btn_jump_temp:
if (!DeviceUtils.isWifiConnected(WifiListNullActivity.this)
|| TextUtils.isEmpty(PreferenceSettingUtils.getIP(this))) {
jumpNum = 1;
AppUtils.getInstance().showLoading(WifiListNullActivity.this, "分机连接中...");
int i = mWifiAdmin.connectToTarget(PreferenceSettingUtils.getWifiName(WifiListNullActivity.this),
PreferenceSettingUtils.getWifiPassword(WifiListNullActivity.this));// 发送通知
if (i == -1) {
Toast.makeText(WifiListNullActivity.this, "附近没有找到指定的分机", Toast.LENGTH_LONG).show();
}
mConnectThread = new ConnectThread();
mConnectThread.start();
} else {
startActivity(new Intent(this, DetctTempActivity.class));
}
break;
}
}
3.也有两种 起线程 和 异步任务 代码如下:
public class ConnectThread extends Thread {// 连接Thread
@Override
public void run() {
int count = 0;
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (DeviceUtils.isWifiConnected(WifiListActivity.this)) {// 连接成功
WifiAdmin wifiAdmin = new WifiAdmin(WifiListActivity.this);
String ip = wifiAdmin.intToIp(wifiAdmin.getServerIPAddress());
PreferenceSettingUtils.setIP(WifiListActivity.this, ip);
// 启动net work
try {
Message msg = new Message();
msg.obj = "WIFI连接成功!";
msg.what = 1;
LinkDetectedHandler.sendMessage(msg);
} catch (Exception e) {
Message msg = new Message();
msg.obj = "获取socket失败";
msg.what = 0;
LinkDetectedHandler.sendMessage(msg);
e.printStackTrace();
}
break;
}
if (count++ > 10) {
Message msg = new Message();
msg.obj = "WIFI连接失败!";
msg.what = -1;
LinkDetectedHandler.sendMessage(msg);
break;
}
}
super.run();
}
}
public class SocketConnectTask extends AsyncTask<Integer, Void, Integer> {
//后面尖括号内分别是参数(线程休息时间),进度(publishProgress用到),返回值 类型
private Context mContext;
private Thread mThread;
public SocketConnectTask(Context context, Thread thread) {
mContext = context;
mThread = thread;
}
public SocketConnectTask(Context context) {
mContext = context;
}
/*
* 第一个执行的方法
* 执行时机:在执行实际的后台操作前,被UI 线程调用
* 作用:可以在该方法中做一些准备工作,如在界面上显示一个进度条,或者一些控件的实例化,这个方法可以不用实现。
* @see android.os.AsyncTask#onPreExecute()
*/
@Override
protected void onPreExecute() {
//AppUtils.getInstance().showLoading(mContext);
super.onPreExecute();
}
/*
* 执行时机:在onPreExecute 方法执行后马上执行,该方法运行在后台线程中
* 作用:主要负责执行那些很耗时的后台处理工作。该方法是抽象方法,子类必须实现。
* @see android.os.AsyncTask#doInBackground(Params[])
*/
@Override
protected Integer doInBackground(Integer... params) {
int count = 0;
while (true) {
try {
Thread.sleep(2000);
if (DeviceUtils.isWifiConnected(mContext)) {
Thread.sleep(1000);
WifiAdmin wifiAdmin = new WifiAdmin(mContext);
String ip = wifiAdmin.intToIp(wifiAdmin.getServerIPAddress());
PreferenceSettingUtils.setIP(mContext, ip);
return 1;
}
if (count++ > 10) return -1;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/*
* 执行时机:在doInBackground 执行完成后,将被UI 线程调用
* 作用:后台的计算结果将通过该方法传递到UI 线程,并且在界面上展示给用户
* result:上面doInBackground执行后的返回值,所以这里是"执行完毕"
* @see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
@Override
protected void onPostExecute(Integer result) {
//AppUtils.getInstance().dismissLoading(mContext);
switch (result) {
case 1:
if (mThread != null) {
mThread.start();
}
Toast.makeText(mContext, "wifi连接成功", Toast.LENGTH_SHORT).show();
break;
case -1:
Toast.makeText(mContext, "wifi连接异常", Toast.LENGTH_SHORT).show();
break;
}
super.onPostExecute(result);
}
}
总结:wifi连接就这些了 其主要问题 就是在连接到热点 建立socket的时候一定要注意这个时延 不能去通过通知的捕获来处理业务逻辑socket建立的成功与否 是在wifi热点必须连接成功的前提下。下面章节我可能会叙述一些 wifi通信协议以及通信中线程中处理业务的问题。如有疑问可以联系我。
扫描二维码关注公众号,回复: 1604344 查看本文章