如何在Android應(yīng)用中連接指定的Wifi?針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
創(chuàng)新互聯(lián)自2013年起,先為霞山等服務(wù)建站,霞山等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為霞山企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問(wèn)題。代碼:
.................... //Open按鍵點(diǎn)擊后的邏輯 mOpenWifiButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //WifiManager的isWifiEnabled接口,用于判斷Wifi開(kāi)關(guān)是否已經(jīng)開(kāi)啟 if (!mWifiManager.isWifiEnabled()) { //setWifiEnabled接口用于開(kāi)啟Wifi mWifiManager.setWifiEnabled(true); mMainHandler.post(mMainRunnable); } } }); ....................
mMainRunnable的代碼如下,主要用于判斷Wifi是否開(kāi)啟成功。
................ private Runnable mMainRunnable = new Runnable() { @Override public void run() { if (mWifiManager.isWifiEnabled()) { //開(kāi)啟成功后,使能Get按鍵 mGetWifiInfoButton.setEnabled(true); } else { mMainHandler.postDelayed(mMainRunnable, 1000); } } }; ..............
這部分代碼,主要使用了WifiManager的公有接口,開(kāi)啟Wifi開(kāi)關(guān)及判斷開(kāi)啟狀態(tài)。
這部分操作需要的權(quán)限是:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
Get按鍵被點(diǎn)擊后,對(duì)應(yīng)的代碼如下:
................. mGetWifiInfoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mWifiManager.isWifiEnabled()) { //getScanResults接口將返回List<ScanResult> //ScanResult中保留了每個(gè)接入點(diǎn)的基本信息 mScanResultList = mWifiManager.getScanResults(); //多個(gè)接入點(diǎn)可能攜帶相同的信息,形成一個(gè)整體的Wifi覆蓋網(wǎng)絡(luò) //因此,篩除一些冗余信息 sortList(mScanResultList); //我使用的是RecyclerView,得到數(shù)據(jù)后,刷新界面進(jìn)行顯示 mWifiInfoRecyclerView.getAdapter().notifyDataSetChanged(); } } }); .................
上面這部分代碼也比較簡(jiǎn)單,主要利用WifiManager的getScanResults接口,獲取終端探索到的接入點(diǎn)信息。
其中,sortList的代碼如下:
.............. private void sortList(List<ScanResult> list) { TreeMap<String, ScanResult> map = new TreeMap<>(); //demo中僅按照SSID進(jìn)行篩選 //實(shí)際使用時(shí),還可以參考信號(hào)強(qiáng)度等條件 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"/>
同時(shí),在高版本中還需要主動(dòng)獲取運(yùn)行時(shí)權(quán)限。
權(quán)限的要求,是由WifiServiceImpl的實(shí)現(xiàn)決定的,我們以Android 7.0為例,看看對(duì)應(yīng)的代碼:
public List<ScanResult> getScanResults(String callingPackage) { //這里要求的是ACCESS_WIFI_STATE enforceAccessPermission(); ............ try { ........... if (!canReadPeerMacAddresses && !isActiveNetworkScorer //在checkCallerCanAccessScanResults中檢查了ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION //如果沒(méi)有這兩個(gè)權(quán)限,就會(huì)返回一個(gè)empty List && !checkCallerCanAccessScanResults(callingPackage, uid)) { return new ArrayList<ScanResult>(); } ........... } fianlly { .......... } }
獲取到信息后,就可以顯示和點(diǎn)擊列表中的Item了。
由于自己使用的是RecyclerView,因此這部分工作全部交給了對(duì)應(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) { //將接入點(diǎn)的名稱和強(qiáng)度顯示到界面上 mWifiName.setText( getString(R.string.scan_wifi_name, "" + scanResult.SSID)); mWifiLevel.setText( getString(R.string.scan_wifi_level, "" + scanResult.level)); //點(diǎn)擊Item后,就連接對(duì)應(yīng)的接入點(diǎn) mView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //createWifiConfig主要用于構(gòu)建一個(gè)WifiConfiguration,代碼中的例子主要用于連接不需要密碼的Wifi //WifiManager的addNetwork接口,傳入WifiConfiguration后,得到對(duì)應(yīng)的NetworkId int netId = mWifiManager.addNetwork(createWifiConfig(scanResult.SSID, "", WIFICIPHER_NOPASS)); //WifiManager的enableNetwork接口,就可以連接到netId對(duì)應(yīng)的wifi了 //其中boolean參數(shù),主要用于指定是否需要斷開(kāi)其它Wifi網(wǎng)絡(luò) boolean enable = mWifiManager.enableNetwork(netId, true); Log.d("ZJTest", "enable: " + enable); //可選操作,讓W(xué)ifi重新連接最近使用過(guò)的接入點(diǎn) //如果上文的enableNetwork成功,那么reconnect同樣連接netId對(duì)應(yīng)的網(wǎng)絡(luò) //若失敗,則連接之前成功過(guò)的網(wǎng)絡(luò) boolean reconnect = mWifiManager.reconnect(); Log.d("ZJTest", "reconnect: " + reconnect); } }); } } .................
以上就是連接指定Wifi的基本套路,從代碼中容易看出,關(guān)鍵問(wèn)題是如何創(chuàng)建出有效的WifiConfiguration。
自己測(cè)試時(shí),初始創(chuàng)建WifiConfiguration失敗,手機(jī)怎么都沒(méi)法連接到熱點(diǎn)上,后來(lái)修改后,基本功能終于能夠?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(); //指定對(duì)應(yīng)的SSID config.SSID = "\"" + ssid + "\""; //如果之前有類似的配置 WifiConfiguration tempConfig = isExist(ssid); if(tempConfig != null) { //則清除舊有配置 mWifiManager.removeNetwork(tempConfig.networkId); } //不需要密碼的場(chǎng)景 if(type == WIFICIPHER_NOPASS) { config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); //以WEP加密的場(chǎng)景 } 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加密的場(chǎng)景,自己測(cè)試時(shí),發(fā)現(xiàn)熱點(diǎn)以WPA2建立時(shí),同樣可以用這種配置連接 } 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; } .................
自己寫(xiě)完demo后,以一個(gè)手機(jī)建立熱點(diǎn),分別測(cè)試了有密碼和無(wú)密碼的場(chǎng)景(對(duì)應(yīng)的,需要修改createWifiConfig的傳入?yún)?shù))。
發(fā)現(xiàn)demo運(yùn)行的手機(jī)在兩種場(chǎng)景下,均能夠連接到指定熱點(diǎn)。
Demo地址如下:
https://github.com/ZhangJianIsAStark/Demos/tree/master/wifitest
在本文的最后,補(bǔ)充一下終端作為熱點(diǎn)時(shí)的接口。
public boolean isWifiApEnabled()
具有@SystemApi、@hide注解的公有接口,判斷手機(jī)的熱點(diǎn)是否開(kāi)啟。
在Android 5.1之前,這個(gè)接口沒(méi)有@SystemApi注解,
于是有很多代碼會(huì)利用Java發(fā)射機(jī)制,獲取該方法并判斷手機(jī)熱點(diǎn)是否開(kāi)啟。
現(xiàn)在那些老代碼已經(jīng)沒(méi)法使用了。
現(xiàn)在的做法(以5.1以上為例),應(yīng)該利用廣播接收器監(jiān)聽(tīng)WifiManager中定義的WIFI_AP_STATE_CHANGED_ACTION。
注意到該Action也有@SystemApi注解,所以要直接監(jiān)聽(tīng)對(duì)應(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對(duì)應(yīng)的字符信息 intentFilter.addAction("android.net.wifi.WIFI_AP_STATE_CHANGED"); this.registerReceiver(mBroadcastReceiver, intentFilter); } ......... private void unregisterBroadcastReceiver() { this.unregisterReceiver(mBroadcastReceiver); } ..........
我暫時(shí)沒(méi)有深究Wifi模塊開(kāi)啟AP的流程。
不過(guò)從自己的測(cè)試結(jié)果來(lái)看,Wifi開(kāi)啟或關(guān)閉AP時(shí),推測(cè)發(fā)送的應(yīng)該是Sticky類型的廣播。
于是,只要APK注冊(cè)了廣播監(jiān)聽(tīng)器,立馬就會(huì)得到回復(fù),明白當(dāng)前AP的狀態(tài)。
例如,我在開(kāi)啟AP后,再打開(kāi)自己的測(cè)試Demo,立馬會(huì)收到如下信息:
//對(duì)應(yīng)WIFI_AP_STATE_ENABLED,定義于WifiManager中,@SystemApi 02-20 17:48:52.470 12773-12773/? D/ZJTest: AP state: 13
手動(dòng)關(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的配置信息。
可以看出不論手機(jī)作為AP還是STA,在Framework中均利用WifiConfiguration抽象對(duì)應(yīng)的配置信息,包括鑒權(quán)算法、密碼、SSID、協(xié)議等。
這種設(shè)計(jì)是符合802.11協(xié)議精神的,畢竟在物理設(shè)備的角度上,AP和STA是完全對(duì)等的。只不過(guò)在實(shí)際情況中,根據(jù)各自的需求,特質(zhì)化了一些組件。
實(shí)際上從底層協(xié)議來(lái)看,僅在傳輸這個(gè)角度上,AP和STA的主要區(qū)別僅在于收到數(shù)據(jù)幀后的處理流程不同。AP收到數(shù)據(jù)幀后,發(fā)現(xiàn)目的地址不是自己,就會(huì)進(jìn)入轉(zhuǎn)發(fā)流程;而STA可能就直接丟棄該數(shù)據(jù)幀了。當(dāng)然如果從控制的角度來(lái)看,即考慮通信信令,AP和STA還是主從的關(guān)系。
public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled)
@SystemApi,改變Wifi-AP的開(kāi)關(guān)狀態(tài)。開(kāi)啟的AP,將使用參數(shù)定義的WifiConfiguration信息。
關(guān)于如何在Android應(yīng)用中連接指定的Wifi問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
新聞標(biāo)題:如何在Android應(yīng)用中連接指定的Wifi-創(chuàng)新互聯(lián)
URL鏈接:http://muchs.cn/article8/cdijip.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供ChatGPT、企業(yè)建站、做網(wǎng)站、用戶體驗(yàn)、網(wǎng)站設(shè)計(jì)、定制開(kāi)發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容