如何在Android應用中連接指定的Wifi-創(chuàng)新互聯(lián)

如何在Android應用中連接指定的Wifi?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

創(chuàng)新互聯(lián)自2013年起,先為霞山等服務建站,霞山等地企業(yè),進行企業(yè)商務咨詢服務。為霞山企業(yè)網站制作PC+手機+微官網三網同步一站式服務解決您的所有建站問題。

代碼:

 ....................
  //Open按鍵點擊后的邏輯
  mOpenWifiButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    //WifiManager的isWifiEnabled接口,用于判斷Wifi開關是否已經開啟
    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開關及判斷開啟狀態(tài)。
這部分操作需要的權限是:

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
 <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>

Get按鍵被點擊后,對應的代碼如下:

.................
  mGetWifiInfoButton.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    if (mWifiManager.isWifiEnabled()) {
     //getScanResults接口將返回List<ScanResult>
     //ScanResult中保留了每個接入點的基本信息
     mScanResultList = mWifiManager.getScanResults();
     //多個接入點可能攜帶相同的信息,形成一個整體的Wifi覆蓋網絡
     //因此,篩除一些冗余信息
     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());
 }
 .............

這部分代碼唯一需要注意的地方是,需要申明權限:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

同時,在高版本中還需要主動獲取運行時權限。

權限的要求,是由WifiServiceImpl的實現(xiàn)決定的,我們以Android 7.0為例,看看對應的代碼:

public List<ScanResult> getScanResults(String callingPackage) {
 //這里要求的是ACCESS_WIFI_STATE
 enforceAccessPermission();
 ............
 try {
  ...........
  if (!canReadPeerMacAddresses && !isActiveNetworkScorer
    //在checkCallerCanAccessScanResults中檢查了ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION
    //如果沒有這兩個權限,就會返回一個empty List
    && !checkCallerCanAccessScanResults(callingPackage, uid)) {
   return new ArrayList<ScanResult>();
  }
  ...........
 } fianlly {
  ..........
 }
}

獲取到信息后,就可以顯示和點擊列表中的Item了。

由于自己使用的是RecyclerView,因此這部分工作全部交給了對應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后,就連接對應的接入點
   mView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
     //createWifiConfig主要用于構建一個WifiConfiguration,代碼中的例子主要用于連接不需要密碼的Wifi
     //WifiManager的addNetwork接口,傳入WifiConfiguration后,得到對應的NetworkId
     int netId = mWifiManager.addNetwork(createWifiConfig(scanResult.SSID, "", WIFICIPHER_NOPASS));
     //WifiManager的enableNetwork接口,就可以連接到netId對應的wifi了
     //其中boolean參數(shù),主要用于指定是否需要斷開其它Wifi網絡
     boolean enable = mWifiManager.enableNetwork(netId, true);
     Log.d("ZJTest", "enable: " + enable);
     //可選操作,讓Wifi重新連接最近使用過的接入點
     //如果上文的enableNetwork成功,那么reconnect同樣連接netId對應的網絡
     //若失敗,則連接之前成功過的網絡
     boolean reconnect = mWifiManager.reconnect();
     Log.d("ZJTest", "reconnect: " + reconnect);
    }
   });
  }
 }
 .................

以上就是連接指定Wifi的基本套路,從代碼中容易看出,關鍵問題是如何創(chuàng)建出有效的WifiConfiguration。
自己測試時,初始創(chuàng)建WifiConfiguration失敗,手機怎么都沒法連接到熱點上,后來修改后,基本功能終于能夠實現(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();
  //指定對應的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后,以一個手機建立熱點,分別測試了有密碼和無密碼的場景(對應的,需要修改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)在那些老代碼已經沒法使用了。

現(xiàn)在的做法(以5.1以上為例),應該利用廣播接收器監(jiān)聽WifiManager中定義的WIFI_AP_STATE_CHANGED_ACTION。

注意到該Action也有@SystemApi注解,所以要直接監(jiān)聽對應的字符串,示例如下(上面鏈接中的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對應的字符信息
  intentFilter.addAction("android.net.wifi.WIFI_AP_STATE_CHANGED");
  this.registerReceiver(mBroadcastReceiver, intentFilter);
 }
 .........
 private void unregisterBroadcastReceiver() {
  this.unregisterReceiver(mBroadcastReceiver);
 }
 ..........

我暫時沒有深究Wifi模塊開啟AP的流程。

不過從自己的測試結果來看,Wifi開啟或關閉AP時,推測發(fā)送的應該是Sticky類型的廣播。

于是,只要APK注冊了廣播監(jiān)聽器,立馬就會得到回復,明白當前AP的狀態(tài)。

例如,我在開啟AP后,再打開自己的測試Demo,立馬會收到如下信息:

//對應WIFI_AP_STATE_ENABLED,定義于WifiManager中,@SystemApi
02-20 17:48:52.470 12773-12773/? D/ZJTest: AP state: 13

手動關閉AP后可以得到如下結果:

//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,設置和獲取Wifi-AP的配置信息。

可以看出不論手機作為AP還是STA,在Framework中均利用WifiConfiguration抽象對應的配置信息,包括鑒權算法、密碼、SSID、協(xié)議等。

這種設計是符合802.11協(xié)議精神的,畢竟在物理設備的角度上,AP和STA是完全對等的。只不過在實際情況中,根據(jù)各自的需求,特質化了一些組件。

實際上從底層協(xié)議來看,僅在傳輸這個角度上,AP和STA的主要區(qū)別僅在于收到數(shù)據(jù)幀后的處理流程不同。AP收到數(shù)據(jù)幀后,發(fā)現(xiàn)目的地址不是自己,就會進入轉發(fā)流程;而STA可能就直接丟棄該數(shù)據(jù)幀了。當然如果從控制的角度來看,即考慮通信信令,AP和STA還是主從的關系。

public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled)

@SystemApi,改變Wifi-AP的開關狀態(tài)。開啟的AP,將使用參數(shù)定義的WifiConfiguration信息。

關于如何在Android應用中連接指定的Wifi問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關知識。

新聞標題:如何在Android應用中連接指定的Wifi-創(chuàng)新互聯(lián)
URL鏈接:http://bm7419.com/article8/cdijip.html

成都網站建設公司_創(chuàng)新互聯(lián),為您提供ChatGPT、企業(yè)建站、做網站、用戶體驗網站設計、定制開發(fā)

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

綿陽服務器托管