新聞中心
這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
AndroidWiFi開發(fā)教程之WiFi熱點的創(chuàng)建與關(guān)閉示例
小編給大家分享一下Android WiFi開發(fā)教程之WiFi熱點的創(chuàng)建與關(guān)閉示例,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
創(chuàng)新互聯(lián)公司專業(yè)提供四川服務(wù)器托管服務(wù),為用戶提供五星數(shù)據(jù)中心、電信、雙線接入解決方案,用戶可自行在線購買四川服務(wù)器托管服務(wù),并享受7*24小時金牌售后服務(wù)。
先上效果圖
Demo功能比較簡單,四個按鈕、兩個文本和一個列表。功能主要有創(chuàng)建WiFi熱點,關(guān)閉WiFi熱點,搜索WiFi,連接WiFi,數(shù)據(jù)通訊。源碼會在教程結(jié)尾提供。
本章節(jié)主要介紹WiFi熱點的創(chuàng)建和關(guān)閉
需要用到的權(quán)限
WiFi熱點的創(chuàng)建
/** * 創(chuàng)建Wifi熱點 */ private void createWifiHotspot() { if (wifiManager.isWifiEnabled()) { //如果wifi處于打開狀態(tài),則關(guān)閉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);//開放系統(tǒng)認證 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; //通過反射調(diào)用設(shè)置熱點 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("創(chuàng)建熱點失敗"); } } catch (Exception e) { e.printStackTrace(); textview.setText("創(chuàng)建熱點失敗"); } }
這里我們需要用到一個很重要的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是關(guān)閉狀態(tài),因為大部分手機都是不支持熱點和WiFi是同時開啟的。接著就需要創(chuàng)建WifiConfiguration這個類,由于配置我們所要創(chuàng)建的熱點的屬性。這里我們所需注意的主要是SSID、preSharedKey和KeyMgmt。 分別對應(yīng)熱點的名稱、密碼和加密方式。
配置完屬性后,我們就可以通過Java的反射機制去創(chuàng)建熱點。
WiFi熱點的關(guān)閉
/** * 關(guān)閉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(); } }
跟創(chuàng)建WiFI熱點一樣,關(guān)閉的時候同樣需要用到Java的反射機制。在調(diào)用Method的invoke方法時,將最后一個參數(shù)改成false就可以了。
Android WiFi開發(fā)教程(二)——WiFi的搜索和連接
WiFi的搜索
/* 搜索wifi熱點 */ private void search() { if (!wifiManager.isWifiEnabled()) { //開啟wifi wifiManager.setWifiEnabled(true); } wifiManager.startScan(); }
我們在開始搜索WiFi之前確保當(dāng)前WiFi功能是處于開啟狀態(tài)。如果未開啟,通過調(diào)用WifiManager的setWifiEnabled(boolean enable)去開啟。之后調(diào)用startScan()就開始掃描附近的WiFi了。而獲取掃描的結(jié)果我們就需要創(chuàng)建一個廣播接收者來處理。
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); } };
系統(tǒng)在掃描結(jié)束后,會發(fā)出WifiManager.SCAN_RESULTS_AVAILABLE_ACTION的廣播,當(dāng)我們的接收者接收到這個廣播的時候,通過WifiManager的getScanResults()就能獲取到掃描結(jié)果的集合了。ScanResult保存著每一個WiFi的信息。這里我將這個集合設(shè)置到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; }
可以看出列表展示的數(shù)據(jù)也是比較簡單,只有WiFi的名稱和信號強度,這兩個數(shù)據(jù)也是平時用得比較多的。獲取到掃描結(jié)果后,我們就可以處理連接的邏輯了。
WiFi的連接
WiFi的連接相當(dāng)于搜索就要復(fù)雜一些。首先給列表項設(shè)置點擊事件,獲取對應(yīng)的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)方法判斷系統(tǒng)是否保存著當(dāng)前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); }
當(dāng)沒有獲取到所要連接WiFi的配置信息時,我們就需要用到前面獲取到的加密方式判斷是否需要輸入密碼。如果加密方式為WAP或WEP時,則彈出提示框提示用戶輸入WiFi密碼。用戶輸入密碼后再調(diào)用connect(WifiConfiguration config)方法
如果可以獲取到所要連接WiFi的配置信息,則直接調(diào)用connect(WifiConfiguration config)。
private void connect(WifiConfiguration config) { int wcgID = wifiManager.addNetwork(config); wifiManager.enableNetwork(wcgID, true); }
直接調(diào)用WifiManger的addNetwork方法,將配置信息傳進去后,會創(chuàng)建一個新的網(wǎng)絡(luò)描述的身份并返回回來,如果返回來是-1,則表示創(chuàng)建失敗。獲取到身份后,調(diào)用enableNetwork方法就能開始連接WiFi了。到了這里,我們連接部分就完成了一半,接下來需要繼續(xù)處理WiFi連接過程中返回來的狀態(tài)。這里同樣我們是需要用到廣播接收者來處理。
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("已連接到網(wǎng)絡(luò):" + 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("連接失敗"); } } }
上面是廣播接收者中的關(guān)鍵代碼。WiFi在連接的過程中系統(tǒng)會發(fā)出WifiManager.NETWORK_STATE_CHANGED_ACTION的廣播,當(dāng)接收者接收到這條廣播時,獲取NetworkInfo的state來判斷當(dāng)前的連接狀態(tài)。狀態(tài)值分別代表如下
NetworkInfo.State.DISCONNECTED //連接已斷開 NetworkInfo.State.CONNECTED //已成功連接
除了這兩個狀態(tài)之外,這里還判斷了其他狀態(tài)
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中包含了很多連接狀態(tài)的信息,這里只對部分狀態(tài)進行處理,其他狀態(tài)值解析具體如下
IDLE:空閑 SCANNING:正在掃描 CONNECTING:連接中 AUTHENTICATING:正在進行身份驗證 OBTAINING_IPADDR:正在獲取Ip地址 CONNECTED:已連接 SUSPENDED:已暫停 DISCONNECTING:正在斷開連接 DISCONNECTED:已斷開 FAILED:失敗 BLOCKED:已阻止 VERIFYING_POOR_LINK:暫時關(guān)閉(網(wǎng)絡(luò)狀況不佳) CAPTIVE_PORTAL_CHECK:判斷是否需要瀏覽器二次登錄
跟藍牙通訊一樣,WiFi熱點數(shù)據(jù)傳輸也是要運用到Socket。這里我創(chuàng)建了兩個線程ConnectThread和ListenerThread,分別去處理數(shù)據(jù)傳輸和監(jiān)聽連接。
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 { //獲取數(shù)據(jù)流 inputStream = socket.getInputStream(); outputStream = socket.getOutputStream(); byte[] buffer = new byte[1024]; int bytes; while (true){ //讀取數(shù)據(jù) 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(); } } /** * 發(fā)送數(shù)據(jù) */ 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的構(gòu)造中,傳入了Socket和Handler。Socket用來獲取數(shù)據(jù)以及發(fā)送數(shù)據(jù),Handler用來更新UI了。在run方法中,我們從Socket中獲取到了輸入流和輸出流,然后循環(huán)地讀取InputStream的數(shù)據(jù),當(dāng)讀取到數(shù)據(jù)時,則通過Handler將數(shù)據(jù)更新到UI上。在sendData方法中主要是通過OutputStream寫入數(shù)據(jù),然后將寫入結(jié)果通過Handler更新到UI上。
ListenerThread監(jiān)聽線程處理的邏輯就比較簡單,通過端口號獲取ServerSocket后調(diào)用accept()阻塞線程,直到有設(shè)備連接上后就通過Handler更新UI。這里需要注意的是連接上的設(shè)備的端口號必須與這里的端口號相同。接著我們看看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獲取到數(shù)據(jù)后,通過listenerThread.getSocket()將獲取到Socket和handler一同創(chuàng)建了ConnectThread實例。
接下來就是用這兩個線程來處理數(shù)據(jù)傳輸了。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... ... listenerThread = new ListenerThread(PORT, handler); listenerThread.start(); }
在onCreate中我們創(chuàng)建ListenerThread并啟動它,讓它監(jiān)聽是否有設(shè)備連接上來。當(dāng)然這里需要先開啟WiFi熱點后才會有設(shè)備連接上來。這是開啟熱點并等待設(shè)備連接的情況。當(dāng)然我們也可以主動去連接其他開啟著熱點的設(shè)備。
在監(jiān)聽WiFi連接情況的廣播接收者中加入下面的代碼
if (info.getState().equals(NetworkInfo.State.CONNECTED)) { WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); final WifiInfo wifiInfo = wifiManager.getConnectionInfo(); text_state.setText("已連接到網(wǎng)絡(luò):" + wifiInfo.getSSID()); if (wifiInfo.getSSID().equals(WIFI_HOTSPOT_SSID)) { //如果當(dāng)前連接到的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 { ... } }
這里本地固定了其他設(shè)備WiFi熱點的SSID,如果當(dāng)前連接的WiFi的SSID跟我們之前保存的SSID一致,則證明我們連上了我們需要的WiFi熱點。然后通過getConnectedIP()獲取WiFi熱點的IP地址,通過這個IP地址和端口號創(chuàng)建一個Socket,然后創(chuàng)建ConnectThread來處理數(shù)據(jù)傳輸。到這里我們可以看到,PORT和SSID這兩個數(shù)據(jù)是需要兩個設(shè)備事先協(xié)議好的。
最后再看看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("設(shè)備連接成功"); break; case SEND_MSG_SUCCSEE: textview.setText("發(fā)送消息成功:" + msg.getData().getString("MSG")); break; case SEND_MSG_ERROR: textview.setText("發(fā)送消息失敗:" + msg.getData().getString("MSG")); break; case GET_MSG: textview.setText("收到消息:" + msg.getData().getString("MSG")); break; } } };
以上是Android WiFi開發(fā)教程之WiFi熱點的創(chuàng)建與關(guān)閉示例的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
新聞名稱:AndroidWiFi開發(fā)教程之WiFi熱點的創(chuàng)建與關(guān)閉示例
標題路徑:http://fisionsoft.com.cn/article/pccsso.html