本篇文章主要记录一下Android中打开Wifi、获取Wifi接入点信息及连接指接入点的方法。
自己写的demo主要用于测试接口的基本功能,因此界面及底层逻辑比较粗糙。
demo的整体界面如下所示:
上图中的OPEN按键负责开启Wifi;
GET按键负责获取扫描到的接入点信息。
当获取到接入点信息后,我选取了其中的名称及信号强度,以列表的形式显示在主界面下方,如下图:
当点击列表中的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); } }); } } .................
以上就是连接指定Wifi的基本套路,从代码中容易看出,关键问题是如何创建出有效的WifiConfiguration。
自己测试时,初始创建WifiConfiguration失败,手机怎么都没法连接到热点上,后来修改后,基本功能终于能够实现:
.................... 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的方法实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对小牛知识库网站的支持!
本文向大家介绍Android 连接Wifi和创建Wifi热点的实例,包括了Android 连接Wifi和创建Wifi热点的实例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了连接Wifi和创建Wifi热点的方法。分享给大家供大家参考,具体如下: android的热点功能不可见,用了反射的技术搞定之外。 Eclipse设置语言为utf-8才能查看中文注释 上代码: MainActivity
本文向大家介绍Android编程实现wifi扫描及连接的方法,包括了Android编程实现wifi扫描及连接的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Android编程实现wifi扫描及连接的方法。分享给大家供大家参考,具体如下: 主界面,搜索附近WIFI信息 工具类: —–相关布局文件————– 主页面 连接页面 主页面ListView的item 主界面未搜索 到WIFI的展
本文向大家介绍Android 判断是否连接成功了指定wifi,包括了Android 判断是否连接成功了指定wifi的使用技巧和注意事项,需要的朋友参考一下 最近在做wifi的相关的东西,打印WifiInfo的时候 无意间发现一个参数,改参数可以查看是否连接成功了指定wifi,但是这是隐藏的,遂将其反射之。代码如下: 以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也
问题内容: 我需要一个有关如何通过wifi在android上处理数据的教程。Fe,我需要向PC发送一些消息并接收其他消息。我应该如何建立连接?我应该怎么做才能传输数据?如何传输? 对不起,这个假人的问题,但是我找不到好的手册。我将不胜感激,因为它提供了尽可能详尽的手册,或者提供了一些有关建立wifi连接和发送/接收简单消息的简单示例。 问题答案: 您无需使用任何API即可连接到wifi,仅当您想在
设置wifi账号和密码,连接特定的wifi网络。 请求方式: "|2|1|wifi_ssid,wifi_pwd|\r" 参数: wifi_ssid wifi账号的SSID wifi_pwd wifi账号密码 返回值: "|2|1|\r" wifi连接状态:wifi断开连接 "|2|2|\r" wifi连接状态:正在连接wifi "|2|3|ip|\r" wifi连接状态:wifi连接成功,返回OB
我是AnyLogic的新手,我正在尝试创建一个自定义网络...但我无法成功完成此任务:( 然后我有一个变量“network”,它包含年龄类之间链接的平均数。 我想要的是每个代理根据矩阵创建与其他代理的链接。 我不知道如何对一个座席说“连接到另一个具有AgeClass=3的座席” 谢谢大家的支持!!!