android6.0 wifi连接
2016-07-07 20:48
393 查看
Android6.0Wifi连接过程基本和之前的版本一致,但是,在获取附近热点的时候,却出现了一些差别,这差别主要包括获取权限的方式发生了改变,以及getScanResults这个函数有点怪异的行为...
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>这样就可以打印出所有的可连接wifi信息。
总结来说第一步:setWifiEnabled(true);第二步:startScan();
android6.0之前,这两个权限在AndroidMenifest文件中声明就可以了,但是android6.0中,又增加了运行时权限。运行是权限这里不多说,总之,这两个权限是要运行时获取的,在Menifest文件中声明是行不通的。当然,为了不那么麻烦,你可以把targetSdkVersion 改为23以下,这样就不存在运行时权限的问题了。如果你不想这么做,那么不妨试试android6.0的运行时权限:
首先,检查有没有该权限:
<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,你在请求权限的时候传入,在回调函数中通过它判断自己的请求有没有成功。
一旦请求成功,那么你已经成功了第一步。
如果通过第一步,你获得了权限,可以是还是无法获取到附近的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的这点设计真的很奇怪。
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>
因此,主要工作就是配置一个WifiConfiguration。主要配置项有:
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>配置安全类型:
通过以上配置,即可调用WifiManager.addNetwork连接wifi。
WifiConfiguration configration = new WifiConfiguration();
String passwd = mPwdInput.getText().toString();
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");
}
1.android6.0之前的版本获取附近的wifi热点
1.1打开和关闭wifi
setWifiEnabled(true/false);1.2扫描附近热点
startScan();之后,接受WifiManager.SCAN_RESULTS_AVAILABLE_ACTION的广播会触发,在这个广播中调用getScanResults()方法可以获得一个List<ScanResult>,它里面的每一个条目就是一个可连接的热点。1.3代码
<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>这样就可以打印出所有的可连接wifi信息。
总结来说第一步:setWifiEnabled(true);第二步:startScan();
2.android6.0获取wifi热点
然而同样的问题在android6.0中确实不可以的。原因有两个:第一:没有权限
android6.0访问wifi新增了两个权限:<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>
android6.0之前,这两个权限在AndroidMenifest文件中声明就可以了,但是android6.0中,又增加了运行时权限。运行是权限这里不多说,总之,这两个权限是要运行时获取的,在Menifest文件中声明是行不通的。当然,为了不那么麻烦,你可以把targetSdkVersion 改为23以下,这样就不存在运行时权限的问题了。如果你不想这么做,那么不妨试试android6.0的运行时权限:
首先,检查有没有该权限:
<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.SSID2.安全类型
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的连接过程就做完了。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories