android6.0 wifi連線
Android6.0Wifi連線過程基本和之前的版本一致,但是,在獲取附近熱點的時候,卻出現了一些差別,這差別主要包括獲取許可權的方式發生了改變,以及getScanResults這個函式有點怪異的行為...
1.android6.0之前的版本獲取附近的wifi熱點
1.1開啟和關閉wifi
setWifiEnabled(true/false);
1.2掃描附近熱點
startScan();之後,接受WifiManager.SCAN_RESULTS_AVAILABLE_ACTION的廣播會觸發,在這個廣播中呼叫getScanResults()方法可以獲得一個List<ScanResult>,它裡面的每一個條目就是一個可連線的熱點。
1.3程式碼
這樣就可以打印出所有的可連線wifi資訊。<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);">public class MainActivity extends AppCompatActivity { WifiManager wifi; int size = 0; List<ScanResult> results; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE); if (wifi.isWifiEnabled() == false) { Toast.makeText(getApplicationContext(), "wifi is disabled..making it enabled", Toast.LENGTH_LONG).show(); wifi.setWifiEnabled(true); Log.d("hello","wifi enabled"); } wifi.startScan(); registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context c, Intent intent) { results = wifi.getScanResults(); size = results.size(); for(int i=0;i<size;i++){ Log.d("hello",results.get(i).toString()); } } }, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); } }</span>
總結來說第一步:setWifiEnabled(true);第二步:startScan();
2.android6.0獲取wifi熱點
然而同樣的問題在android6.0中確實不可以的。原因有兩個:
第一:沒有許可權
android6.0訪問wifi新增了兩個許可權:
android6.0之前,這兩個許可權在AndroidMenifest檔案中宣告就可以了,但是android6.0中,又增加了執行時許可權。執行是許可權這裡不多說,總之,這兩個許可權是要執行時獲取的,在Menifest檔案中宣告是行不通的。當然,為了不那麼麻煩,你可以把targetSdkVersion 改為23以下,這樣就不存在執行時許可權的問題了。如果你不想這麼做,那麼不妨試試android6.0的執行時許可權:<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);"><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /></span>
首先,檢查有沒有該許可權:
<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);"> final int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 111;
private boolean checkPermission() {
Log.d("hello","checkPermission");
List<String> permissionsList = new ArrayList<String>();
String[] permission = new String[2];
if (checkSelfPermission( Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permission[0] = Manifest.permission.ACCESS_FINE_LOCATION;
}
if (checkSelfPermission( Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
permission[1] = Manifest.permission.ACCESS_COARSE_LOCATION;
}
if(permission[0] != null || permission[1] != null){
Log.d("hello","regist Permission");
requestPermissions(permission,REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
return false;
}
return true;
}</span>
這個方法中使用checkSelfPermisson方法檢查有沒有對應許可權,最後使用requestPermissions方法請求執行時許可權。這會導致介面彈出一個詢問框,問你要不要允許什麼什麼許可權。最終請求對導致一個回撥方法被呼叫:<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);"> @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
Log.d("hello","onRequestPermissionsResult");
switch (requestCode) {
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
if (permissions.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED ||
(permissions.length == 2 && grantResults[0] == PackageManager.PERMISSION_GRANTED &&
grantResults[1] == PackageManager.PERMISSION_GRANTED)){
wifi.startScan();
Log.d("hello","permission allow");
Toast.makeText(this, "permission allow", Toast.LENGTH_LONG).show();
//list is still empty
}
else {
// Permission Denied
Toast.makeText(this, "permission deny", Toast.LENGTH_LONG).show();
Log.d("hello","permission deny");
}
break;
}
}</span>
在這個方法中你可以知道你是不是獲得了對應的許可權。這兩個文法之間有個紐帶就是REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS,你在請求許可權的時候傳入,在回撥函式中通過它判斷自己的請求有沒有成功。一旦請求成功,那麼你已經成功了第一步。
第二:GPS沒有開啟
(注意:如果你打開了的話就沒有這個問題,這是android6.0很奇怪的一點)如果通過第一步,你獲得了許可權,可以是還是無法獲取到附近的wifi熱點,那麼你不妨開啟GPS試試。是的,就是這麼神奇,開啟後你就可以獲得附近wifi熱點了。那麼,這到底是怎麼回事呢?不妨看看getScanResults方法到底做了什麼。
getScanResults是WifiManager中的一個方法,然後通過遠端系統呼叫,呼叫到了WifiServiceImpl.java中的getScanResults方法,這個方法如下:
<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);"> public List<ScanResult> getScanResults(String callingPackage) {
enforceAccessPermission();
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
boolean canReadPeerMacAddresses = checkPeersMacAddress();
boolean isActiveNetworkScorer =
NetworkScorerAppManager.isCallerActiveScorer(mContext, uid);
boolean hasInteractUsersFull = checkInteractAcrossUsersFull();
long ident = Binder.clearCallingIdentity();
try {
if (!canReadPeerMacAddresses && !isActiveNetworkScorer
&& !isLocationEnabled() ) {
return new ArrayList<ScanResult>();
}
if (!canReadPeerMacAddresses && !isActiveNetworkScorer
&& !checkCallerCanAccessScanResults(callingPackage, uid)) {
return new ArrayList<ScanResult>();
}
if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return new ArrayList<ScanResult>();
}
if (!isCurrentProfile(userId) && !hasInteractUsersFull) {
return new ArrayList<ScanResult>();
}
return mWifiStateMachine.syncGetScanResultsList();
} finally {
Binder.restoreCallingIdentity(ident);
}
}</span>
這個方法的try語句塊中,首先第一個if中,它居然會判斷:
<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);">isLocationEnabled() </span>
如果它是false,!isLocationEnabled 就為true,再加上前面兩個對位true了,那麼if裡面的就會執行,然後就會返回一個空的List.第二個if中則會判斷許可權:
<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);"> if (ActivityManager.checkUidPermission(Manifest.permission.ACCESS_FINE_LOCATION, uid)
== PackageManager.PERMISSION_GRANTED
&& isAppOppAllowed(AppOpsManager.OP_FINE_LOCATION, callingPackage, uid)) {
return true;
}
if (ActivityManager.checkUidPermission(Manifest.permission.ACCESS_COARSE_LOCATION, uid)
== PackageManager.PERMISSION_GRANTED
&& isAppOppAllowed(AppOpsManager.OP_COARSE_LOCATION, callingPackage, uid)) {
return true;
}
</span>
可以看到他就是判斷我們之前說的那兩個許可權,如果不想這麼麻煩,統統把它們幹掉也是可以的。這可能不是好主意,不過把對Location有沒有使能的判斷幹掉是合理的,誰會在獲取wifi熱點資訊的時候關注GPS有沒有開啟呢?google的這點設計真的很奇怪。3.按照訊號強弱排序
<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);"> public List<ScanResult> sortSSIDBySignalLevel(List<ScanResult> resultList){
List<ScanResult> wifiScanResults = new ArrayList<ScanResult>();
int length = resultList.size();
for (int i = 0; i < length; i++) {
for (int j = 0; j < length - i - 1; j++) {
boolean is2Swaped = false;
int lvl1 = resultList.get(j).level;
lvl1 = WifiManager
.calculateSignalLevel(lvl1, 7);
int lvl2 = resultList.get(j + 1).level;
lvl2 = WifiManager
.calculateSignalLevel(lvl2, 7);
if (lvl1 < lvl2) {
is2Swaped = true;
} else if (lvl1 == lvl2) {
String str1 = resultList.get(j).SSID;
String str2 = resultList.get(j + 1).SSID;
if (str1 != null && str2 != null
&& str1.compareToIgnoreCase(str2) > 0) {
is2Swaped = true;
} else if (str1 != null && str2 == null) {
is2Swaped = true;
}
}
if (is2Swaped) {
ScanResult temp = resultList.get(j);
resultList.set(j, resultList.get(j + 1));
resultList.set(j + 1, temp);
}
}
}
// key
wifiScanResults = resultList;
return wifiScanResults;
}</span>
4.連線wifi
wifi的連線主要使用WifiManager.addNetwork(WifiConfiguration config)方法。
因此,主要工作就是配置一個WifiConfiguration。主要配置項有:
4.1配置
1.configration.SSID
2.安全型別
2.1SECURITY_NONE,沒有密碼
<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);">config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);</span>
2.2SECURITY_WEP配置密碼:
config.wepKeys[0] = passwd;
配置安全型別之類的資訊。
<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);">config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);</span>
2.3SECURITY_PSK配置密碼:
<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);">config.preSharedKey = passwd;</span>
配置安全型別:<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);">config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);</span>
通過以上配置,即可呼叫WifiManager.addNetwork連線wifi。
4.2完整配置的示例
<span style="font-family:SimSun;font-size:14px;background-color: rgb(255, 255, 255);"> private WifiConfiguration getNetConfig(){
WifiConfiguration configration = new WifiConfiguration();
String passwd = mPwdInput.getText().toString();
configration .SSID = "\"" + SSID + "\"";
switch(getSecurity(selScanResult.capabilities)){ case SECURITY_NONE: configration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); break; case SECURITY_WEP: if(passwd.equals("")){ return null; } if (passwd.length() != 0) { int length = passwd.length(); if ((length == 10 || length == 26 || length == 58) && passwd.matches("[0-9A-Fa-f]*")) { configration.wepKeys[0] = passwd; } else { configration.wepKeys[0] = "\"" + passwd + "\""; } } else{ return null; }configration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); configration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); configration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); break; case SECURITY_PSK: if(passwd.equals("")) { return null; } if (passwd.length() != 0) { if (passwd.matches("[0-9A-Fa-f]{64}")) { configration.preSharedKey = passwd; } else { configration.preSharedKey = "\"" + passwd + "\""; } }else{ return null; } configration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); break; default: break; }</span>新增示例程式碼如下: int netId = wifiManager.addNetwork(wifiConfiguration);
if(netId>=0){
listViewConnectStatus("已儲存");
}else {
Toast.makeText(WifiSettingsActivity.this,"儲存失敗",Toast.LENGTH_SHORT).show();
listViewConnectStatus("未儲存");
}
if(!wifiManager.enableNetwork(netId,true)){
Log.d("test","enable network: "+netId+" failed");
}
這個,整個wifi的連線過程就做完了。