Android连接指定Wifi的方法实例代码

作者:ZhangJianIsAStark 时间:2022-03-01 06:26:25 

本篇文章主要记录一下Android中打开Wifi、获取Wifi接入点信息及连接指接入点的方法。

自己写的demo主要用于测试接口的基本功能,因此界面及底层逻辑比较粗糙。

demo的整体界面如下所示:

Android连接指定Wifi的方法实例代码

上图中的OPEN按键负责开启Wifi;

GET按键负责获取扫描到的接入点信息。

当获取到接入点信息后,我选取了其中的名称及信号强度,以列表的形式显示在主界面下方,如下图:

Android连接指定Wifi的方法实例代码

当点击列表中的Item时,就会去连接对应的接入点。
自己的逻辑比较简单,测试时的代码,假定连接的是不许要密码或密码已知的接入点。

demo的布局文件就不介绍了,就是Button和RecyclerView。
主要记录一下,使用到的核心代码。


....................
 //Open按键点击后的逻辑
 mOpenWifiButton.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
   //WifiManager的isWifiEnabled接口,用于判断Wifi开关是否已经开启
   if (!mWifiManager.isWifiEnabled()) {
    //setWifiEnabled接口用于开启Wifi
    mWifiManager.setWifiEnabled(true);
    mMainHandler.post(mMainRunnable);
   }
  }
 });
 ....................

mMainRunnable的代码如下,主要用于判断Wifi是否开启成功。


................
private Runnable mMainRunnable = new Runnable() {
 @Override
 public void run() {
  if (mWifiManager.isWifiEnabled()) {
   //开启成功后,使能Get按键
   mGetWifiInfoButton.setEnabled(true);
  } else {
   mMainHandler.postDelayed(mMainRunnable, 1000);
  }
 }
};
..............

这部分代码,主要使用了WifiManager的公有接口,开启Wifi开关及判断开启状态。
这部分操作需要的权限是:


<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>

Get按键被点击后,对应的代码如下:


.................
 mGetWifiInfoButton.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
   if (mWifiManager.isWifiEnabled()) {
    //getScanResults接口将返回List<ScanResult>
    //ScanResult中保留了每个接入点的基本信息
    mScanResultList = mWifiManager.getScanResults();
    //多个接入点可能携带相同的信息,形成一个整体的Wifi覆盖网络
    //因此,筛除一些冗余信息
    sortList(mScanResultList);
    //我使用的是RecyclerView,得到数据后,刷新界面进行显示
    mWifiInfoRecyclerView.getAdapter().notifyDataSetChanged();
   }
  }
 });
 .................

上面这部分代码也比较简单,主要利用WifiManager的getScanResults接口,获取终端探索到的接入点信息。
其中,sortList的代码如下:


..............
private void sortList(List<ScanResult> list) {
 TreeMap<String, ScanResult> map = new TreeMap<>();
 //demo中仅按照SSID进行筛选
 //实际使用时,还可以参考信号强度等条件
 for (ScanResult scanResult : list) {
  map.put(scanResult.SSID, scanResult);
 }
 list.clear();
 list.addAll(map.values());
}
.............

这部分代码唯一需要注意的地方是,需要申明权限:


<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

同时,在高版本中还需要主动获取运行时权限。

权限的要求,是由WifiServiceImpl的实现决定的,我们以Android 7.0为例,看看对应的代码:


public List<ScanResult> getScanResults(String callingPackage) {
//这里要求的是ACCESS_WIFI_STATE
enforceAccessPermission();
............
try {
 ...........
 if (!canReadPeerMacAddresses && !isActiveNetworkScorer
   //在checkCallerCanAccessScanResults中检查了ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION
   //如果没有这两个权限,就会返回一个empty List
   && !checkCallerCanAccessScanResults(callingPackage, uid)) {
  return new ArrayList<ScanResult>();
 }
 ...........
} fianlly {
 ..........
}
}

获取到信息后,就可以显示和点击列表中的Item了。

由于自己使用的是RecyclerView,因此这部分工作全部交给了对应ViewHolder:


...............
private class ScanResultViewHolder extends RecyclerView.ViewHolder {
 private View mView;
 private TextView mWifiName;
 private TextView mWifiLevel;
 ScanResultViewHolder(View itemView) {
  super(itemView);
  mView = itemView;
  mWifiName = (TextView) itemView.findViewById(R.id.ssid);
  mWifiLevel = (TextView) itemView.findViewById(R.id.level);
 }
 void bindScanResult(final ScanResult scanResult) {
  //将接入点的名称和强度显示到界面上
  mWifiName.setText(
    getString(R.string.scan_wifi_name, "" + scanResult.SSID));
  mWifiLevel.setText(
    getString(R.string.scan_wifi_level, "" + scanResult.level));
  //点击Item后,就连接对应的接入点
  mView.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    //createWifiConfig主要用于构建一个WifiConfiguration,代码中的例子主要用于连接不需要密码的Wifi
    //WifiManager的addNetwork接口,传入WifiConfiguration后,得到对应的NetworkId
    int netId = mWifiManager.addNetwork(createWifiConfig(scanResult.SSID, "", WIFICIPHER_NOPASS));
    //WifiManager的enableNetwork接口,就可以连接到netId对应的wifi了
    //其中boolean参数,主要用于指定是否需要断开其它Wifi网络
    boolean enable = mWifiManager.enableNetwork(netId, true);
    Log.d("ZJTest", "enable: " + enable);
    //可选操作,让Wifi重新连接最近使用过的接入点
    //如果上文的enableNetwork成功,那么reconnect同样连接netId对应的网络
    //若失败,则连接之前成功过的网络
    boolean reconnect = mWifiManager.reconnect();
    Log.d("ZJTest", "reconnect: " + reconnect);
   }
  });
 }
}
.................

....................
private static final int WIFICIPHER_NOPASS = 0;
private static final int WIFICIPHER_WEP = 1;
private static final int WIFICIPHER_WPA = 2;
private WifiConfiguration createWifiConfig(String ssid, String password, int type) {
 //初始化WifiConfiguration
 WifiConfiguration config = new WifiConfiguration();
 config.allowedAuthAlgorithms.clear();
 config.allowedGroupCiphers.clear();
 config.allowedKeyManagement.clear();
 config.allowedPairwiseCiphers.clear();
 config.allowedProtocols.clear();
 //指定对应的SSID
 config.SSID = "\"" + ssid + "\"";
 //如果之前有类似的配置
 WifiConfiguration tempConfig = isExist(ssid);
 if(tempConfig != null) {
  //则清除旧有配置
  mWifiManager.removeNetwork(tempConfig.networkId);
 }
 //不需要密码的场景
 if(type == WIFICIPHER_NOPASS) {
  config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
 //以WEP加密的场景
 } else if(type == WIFICIPHER_WEP) {
  config.hiddenSSID = true;
  config.wepKeys[0]= "\""+password+"\"";
  config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
  config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
  config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
  config.wepTxKeyIndex = 0;
 //以WPA加密的场景,自己测试时,发现热点以WPA2建立时,同样可以用这种配置连接
 } else if(type == WIFICIPHER_WPA) {
  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;
}
.................
private WifiConfiguration isExist(String ssid) {
 List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
 for (WifiConfiguration config : configs) {
  if (config.SSID.equals("\""+ssid+"\"")) {
   return config;
  }
 }
 return null;
}
.................

自己写完demo后,以一个手机建立热点,分别测试了有密码和无密码的场景(对应的,需要修改createWifiConfig的传入参数)。

发现demo运行的手机在两种场景下,均能够连接到指定热点。

Demo地址如下:

https://github.com/ZhangJianIsAStark/Demos/tree/master/wifitest

在本文的最后,补充一下终端作为热点时的接口。

public boolean isWifiApEnabled()

具有@SystemApi、@hide注解的公有接口,判断手机的热点是否开启。

在Android 5.1之前,这个接口没有@SystemApi注解,

于是有很多代码会利用Java发射机制,获取该方法并判断手机热点是否开启。

现在那些老代码已经没法使用了。

现在的做法(以5.1以上为例),应该利用广播 * 监听WifiManager中定义的WIFI_AP_STATE_CHANGED_ACTION。

注意到该Action也有@SystemApi注解,所以要直接监听对应的字符串,示例如下(上面链接中的demo也有涉及):


...................
private BroadcastReceiver mBroadcastReceiver;
private void registerBroadcastReceiver() {
 mBroadcastReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
   //收到广播后,利用"wifi_state"的字段,得到AP的状态
   int state = intent.getIntExtra("wifi_state", 11);
   Log.d("ZJTest", "AP state: " + state);
  }
 };
 IntentFilter intentFilter = new IntentFilter();
 //添加Action对应的字符信息
 intentFilter.addAction("android.net.wifi.WIFI_AP_STATE_CHANGED");
 this.registerReceiver(mBroadcastReceiver, intentFilter);
}
.........
private void unregisterBroadcastReceiver() {
 this.unregisterReceiver(mBroadcastReceiver);
}
..........

