Android連接指定Wifi的方法實例代碼
本篇文章主要記錄一下Android中打開Wifi、獲取Wifi接入點信息及連接指接入點的方法。
自己寫的demo主要用于測試接口的基本功能,因此界面及底層邏輯比較粗糙。
demo的整體界面如下所示:

上圖中的OPEN按鍵負責(zé)開啟Wifi;
GET按鍵負責(zé)獲取掃描到的接入點信息。
當(dāng)獲取到接入點信息后,我選取了其中的名稱及信號強度,以列表的形式顯示在主界面下方,如下圖:

當(dāng)點擊列表中的Item時,就會去連接對應(yīng)的接入點。
自己的邏輯比較簡單,測試時的代碼,假定連接的是不許要密碼或密碼已知的接入點。
demo的布局文件就不介紹了,就是Button和RecyclerView。
主要記錄一下,使用到的核心代碼。
....................
//Open按鍵點擊后的邏輯
mOpenWifiButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//WifiManager的isWifiEnabled接口,用于判斷Wifi開關(guān)是否已經(jīng)開啟
if (!mWifiManager.isWifiEnabled()) {
//setWifiEnabled接口用于開啟Wifi
mWifiManager.setWifiEnabled(true);
mMainHandler.post(mMainRunnable);
}
}
});
....................
mMainRunnable的代碼如下,主要用于判斷Wifi是否開啟成功。
................
private Runnable mMainRunnable = new Runnable() {
@Override
public void run() {
if (mWifiManager.isWifiEnabled()) {
//開啟成功后,使能Get按鍵
mGetWifiInfoButton.setEnabled(true);
} else {
mMainHandler.postDelayed(mMainRunnable, 1000);
}
}
};
..............
這部分代碼,主要使用了WifiManager的公有接口,開啟Wifi開關(guān)及判斷開啟狀態(tài)。
這部分操作需要的權(quán)限是:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
Get按鍵被點擊后,對應(yīng)的代碼如下:
.................
mGetWifiInfoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mWifiManager.isWifiEnabled()) {
//getScanResults接口將返回List<ScanResult>
//ScanResult中保留了每個接入點的基本信息
mScanResultList = mWifiManager.getScanResults();
//多個接入點可能攜帶相同的信息,形成一個整體的Wifi覆蓋網(wǎng)絡(luò)
//因此,篩除一些冗余信息
sortList(mScanResultList);
//我使用的是RecyclerView,得到數(shù)據(jù)后,刷新界面進行顯示
mWifiInfoRecyclerView.getAdapter().notifyDataSetChanged();
}
}
});
.................
上面這部分代碼也比較簡單,主要利用WifiManager的getScanResults接口,獲取終端探索到的接入點信息。
其中,sortList的代碼如下:
..............
private void sortList(List<ScanResult> list) {
TreeMap<String, ScanResult> map = new TreeMap<>();
//demo中僅按照SSID進行篩選
//實際使用時,還可以參考信號強度等條件
for (ScanResult scanResult : list) {
map.put(scanResult.SSID, scanResult);
}
list.clear();
list.addAll(map.values());
}
.............
這部分代碼唯一需要注意的地方是,需要申明權(quán)限:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
同時,在高版本中還需要主動獲取運行時權(quán)限。
權(quán)限的要求,是由WifiServiceImpl的實現(xiàn)決定的,我們以Android 7.0為例,看看對應(yīng)的代碼:
public List<ScanResult> getScanResults(String callingPackage) {
//這里要求的是ACCESS_WIFI_STATE
enforceAccessPermission();
............
try {
...........
if (!canReadPeerMacAddresses && !isActiveNetworkScorer
//在checkCallerCanAccessScanResults中檢查了ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION
//如果沒有這兩個權(quán)限,就會返回一個empty List
&& !checkCallerCanAccessScanResults(callingPackage, uid)) {
return new ArrayList<ScanResult>();
}
...........
} fianlly {
..........
}
}
獲取到信息后,就可以顯示和點擊列表中的Item了。
由于自己使用的是RecyclerView,因此這部分工作全部交給了對應(yīng)ViewHolder:
...............
private class ScanResultViewHolder extends RecyclerView.ViewHolder {
private View mView;
private TextView mWifiName;
private TextView mWifiLevel;
ScanResultViewHolder(View itemView) {
super(itemView);
mView = itemView;
mWifiName = (TextView) itemView.findViewById(R.id.ssid);
mWifiLevel = (TextView) itemView.findViewById(R.id.level);
}
void bindScanResult(final ScanResult scanResult) {
//將接入點的名稱和強度顯示到界面上
mWifiName.setText(
getString(R.string.scan_wifi_name, "" + scanResult.SSID));
mWifiLevel.setText(
getString(R.string.scan_wifi_level, "" + scanResult.level));
//點擊Item后,就連接對應(yīng)的接入點
mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//createWifiConfig主要用于構(gòu)建一個WifiConfiguration,代碼中的例子主要用于連接不需要密碼的Wifi
//WifiManager的addNetwork接口,傳入WifiConfiguration后,得到對應(yīng)的NetworkId
int netId = mWifiManager.addNetwork(createWifiConfig(scanResult.SSID, "", WIFICIPHER_NOPASS));
//WifiManager的enableNetwork接口,就可以連接到netId對應(yīng)的wifi了
//其中boolean參數(shù),主要用于指定是否需要斷開其它Wifi網(wǎng)絡(luò)
boolean enable = mWifiManager.enableNetwork(netId, true);
Log.d("ZJTest", "enable: " + enable);
//可選操作,讓W(xué)ifi重新連接最近使用過的接入點
//如果上文的enableNetwork成功,那么reconnect同樣連接netId對應(yīng)的網(wǎng)絡(luò)
//若失敗,則連接之前成功過的網(wǎng)絡(luò)
boolean reconnect = mWifiManager.reconnect();
Log.d("ZJTest", "reconnect: " + reconnect);
}
});
}
}
.................
以上就是連接指定Wifi的基本套路,從代碼中容易看出,關(guān)鍵問題是如何創(chuàng)建出有效的WifiConfiguration。
自己測試時,初始創(chuàng)建WifiConfiguration失敗,手機怎么都沒法連接到熱點上,后來修改后,基本功能終于能夠?qū)崿F(xiàn):
....................
private static final int WIFICIPHER_NOPASS = 0;
private static final int WIFICIPHER_WEP = 1;
private static final int WIFICIPHER_WPA = 2;
private WifiConfiguration createWifiConfig(String ssid, String password, int type) {
//初始化WifiConfiguration
WifiConfiguration config = new WifiConfiguration();
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
//指定對應(yīng)的SSID
config.SSID = "\"" + ssid + "\"";
//如果之前有類似的配置
WifiConfiguration tempConfig = isExist(ssid);
if(tempConfig != null) {
//則清除舊有配置
mWifiManager.removeNetwork(tempConfig.networkId);
}
//不需要密碼的場景
if(type == WIFICIPHER_NOPASS) {
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
//以WEP加密的場景
} else if(type == WIFICIPHER_WEP) {
config.hiddenSSID = true;
config.wepKeys[0]= "\""+password+"\"";
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.wepTxKeyIndex = 0;
//以WPA加密的場景,自己測試時,發(fā)現(xiàn)熱點以WPA2建立時,同樣可以用這種配置連接
} else if(type == 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.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
config.status = WifiConfiguration.Status.ENABLED;
}
return config;
}
.................
private WifiConfiguration isExist(String ssid) {
List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
for (WifiConfiguration config : configs) {
if (config.SSID.equals("\""+ssid+"\"")) {
return config;
}
}
return null;
}
.................
自己寫完demo后,以一個手機建立熱點,分別測試了有密碼和無密碼的場景(對應(yīng)的,需要修改createWifiConfig的傳入?yún)?shù))。
發(fā)現(xiàn)demo運行的手機在兩種場景下,均能夠連接到指定熱點。
Demo地址如下:
https://github.com/ZhangJianIsAStark/Demos/tree/master/wifitest
在本文的最后,補充一下終端作為熱點時的接口。
public boolean isWifiApEnabled()
具有@SystemApi、@hide注解的公有接口,判斷手機的熱點是否開啟。
在Android 5.1之前,這個接口沒有@SystemApi注解,
于是有很多代碼會利用Java發(fā)射機制,獲取該方法并判斷手機熱點是否開啟。
現(xiàn)在那些老代碼已經(jīng)沒法使用了。
現(xiàn)在的做法(以5.1以上為例),應(yīng)該利用廣播接收器監(jiān)聽WifiManager中定義的WIFI_AP_STATE_CHANGED_ACTION。
注意到該Action也有@SystemApi注解,所以要直接監(jiān)聽對應(yīng)的字符串,示例如下(上面鏈接中的demo也有涉及):
...................
private BroadcastReceiver mBroadcastReceiver;
private void registerBroadcastReceiver() {
mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//收到廣播后,利用"wifi_state"的字段,得到AP的狀態(tài)
int state = intent.getIntExtra("wifi_state", 11);
Log.d("ZJTest", "AP state: " + state);
}
};
IntentFilter intentFilter = new IntentFilter();
//添加Action對應(yīng)的字符信息
intentFilter.addAction("android.net.wifi.WIFI_AP_STATE_CHANGED");
this.registerReceiver(mBroadcastReceiver, intentFilter);
}
.........
private void unregisterBroadcastReceiver() {
this.unregisterReceiver(mBroadcastReceiver);
}
..........
我暫時沒有深究Wifi模塊開啟AP的流程。
不過從自己的測試結(jié)果來看,Wifi開啟或關(guān)閉AP時,推測發(fā)送的應(yīng)該是Sticky類型的廣播。
于是,只要APK注冊了廣播監(jiān)聽器,立馬就會得到回復(fù),明白當(dāng)前AP的狀態(tài)。
例如,我在開啟AP后,再打開自己的測試Demo,立馬會收到如下信息:
//對應(yīng)WIFI_AP_STATE_ENABLED,定義于WifiManager中,@SystemApi 02-20 17:48:52.470 12773-12773/? D/ZJTest: AP state: 13
手動關(guān)閉AP后可以得到如下結(jié)果:
//WIFI_AP_STATE_DISABLING 02-20 17:49:35.803 12773-12773/stark.a.is.zhang.wifitest D/ZJTest: AP state: 10 //WIFI_AP_STATE_DISABLED 02-20 17:49:36.960 12773-12773/stark.a.is.zhang.wifitest D/ZJTest: AP state: 11
public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) public WifiConfiguration getWifiApConfiguration()
@SystemApi,設(shè)置和獲取Wifi-AP的配置信息。
可以看出不論手機作為AP還是STA,在Framework中均利用WifiConfiguration抽象對應(yīng)的配置信息,包括鑒權(quán)算法、密碼、SSID、協(xié)議等。
這種設(shè)計是符合802.11協(xié)議精神的,畢竟在物理設(shè)備的角度上,AP和STA是完全對等的。只不過在實際情況中,根據(jù)各自的需求,特質(zhì)化了一些組件。
實際上從底層協(xié)議來看,僅在傳輸這個角度上,AP和STA的主要區(qū)別僅在于收到數(shù)據(jù)幀后的處理流程不同。AP收到數(shù)據(jù)幀后,發(fā)現(xiàn)目的地址不是自己,就會進入轉(zhuǎn)發(fā)流程;而STA可能就直接丟棄該數(shù)據(jù)幀了。當(dāng)然如果從控制的角度來看,即考慮通信信令,AP和STA還是主從的關(guān)系。
public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled)
@SystemApi,改變Wifi-AP的開關(guān)狀態(tài)。開啟的AP,將使用參數(shù)定義的WifiConfiguration信息。
可以看出,手機熱點對應(yīng)接口全部變成了SystemApi,因此在android的高版本上,應(yīng)用基本上是無法再操作熱點了。
以上所述是小編給大家介紹的Android連接指定Wifi的方法實例代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Android動畫 實現(xiàn)開關(guān)按鈕動畫(屬性動畫之平移動畫)實例代碼
這篇文章主要介紹了Android動畫 實現(xiàn)開關(guān)按鈕動畫(屬性動畫之平移動畫)實例代碼的相關(guān)資料,需要的朋友可以參考下2016-11-11
取消Android Studio項目與SVN關(guān)聯(lián)的方法
今天小編就為大家分享一篇關(guān)于取消Android Studio項目與SVN關(guān)聯(lián)的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12
Android之ArcSlidingHelper制作圓弧滑動效果
這篇文章主要介紹了Android之ArcSlidingHelper制作圓弧滑動效果,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08
Android數(shù)據(jù)庫greenDAO配置與使用介紹
這篇文章主要介紹了Android集成GreenDao數(shù)據(jù)庫,使用數(shù)據(jù)庫存儲時候,一般都會使用一些第三方ORM框架,比如GreenDao,本文分幾步給大家介紹Android集成GreenDao數(shù)據(jù)庫的方法,需要的朋友可以參考下2023-03-03
ViewPager頂部導(dǎo)航欄聯(lián)動效果(標題欄條目多)
這篇文章主要介紹了ViewPager頂部導(dǎo)航欄聯(lián)動效果,代碼簡單易懂,感興趣的朋友參考下吧2016-08-08

