Android WiFi開發教程(二)——WiFi的搜尋和連線
阿新 • • 發佈:2019-01-23
在上一篇中我們介紹了WiFi熱點的建立和關閉,如果你還沒閱讀過,建議先閱讀上一篇文章Android WiFi開發教程(一)——WiFi熱點的建立與關閉。 本章節主要繼續介紹WiFi的搜尋和連線。
WiFi的搜尋
/* 搜尋wifi熱點
*/
private void search() {
if (!wifiManager.isWifiEnabled()) {
//開啟wifi
wifiManager.setWifiEnabled(true);
}
wifiManager.startScan();
}
我們在開始搜尋WiFi之前確保當前WiFi功能是處於開啟狀態。如果未開啟,通過呼叫WifiManager的setWifiEnabled(boolean enable)去開啟。之後呼叫startScan()就開始掃描附近的WiFi了。而獲取掃描的結果我們就需要建立一個廣播接收者來處理。
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<ScanResult> scanResults = wifiManager.getScanResults();
wifiListAdapter.clear();
wifiListAdapter.addAll(scanResults);
}
};
系統在掃描結束後,會發出WifiManager.SCAN_RESULTS_AVAILABLE_ACTION的廣播,當我們的接收者接收到這個廣播的時候,通過WifiManager的getScanResults()就能獲取到掃描結果的集合了。ScanResult儲存著每一個WiFi的資訊。這裡我將這個集合設定到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 <= 0 && level >= -50) {
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;
}
可以看出列表展示的資料也是比較簡單,只有WiFi的名稱和訊號強度,這兩個資料也是平時用得比較多的。獲取到掃描結果後,我們就可以處理連線的邏輯了。
WiFi的連線
WiFi的連線相當於搜尋就要複雜一些。首先給列表項設定點選事件,獲取對應的ScanResult。
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
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)方法判斷系統是否儲存著當前WiFi的資訊。
private WifiConfiguration isExsits(String SSID) {
List<WifiConfiguration> existingConfigs = wifiManager.getConfiguredNetworks();
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);
}
當沒有獲取到所要連線WiFi的配置資訊時,我們就需要用到前面獲取到的加密方式判斷是否需要輸入密碼。如果加密方式為WAP或WEP時,則彈出提示框提示使用者輸入WiFi密碼。使用者輸入密碼後再呼叫connect(WifiConfiguration config)方法
如果可以獲取到所要連線WiFi的配置資訊,則直接呼叫connect(WifiConfiguration config)。
private void connect(WifiConfiguration config) {
int wcgID = wifiManager.addNetwork(config);
wifiManager.enableNetwork(wcgID, true);
}
直接呼叫WifiManger的addNetwork方法,將配置資訊傳進去後,會建立一個新的網路描述的身份並返回回來,如果返回來是-1,則表示建立失敗。獲取到身份後,呼叫enableNetwork方法就能開始連線WiFi了。到了這裡,我們連線部分就完成了一半,接下來需要繼續處理WiFi連線過程中返回來的狀態。這裡同樣我們是需要用到廣播接收者來處理。
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("已連線到網路:" + 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("連線失敗");
}
}
}
上面是廣播接收者中的關鍵程式碼。WiFi在連線的過程中系統會發出WifiManager.NETWORK_STATE_CHANGED_ACTION的廣播,當接收者接收到這條廣播時,獲取NetworkInfo的state來判斷當前的連線狀態。狀態值分別代表如下
NetworkInfo.State.DISCONNECTED //連線已斷開
NetworkInfo.State.CONNECTED //已成功連線
除了這兩個狀態之外,這裡還判斷了其他狀態
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中包含了很多連線狀態的資訊,這裡只對部分狀態進行處理,其他狀態值解析具體如下
IDLE:空閒
SCANNING:正在掃描
CONNECTING:連線中
AUTHENTICATING:正在進行身份驗證
OBTAINING_IPADDR:正在獲取Ip地址
CONNECTED:已連線
SUSPENDED:已暫停
DISCONNECTING:正在斷開連線
DISCONNECTED:已斷開
FAILED:失敗
BLOCKED:已阻止
VERIFYING_POOR_LINK:暫時關閉(網路狀況不佳)
CAPTIVE_PORTAL_CHECK:判斷是否需要瀏覽器二次登入