我暂时没有深究Wifi模块开启AP的流程。

不过从自己的测试结果来看,Wifi开启或关闭AP时,推测发送的应该是Sticky类型的广播。

于是,只要APK注册了广播 * ,立马就会得到回复,明白当前AP的状态。

例如,我在开启AP后,再打开自己的测试Demo,立马会收到如下信息:


//对应WIFI_AP_STATE_ENABLED,定义于WifiManager中,@SystemApi
02-20 17:48:52.470 12773-12773/? D/ZJTest: AP state: 13

手动关闭AP后可以得到如下结果:


//WIFI_AP_STATE_DISABLING
02-20 17:49:35.803 12773-12773/stark.a.is.zhang.wifitest D/ZJTest: AP state: 10
//WIFI_AP_STATE_DISABLED
02-20 17:49:36.960 12773-12773/stark.a.is.zhang.wifitest D/ZJTest: AP state: 11

public boolean setWifiApConfiguration(WifiConfiguration wifiConfig)
public WifiConfiguration getWifiApConfiguration()

@SystemApi,设置和获取Wifi-AP的配置信息。

可以看出不论手机作为AP还是STA,在Framework中均利用WifiConfiguration抽象对应的配置信息,包括鉴权算法、密码、SSID、协议等。

这种设计是符合802.11协议精神的,毕竟在物理设备的角度上,AP和STA是完全对等的。只不过在实际情况中,根据各自的需求,特质化了一些组件。

实际上从底层协议来看,仅在传输这个角度上,AP和STA的主要区别仅在于收到数据帧后的处理流程不同。AP收到数据帧后,发现目的地址不是自己,就会进入转发流程;而STA可能就直接丢弃该数据帧了。当然如果从控制的角度来看,即考虑通信信令,AP和STA还是主从的关系。


public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled)

@SystemApi,改变Wifi-AP的开关状态。开启的AP,将使用参数定义的WifiConfiguration信息。

可以看出,手机热点对应接口全部变成了SystemApi,因此在android的高版本上,应用基本上是无法再操作热点了。

以上所述是小编给大家介绍的Android连接指定Wifi的方法实例代码网站的支持!

来源:http://blog.csdn.net/gaugamela/article/details/64442989

标签:android,连接,wifi
0
投稿

猜你喜欢

  • 一文看懂JAVA设计模式之工厂模式

    2023-11-27 02:30:54
  • c#入门之分支语句使用方法(三元运算符、if语句、switch语句)

    2021-12-06 00:55:20
  • Spring Cloud之服务监控turbine的示例

    2023-04-20 23:26:44
  • Java8方法引用及构造方法引用原理实例解析

    2022-07-24 08:21:22
  • 宝塔面板配置及部署javaweb教程(全网最全)

    2023-11-10 15:26:27
  • Java枚举类型enum的详解及使用

    2023-08-02 14:23:57
  • 一文带你真正理解Java中的内部类

    2023-11-24 20:42:07
  • java连接zookeeper实现zookeeper教程

    2022-09-19 03:04:35
  • WPF中不规则窗体与WindowsFormsHost控件兼容问题的解决方法

    2022-09-26 12:47:19
  • C#创建Windows服务与服务的安装、卸载

    2022-12-25 00:46:58
  • java中Class.forName的作用浅谈

    2023-11-11 12:30:26
  • 详解Java设计模式之备忘录模式的使用

    2023-09-10 09:38:32
  • Java Socket编程实例(一)- TCP基本使用

    2023-11-11 08:42:50
  • 关于C#线程的全面解析

    2021-08-14 11:14:59
  • Spring AOP如何自定义注解实现审计或日志记录(完整代码)

    2022-03-28 02:19:34
  • 基于WPF实现简单的文件夹比较工具

    2023-11-15 16:08:34
  • 安卓Android6.0权限动态获取操作示例

    2023-01-26 22:56:43
  • Java并发编程之阻塞队列详解

    2022-06-20 02:08:19
  • C语言高效编程的几招小技巧

    2023-11-02 14:12:50
  • c#高效的线程安全队列ConcurrentQueue<T>的实现

    2021-07-27 11:01:05
  • asp之家 软件编程 m.aspxhome.com