项目里面有个面对面快传的功能,开始自己确定需求的时候想,不就是开启一个热点,然后另一方连上,然后就使用udp通信完事儿.没想到这个开启热点然后wifi连接坑了比较久的时间,还好完成了,完成了.........
直接进入主题吧,6.0以下的开启热点网上一大堆这里就不做赘述.6.0-7.0主要就是需要添加一个修改系统设置的权限,和大部分申请动态权限的方法是一致的
//6.0需要的动态权限
public void initWithGetPermission(Activity context) {
boolean permission;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
permission = Settings.System.canWrite(context);
} else {
permission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_SETTINGS) == PackageManager.PERMISSION_GRANTED;
}
if (permission) {
init();
} else {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + context.getPackageName()));
context.startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS);
} else {
ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.WRITE_SETTINGS}, REQUEST_CODE_WRITE_SETTINGS);
}
}
}
//7.0之前开启热点的方法
public static boolean configApState(Context context, String apName) {
WifiManager wifimanager = (WifiManager) context.getSystemService(context.WIFI_SERVICE);
WifiConfiguration wificonfiguration = null;
try {
wificonfiguration = new WifiConfiguration();
wificonfiguration.SSID = apName;
if (isApOn(context)) {
wifimanager.setWifiEnabled(false);
disableAp(context);
}
Method method = wifimanager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
method.invoke(wifimanager, wificonfiguration, !isApOn(context));
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
接下来就是7.1的适配,自己之前是是用的6.0的开启热点方法连接7.1的手机,虽然发现热点能够创建成功,但是其他手机无论怎么样都连接不上这个热点,而且一直显示的连接中,这是因为从7.1开始原先的接口WifiManager.java中setWifiApEnabled方法用来打开WiFi热点已经用不了了。这个时候只能用了一种投机取巧的方法,提示用户自己去开启热点,然后我们回来判断热点是否开启,这个时候万无一失其他手机肯定能连上这个热点.后来用了下QQ的面对面快传,发现QQ也是用这种方法,英雄所见略同(手动滑稽).
private void showRequestApDialogOnN_MR1() {
final CustomDialog dialog = new CustomDialog(this);
dialog.setMessage("android7.1系统以上不支持自动开启热点,需要手动开启热点");
dialog.setPositiveButton("去开启", new CustomDialog.onPositiveOnclickListener() {
@Override
public void onYesClick() {
dialog.dismiss();
openAP();
}
});
dialog.setNegativeButton("退出", new CustomDialog.onNoOnclickListener() {
@Override
public void onNoClick() {
finishBack();
dialog.dismiss();
}
});
dialog.show();
}
//打开系统的便携式热点界面
private void openAP() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
ComponentName com = new ComponentName("com.android.settings", "com.android.settings.TetherSettings");
intent.setComponent(com);
startActivityForResult(intent, 1000);
}
//判断用户是否开启热点 getWiFiAPConfig(); 这个方法去获取本机的wifi热点的信息就不贴了
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_WRITE_SETTINGS && Settings.System.canWrite(this)) {
init();
} else if (requestCode == 1000) {
if (!ApMgr.isApOn(this)) {
showRequestApDialogOnN_MR1();
} else {
getWiFiAPConfig();
}
}
}
到此基本7.1的手机开启热点基本都能连上了
接下来是8.0的适配 关于8.0的热点网上的文章还是挺多的,我这里使用的startLocalOnlyHotspot这个方法 具体开启的方法如下
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiManager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() {
@TargetApi(Build.VERSION_CODES.O)
@Override
public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
super.onStarted(reservation);
WifiConfiguration wifiConfiguration = reservation.getWifiConfiguration();
ssid = wifiConfiguration.SSID;
mHandler.obtainMessage(2018, wifiConfiguration).sendToTarget();
}
@Override
public void onStopped() {
super.onStopped();
}
@Override
public void onFailed(int reason) {
super.onFailed(reason);
}
}, mHandler);
通过这个方法开启热点,我们是不能设置热点的名称以及密码的只能使用系统生成的随机密码和名称
以上概括了生成热点的方法,但是在连接热点的时候,有可能存在连接不上热点的情况,我针对网上某些这部分功能代码做了修改,目前在我测试的所有机型基本都是能两两连上的,如下
public static WifiConfiguration createWifiCfg(String ssid, String password, int type){
WifiConfiguration config = new WifiConfiguration();
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
config.SSID = "\"" + ssid + "\"";
if(type == WIFICIPHER_NOPASS){
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
}else if(type == WIFICIPHER_WEP){
config.hiddenSSID = true;
config.wepKeys[0]= "\""+password+"\"";
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.wepTxKeyIndex = 0;
}else if(type == WIFICIPHER_WPA){// WPA/WPA2 PSK的加密方式都可以通过此方法连上热点 也就是说我们连接热点只用分为有密码和无密码情况
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;
}
最后提下在我们生成我们需要的热点名称的时候最好后面最好带个随机数,如时间戳?避免这个名称被使用过导致我们的热点可能链接不上...
8.0的热点关闭的问题:在api26的源码中我们可以看到有一个方法是关闭热点的
这个方法是类私有的,所以我们只能去上一层发现有两个地方调用了它
很好,这个是公用的方法,不过被标志成了hide,这意味着我们只能通过反射来调用这个方法了.但是我们使用第二个方法可以更简单的关闭热点,看看另一个调用关闭方法的地方
至于这个对象LocalOnlyHotspotReservation我们在开启热点的在start的时候它会给我们返回一个它的对象,我们只用把它存起来在finish的时候调用LocalOnlyHotspotReservation的close方法就行了,如下
退出的时候