AndroidWiFi开发教程之WiFi热点的创建与关闭示例
小编给大家分享一下Android WiFi开发教程之WiFi热点的创建与关闭示例,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
创新互联公司专业提供四川服务器托管服务,为用户提供五星数据中心、电信、双线接入解决方案,用户可自行在线购买四川服务器托管服务,并享受7*24小时金牌售后服务。
先上效果图
Demo功能比较简单,四个按钮、两个文本和一个列表。功能主要有创建WiFi热点,关闭WiFi热点,搜索WiFi,连接WiFi,数据通讯。源码会在教程结尾提供。
本章节主要介绍WiFi热点的创建和关闭
需要用到的权限
WiFi热点的创建
/** * 创建Wifi热点 */ private void createWifiHotspot() { if (wifiManager.isWifiEnabled()) { //如果wifi处于打开状态,则关闭wifi, wifiManager.setWifiEnabled(false); } WifiConfiguration config = new WifiConfiguration(); config.SSID = WIFI_HOTSPOT_SSID; config.preSharedKey = "123456789"; 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.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); config.allowedPairwiseCiphers .set(WifiConfiguration.PairwiseCipher.CCMP); config.status = WifiConfiguration.Status.ENABLED; //通过反射调用设置热点 try { Method method = wifiManager.getClass().getMethod( "setWifiApEnabled", WifiConfiguration.class, Boolean.TYPE); boolean enable = (Boolean) method.invoke(wifiManager, config, true); if (enable) { textview.setText("热点已开启 SSID:" + WIFI_HOTSPOT_SSID + " password:123456789"); } else { textview.setText("创建热点失败"); } } catch (Exception e) { e.printStackTrace(); textview.setText("创建热点失败"); } }
这里我们需要用到一个很重要的API——WifiManager。源码中是有这么一段介绍:
This class provides the primary API for managing all aspects of Wi-Fi connectivity. Get an instance of this class by calling {@link android.content.Context#getSystemService(String) Context.getSystemService(Context.WIFI_SERVICE)}.
可以了解到,我们能够通过WifiManager来管理WiFi的连接。而通过Context.getSystemService(Context.WIFI_SERVICE)就能获取到它的实例。
wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
在开启热点的时候,我们需要确保WiFi是关闭状态,因为大部分手机都是不支持热点和WiFi是同时开启的。接着就需要创建WifiConfiguration这个类,由于配置我们所要创建的热点的属性。这里我们所需注意的主要是SSID、preSharedKey和KeyMgmt。 分别对应热点的名称、密码和加密方式。
配置完属性后,我们就可以通过Java的反射机制去创建热点。
WiFi热点的关闭
/** * 关闭WiFi热点 */ public void closeWifiHotspot() { try { Method method = wifiManager.getClass().getMethod("getWifiApConfiguration"); method.setAccessible(true); WifiConfiguration config = (WifiConfiguration) method.invoke(wifiManager); Method method2 = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class); method2.invoke(wifiManager, config, false); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }
跟创建WiFI热点一样,关闭的时候同样需要用到Java的反射机制。在调用Method的invoke方法时,将最后一个参数改成false就可以了。
Android WiFi开发教程(二)——WiFi的搜索和连接
WiFi的搜索
/* 搜索wifi热点 */ private void search() { if (!wifiManager.isWifiEnabled()) { //开启wifi wifiManager.setWifiEnabled(true); } wifiManager.startScan(); }
我们在开始搜索WiFi之前确保当前WiFi功能是处于开启状态。如果未开启,通过调用WifiManager的setWifiEnabled(boolean enable)去开启。之后调用startScan()就开始扫描附近的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 wifiListAdapter.clear(); wifiListAdapter.addAll(scanResults); } };
系统在扫描结束后,会发出WifiManager.SCAN_RESULTS_AVAILABLE_ACTION的广播,当我们的接收者接收到这个广播的时候,通过WifiManager的getScanResults()就能获取到扫描结果的集合了。ScanResult保存着每一个WiFi的信息。这里我将这个集合设置到Adapter中,并在列表中展示出来。下面是Apater中主要的代码:
@Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(mResource, parent, false); } TextView name = (TextView) convertView.findViewById(R.id.wifi_name); TextView signl = (TextView) convertView.findViewById(R.id.wifi_signal); ScanResult scanResult = getItem(position); name.setText(scanResult.SSID); int level = scanResult.level; if (level signl.setText("信号很好"); } else if (level < -50 && level >= -70) { signl.setText("信号较好"); } else if (level < -70 && level >= -80) { signl.setText("信号一般"); } else if (level < -80 && level >= -100) { signl.setText("信号较差"); } else { signl.setText("信号很差"); } return convertView; }
可以看出列表展示的数据也是比较简单,只有WiFi的名称和信号强度,这两个数据也是平时用得比较多的。获取到扫描结果后,我们就可以处理连接的逻辑了。
WiFi的连接
WiFi的连接相当于搜索就要复杂一些。首先给列表项设置点击事件,获取对应的ScanResult。
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView 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); });
取到ScanResult后我们通过他的capabilities属性判断WiFi的加密方式。接着通过isExsits(String SSID)方法判断系统是否保存着当前WiFi的信息。
private WifiConfiguration isExsits(String SSID) { List for (WifiConfiguration existingConfig : existingConfigs) { if (existingConfig.SSID.equals("\"" + SSID + "\"")) { return existingConfig; } } return null; }
如果之前连接过,则返回WiFi的配置信息,否则返回空对象。然后接着处理连接的逻辑
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的配置信息时,我们就需要用到前面获取到的加密方式判断是否需要输入密码。如果加密方式为WAP或WEP时,则弹出提示框提示用户输入WiFi密码。用户输入密码后再调用connect(WifiConfiguration config)方法
如果可以获取到所要连接WiFi的配置信息,则直接调用connect(WifiConfiguration config)。
private void connect(WifiConfiguration config) { int wcgID = wifiManager.addNetwork(config); wifiManager.enableNetwork(wcgID, true); }
直接调用WifiManger的addNetwork方法,将配置信息传进去后,会创建一个新的网络描述的身份并返回回来,如果返回来是-1,则表示创建失败。获取到身份后,调用enableNetwork方法就能开始连接WiFi了。到了这里,我们连接部分就完成了一半,接下来需要继续处理WiFi连接过程中返回来的状态。这里同样我们是需要用到广播接收者来处理。
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("连接失败"); } } }
上面是广播接收者中的关键代码。WiFi在连接的过程中系统会发出WifiManager.NETWORK_STATE_CHANGED_ACTION的广播,当接收者接收到这条广播时,获取NetworkInfo的state来判断当前的连接状态。状态值分别代表如下
NetworkInfo.State.DISCONNECTED //连接已断开 NetworkInfo.State.CONNECTED //已成功连接
除了这两个状态之外,这里还判断了其他状态
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("连接失败"); }
DetailedState中包含了很多连接状态的信息,这里只对部分状态进行处理,其他状态值解析具体如下
IDLE:空闲 SCANNING:正在扫描 CONNECTING:连接中 AUTHENTICATING:正在进行身份验证 OBTAINING_IPADDR:正在获取Ip地址 CONNECTED:已连接 SUSPENDED:已暂停 DISCONNECTING:正在断开连接 DISCONNECTED:已断开 FAILED:失败 BLOCKED:已阻止 VERIFYING_POOR_LINK:暂时关闭(网络状况不佳) CAPTIVE_PORTAL_CHECK:判断是否需要浏览器二次登录
跟蓝牙通讯一样,WiFi热点数据传输也是要运用到Socket。这里我创建了两个线程ConnectThread和ListenerThread,分别去处理数据传输和监听连接。
ConnectThread
** * 连接线程 * Created by 坤 on 2016/9/7. */ public class ConnectThread extends Thread{ private final Socket socket; private Handler handler; private InputStream inputStream; private OutputStream outputStream; public ConnectThread(Socket socket, Handler handler){ setName("ConnectThread"); this.socket = socket; this.handler = handler; } @Override public void run() { if(socket==null){ return; } handler.sendEmptyMessage(MainActivity.DEVICE_CONNECTED); try { //获取数据流 inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); byte[] buffer = new byte[1024]; int bytes; while (true){ //读取数据 bytes = inputStream.read(buffer); if (bytes > 0) { final byte[] data = new byte[bytes]; System.arraycopy(buffer, 0, data, 0, bytes); Message message = Message.obtain(); message.what = MainActivity.GET_MSG; Bundle bundle = new Bundle(); bundle.putString("MSG",new String(data)); message.setData(bundle); handler.sendMessage(message); } } } catch (IOException e) { e.printStackTrace(); } } /** * 发送数据 */ public void sendData(String msg){ if(outputStream!=null){ try { outputStream.write(msg.getBytes()); Message message = Message.obtain(); message.what = MainActivity.SEND_MSG_SUCCSEE; Bundle bundle = new Bundle(); bundle.putString("MSG",new String(msg)); message.setData(bundle); handler.sendMessage(message); } catch (IOException e) { e.printStackTrace(); Message message = Message.obtain(); message.what = MainActivity.SEND_MSG_ERROR; Bundle bundle = new Bundle(); bundle.putString("MSG",new String(msg)); message.setData(bundle); handler.sendMessage(message); } } } }
ConnectThread的构造中,传入了Socket和Handler。Socket用来获取数据以及发送数据,Handler用来更新UI了。在run方法中,我们从Socket中获取到了输入流和输出流,然后循环地读取InputStream的数据,当读取到数据时,则通过Handler将数据更新到UI上。在sendData方法中主要是通过OutputStream写入数据,然后将写入结果通过Handler更新到UI上。
ListenerThread监听线程处理的逻辑就比较简单,通过端口号获取ServerSocket后调用accept()阻塞线程,直到有设备连接上后就通过Handler更新UI。这里需要注意的是连接上的设备的端口号必须与这里的端口号相同。接着我们看看Hander获取到消息后做了哪些处理。
private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case DEVICE_CONNECTING: connectThread = new ConnectThread(listenerThread.getSocket(),handler); connectThread.start(); break; ... ... } } };
可以看到Handler获取到数据后,通过listenerThread.getSocket()将获取到Socket和handler一同创建了ConnectThread实例。
接下来就是用这两个线程来处理数据传输了。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... ... listenerThread = new ListenerThread(PORT, handler); listenerThread.start(); }
在onCreate中我们创建ListenerThread并启动它,让它监听是否有设备连接上来。当然这里需要先开启WiFi热点后才会有设备连接上来。这是开启热点并等待设备连接的情况。当然我们也可以主动去连接其他开启着热点的设备。
在监听WiFi连接情况的广播接收者中加入下面的代码
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()); if (wifiInfo.getSSID().equals(WIFI_HOTSPOT_SSID)) { //如果当前连接到的wifi是热点,则开启连接线程 new Thread(new Runnable() { @Override public void run() { try { ArrayList for (String ip : connectedIP) { if (ip.contains(".")) { Socket socket = new Socket(ip, PORT); connectThread = new ConnectThread(socket, handler); connectThread.start(); } } } catch (IOException e) { e.printStackTrace(); } } }).start(); } } else { ... } }
这里本地固定了其他设备WiFi热点的SSID,如果当前连接的WiFi的SSID跟我们之前保存的SSID一致,则证明我们连上了我们需要的WiFi热点。然后通过getConnectedIP()获取WiFi热点的IP地址,通过这个IP地址和端口号创建一个Socket,然后创建ConnectThread来处理数据传输。到这里我们可以看到,PORT和SSID这两个数据是需要两个设备事先协议好的。
最后再看看Handler完整的代码
private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case DEVICE_CONNECTING: connectThread = new ConnectThread(listenerThread.getSocket(),handler); connectThread.start(); break; case DEVICE_CONNECTED: textview.setText("设备连接成功"); break; case SEND_MSG_SUCCSEE: textview.setText("发送消息成功:" + msg.getData().getString("MSG")); break; case SEND_MSG_ERROR: textview.setText("发送消息失败:" + msg.getData().getString("MSG")); break; case GET_MSG: textview.setText("收到消息:" + msg.getData().getString("MSG")); break; } } };
以上是Android WiFi开发教程之WiFi热点的创建与关闭示例的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联行业资讯频道!
分享文章:AndroidWiFi开发教程之WiFi热点的创建与关闭示例
分享链接:http://hbruida.cn/article/pccsso.html