通過wifi與設備進行通信(Android)

通過wifi與設備進行通信(Android)

最近leader決定把app與設備之間的通信改爲wifi,通過http協議實現設備之間的通信。
相對與之前的藍牙通信,的確簡單不少,但實際編碼當中也有坑。現在分享出來,希望能給需要的鞋童以幫助,有啥問題大家也可以討論一下。

切換手機wifi到指定wifi熱點

baidu或者google輸入以上內容搜索,會出現很多相關資料,但是點開之後,才發現大多都是一樣,那麼實用性怎麼樣,於是我驗證了一下。
大致思路是,首先創建WifiConfiguration,按照wifi加密方式分爲無密碼,有密碼(WEP,WPA)。

// 創建 WifiConfiguration
public WifiConfiguration CreateWifiInfo(String ssid, String password, int type) {     
      WifiConfiguration config = new WifiConfiguration();          
      config.SSID = "\"" + ssid + "\"";       
      WifiConfiguration tempConfig = this.IsExsits(ssid);               
      if(tempConfig != null) {      
          mWifiManager.removeNetwork(tempConfig.networkId);      
      }    
      if(Type == 1) //WIFICIPHER_NOPASS {     
            此處省略……
      }     
      if(Type == 2) //WIFICIPHER_WEP {  
            此處省略……      
      }     
      if(Type == 3) //WIFICIPHER_WPA {   
            此處省略……       
      }    
       return config;     
} 

從代碼中看,之中還判斷ssid是否存在,如果存在就用removeNetwork將此ssidwifi從已配置信息wifi列表中remove掉。這一步是必要的,因爲ssid就是手機wifi列表中wifi的名稱。具有相同ssidwifi可能並不是同一wifi,如果使用了上次保留的配置信息,就可能到導致自動連接wifi失敗。但此代碼因爲是很早之前寫的,所以在android6.0版本上並不適用。android6.0新特性加強了對手機權限控制,同時在wifi模塊也不再允許對已保存的wifi配置列表進行更新和刪除,這將會導致removeNetwork操作失敗。

下面看一下添加切換手機到指定wifi熱點的代碼

// 更改前寫法
public boolean addNetwork(WifiConfiguration wcg) {     
     int wcgID = mWifiManager.addNetwork(wcg);     
     boolean b =  mWifiManager.enableNetwork(wcgID, true);     
     return b;  
}

此代碼的確能使部分手機成功切換到指定wifi,但其實代碼並不規範,這將會導致在部分手機中切換失敗。下面介紹正確寫法

//更改後寫法
public boolean addNetwork(WifiConfiguration wifiConfiguration) {   
    mWifiManager.disconnect();
    int networkId = mWifiManager.addNetwork(wifiConfiguration);
    boolean res = mWifiManager.enableNetwork(networkId, true);
    mWifiManager.saveConfiguration();
    mWifiManager.reconnect();
    return res;
}

指定通過wifi進行http請求

不要以爲這樣就完了,還有個大坑在等我們

在測試過程中,突然發現,在手機wifi和數據流量同時存在時,部分手機會直接使用數據流量進行通信,這樣就導致手機與設備之間無法通信,因爲手機與設備只有處在同一局域網下才能正常通信。

這可麻煩了,於是到處到解決辦法,終於在WifiManager 這個類找到一個方法enableNetwork上面有一大段英文,我們一起看一下。

/**
 * Allow a previously configured network to be associated with. If
 * <code>disableOthers</code> is true, then all other configured
 * networks are disabled, and an attempt to connect to the selected
 * network is initiated. This may result in the asynchronous delivery
 * of state change events.
 * <p>
 * <b>Note:</b> If an application's target SDK version is
 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or newer, network
 * communication may not use Wi-Fi even if Wi-Fi is connected; traffic may
 * instead be sent through another network, such as cellular data,
 * Bluetooth tethering, or Ethernet. For example, traffic will never use a
 * Wi-Fi network that does not provide Internet access (e.g. a wireless
 * printer), if another network that does offer Internet access (e.g.
 * cellular data) is available. Applications that need to ensure that their
 * network traffic uses Wi-Fi should use APIs such as
 * {@link Network#bindSocket(java.net.Socket)},
 * {@link Network#openConnection(java.net.URL)}, or
 * {@link ConnectivityManager#bindProcessToNetwork} to do so.
 *
 * @param netId the ID of the network in the list of configured networks
 * @param disableOthers if true, disable all other networks. The way to
 * select a particular network to connect to is specify {@code true}
 * for this parameter.
 * @return {@code true} if the operation succeeded
 */

從第七行開始,大概意思就是,在應用目標版本大於或等於LOLLIPOP(5.0) 就算wifi是已連接的,網絡通信也可能不用wifi,比喻說蜂窩數據。當 wifi 與蜂窩數據同時存在時,當wifi無法使用時,系統會自動切換到蜂窩數據。這不就是我們出現的問題嗎,下面趕緊找解決辦法。接着看,app確保使用wifi進行通信,應該使用下面三個方法 APIbindSocketopenConnectionbindProcessToNetwork。這個是不是說的有點抽象,就說三個方法,不告訴到底怎麼樣。額,我們只有迅速補腦了。

    ConnectivityManager mConnectivityManager = (ConnectivityManager)Context.getSystemService(Context.CONNECTIVITY_SERVICE);
    final NetworkRequest networkRequest = new NetworkRequest.Builder()
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .build();
    mConnectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback(){
        @Override
        public void onAvailable(Network network) {
            String url = "";
            try {
                HttpURLConnection conn = (HttpURLConnection) network.openConnection(new URL(url));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });

原來解決辦法在 ConnectivityManager 這個類的方法 requestNetwork 可以指定使用 wifi 或者蜂窩數據等訪問網絡。如果要指定用蜂窩數據進行通信,將 addTransportType 設置爲TRANSPORT_CELLULAR即可。在有可用指定傳輸類型連接上後,onAvailable方法就會調用,其實主要就是獲取到 NetworkNetwork 通過 openConnection 得到 HttpURLConnection ,相信大家對HttpURLConnection十分熟悉,直接用它發起網絡請求就可以了。

聯繫我

github 個人地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章