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

本篇文章主要记录一下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的方法实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android获取当前已连接的wifi信号强度的方法

    本文实例讲述了Android获取当前已连接的wifi信号强度的方法,是Android程序开发中非常常见的重要技巧.分享给大家供大家参考之用.具体方法如下: 1.得到当前已连接的wifi信息 WifiManager wifi_service = (WifiManager)getSystemService(WIFI_SERVICE); WifiInfo wifiInfo = wifi_service.getConnectionInfo(); 其中wifiInfo有以下的方法: wifiinfo.ge

  • android开发教程之wifi开发示例

    1. WIFI网卡的状态WIFI网卡的状态信息都以整型变量的形式存放在 android.net.wifi.WifiManager 类中,有以下状态:WIFI_STATE_DISABLEDWIFI网卡不可用WIFI_STATE_DISABLINGWIFI网卡正在关闭WIFI_STATE_ENABLEDWIFI网卡可用WIFI_STATE_ENABLINGWIFI网卡正在打开WIFI_STATE_UNKNOWNWIFI网卡状态未知2. 操作WIFI 网卡所需的权限CHANGE_NETWORK_STA

  • Android自定义View展示Wifi信号强弱指示方法示例

    前言 最近因为工作的需要,要自定义展示Wifi信号强弱的需要,就通过利用系统广播的方式实现了一个自定义View--WifiStateView,下面话不多说了,感兴趣的朋友们一起来看看详细的介绍吧. 实现的效果图如下所示: 用不同的图片来表示Wifi信号的强弱,可以自定义Wifi信号等级 图标简陋了点,根据需要来替换即可 /** * 作者: 叶应是叶 * 时间: 2017/8/22 18:25 * 描述: */ public class WifiStateView extends AppCompa

  • Android开发之Wifi基础教程

    本文实例讲述了Android开发Wifi的基础知识.分享给大家供大家参考.具体如下: Android提供了WifiManager这个类,通过这个类可以进行wifi相关的各种操作. 通过 wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE) 可获取该类的实例. 1. 获取wifi开启状态 (只要手机的wifi打开了,即认为是开启状态,而与是否连接了某个wifi无关): boolean isOpen = wifiMana

  • Android wifi 调试详解及简单实例

    Android wifi 调试 前言: 做android开发的时候,经常遇到的一个问题就是真机调试次数多了,会导致usb口,损坏,而且长期给手机充电也会损坏手机,所以我想了想是否可以拿usb调试,网上找了下,还真可以,但都写的比较繁琐,今天我们就使用3条命令完成任务 首先我们需要使用的工具是adb,它基于tcp协议之上 1.开始我们需要给手机开启tcpip模式,这个时候需要usb线连接手机 adb tcpip 端口号(随便写个大点的比如:5555) 写完这个之后,usb就没用了 2.你需要查看你

  • Android判断是Wifi还是4G网络代码

    本文实例为大家分享了Android判断是Wifi还是4G网络的具体代码,供大家参考,具体内容如下 package com.chengxinet.bobo.utils; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.telephony.TelephonyManager; /** * Created by Ad

  • ubuntu用wifi连接android调试程序的步骤

    注:如果没有 root 权限也是可以试试,一般情况下,都需要 root 权限,才能连接成功. 1.需要确保你的开发 PC 和 Android 手机都连上了 wifi 并处于同一网段下:2.开启 usb 调试,且用 usb 将 Android 设备连接到开发 PC 上:3.进入到你的 Android SDK 的 platform-tools 目录下,执行如下命令来重启 Android 设备中的 adbd 后台程序重新侦听 TCP 的指定端口:$./adb tcpip 5555注:5555 是默认端

  • android wifi信号强度等级区分的修改介绍

    /frameworks/base/wifi/java/android/net/wifi/wifimanager.java calculateSignalLevel为计算信号等级函数,MAX_RSSI和MIN_RSSI分别为最强和最弱信号强度等级的信号强度阀值.

  • 在Android里完美实现基站和WIFI定位

    不过其实只要明白了基站/WIFI定位的原理,自己实现基站/WIFI定位其实不难.基站定位一般有几种,第一种是利用手机附近的三个基站进行三角定位,由于每个基站的位置是固定的,利用电磁波在这三个基站间中转所需要时间来算出手机所在的坐标:第二种则是利用获取最近的基站的信息,其中包括基站id,location area code.mobile country code.mobile network code和信号强度,将这些数据发送到google的定位web服务里,就能拿到当前所在的位置信息,误差一般在

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

    本篇文章主要记录一下Android中打开Wifi.获取Wifi接入点信息及连接指接入点的方法. 自己写的demo主要用于测试接口的基本功能,因此界面及底层逻辑比较粗糙. demo的整体界面如下所示: 上图中的OPEN按键负责开启Wifi: GET按键负责获取扫描到的接入点信息. 当获取到接入点信息后,我选取了其中的名称及信号强度,以列表的形式显示在主界面下方,如下图: 当点击列表中的Item时,就会去连接对应的接入点. 自己的逻辑比较简单,测试时的代码,假定连接的是不许要密码或密码已知的接入点.

  • Android连接服务器端的Socket的实例代码

    废话不多说了,直接给大家贴代码了,具体代码如下所述: package com.exa mple.esp8266; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; import android.app.Activity; import android.os.Bundle; i

  • Android手机通过蓝牙连接佳博打印机的实例代码

    所使用的打印机为佳博打印机,支持蓝牙.wifi.usb我所使用的是通过蓝牙来连接. 在网上找到一个佳博官方针对安卓开发的App源码,但是各种的跳转,没有看太懂,所以又去问度娘,找到了一个不错的文章 Android对于蓝牙开发从2.0版本的sdk才开始支持,而且模拟器不支持,测试至少需要两部手机,所以制约了很多技术人员的开发. 1. 首先,要操作蓝牙,先要在AndroidManifest.xml里加入权限 // 管理蓝牙设备的权限 <uses-permissionandroid:name="

  • Android 中隐藏虚拟按键的方法实例代码

    下面通过一段代码给大家讲解android 隐藏虚拟按键的方法,废话不多说了,大家多多看看代码和注释吧,具体代码如下所示: /** * 隐藏虚拟按键,并且全屏 */ protected void hideBottomUIMenu() { //隐藏虚拟按键,并且全屏 if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api View v = this.getWindow().getDec

  • Android 百度地图POI搜索功能实例代码

    在没介绍正文之前先给大家说下poi是什么意思. 由于工作的关系,经常在文件中会看到POI这三个字母的缩写,但是一直对POI的概念和含义没有很详细的去研究其背后代表的意思.今天下班之前,又看到了POI这三个字母,决定认认真真的搜索一些POI具体的含义. POI是英文的缩写,原来的单词是point of interest, 直译成中文就是兴趣点的意思.兴趣点这个词最早来自于导航地图厂商.地图厂商为了提供尽可能多的位置信息,花费了很大的精力去寻找诸如加油站,餐馆,酒店,景点等目的地,这些目的地其实都可

  • Android网络状态实时监听实例代码(二)

    上篇文章给大家介绍了Android 网络状态实时监听代码实例(一),感兴趣的朋友可以点击了解详情,本文接着给大家介绍android网络状态监听相关知识,具体内容如下所示: 在开发android应用时,涉及到要进行网络访问,时常需要进行网络状态的检查,以提供给用户必要的提醒.一般可以通过ConnectivityManager来完成该工作. ConnectivityManager有四个主要任务: 1.监听手机网络状态(包括GPRS,WIFI, UMTS等) 2.手机状态发生改变时,发送广播 3.当一

  • Android自定义View的实现方法实例详解

    一.自绘控件 下面我们准备来自定义一个计数器View,这个View可以响应用户的点击事件,并自动记录一共点击了多少次.新建一个CounterView继承自View,代码如下所示: 可以看到,首先我们在CounterView的构造函数中初始化了一些数据,并给这个View的本身注册了点击事件,这样当CounterView被点击的时候,onClick()方法就会得到调用.而onClick()方法中的逻辑就更加简单了,只是对mCount这个计数器加1,然后调用invalidate()方法.通过 Andr

  • Android条目拖拽删除功能实例代码

    项目中需求,要做条目条目拖拽删除效果,实际效果和QQ消息删除一样,侧滑有制定和删除. 效果图 第一步效果图 1.0自定义控件 SwipeLayout 继承FrameLayout重写里面三个构造方法,分别调用initView(). 2.0在布局中使用自定义控件 3.0在initView()方法中,创建拖拽辅辅助工具 ViewDragHelper() 该方法需要传入回调 MyCallBack() 4.0,创建MyCallBack()回调,继承ViewDragHelper.Callback 在回调中

  • Android图片实现压缩处理的实例代码

    整理文档,搜刮出一个Android图片实现压缩处理的实例代码,稍微整理精简一下做下分享. 详解: 1.获取本地图片File文件 获取BitmapFactory.Options对象 计算原始图片 目标图片宽高比 计算输出的图片宽高 2.根据宽高比计算options.inSampleSize值(缩放比例 If set to a value > 1, requests the decoder to subsample the original image, returning a smaller i

  • Android 绘制多级树形选择列表实例代码

    一.概述 前段时间有个项目的需要在Android端显示一个复选的多层树形控件,主要展示一个公司的组织架构,类似总部下面有各个部门,部门之下是组和员工等.另外需要加上展开与回收部门详情.关闭部分已开展的布局.勾选等功能. 效果图如下: 二.思路分析 毫无疑问,对于这种数据可能达到几千几万行的列表视图,我们需要选择recyclerview等具有回收item功能的控件,因此Item的状态保持放在Model中而不是View中. 由于原始数据是树形结构的,我们需要先将树形结构转换为列表数据,类似根结点 -

随机推荐