需要全部代码请点赞关注收藏后评论区留言私信~~~
一、检查是否连接WiFi以及输出WiFi信息
传统的定位方式不适用于室内的垂直定位,原因如下: (1)卫星定位要求没有障碍物遮挡,它在户外比较精准,在室内信号就变差。 (2)基站定位依赖于运营商的通讯服务,如果身处基站信号尚未覆盖的偏僻空间,就无法使用基站定位。 室内WiFi定位纳入了IEEE的802.11标准,名叫WLAN RTT (IEEE 802.11mc)。
RTT是Round-Trip-Time的缩写,即往返时间,可以用于计算网络两端的距离
室内WiFi定义的实现步骤有以下三步
(1)检查是否连接无线网络 通过无线网络管理器WifiManager获取WiFi信息。
(2)扫描周围的无线网络 用到无线网络管理器的startScan和getScanResults两个方法。
(3)计算WiFi路由器的往返时延 通过WifiRttManager对目标路由器测距。
上网方式主要有两种 即数据连接和WiFi,不过连接管理器ConnectivityManager只能笼统地判断能否上网,并不能获知WiFi连接的详细信息,在当前网络类型是WiFi时,要想得知WiFi上网的具体信息,还需另外通过无线网络管理器WifiManager获取 它的部分方法如下
isWifiEnabled 判断WLAN功能是否开启
setWifiEnabled 开启或关闭WLAN功能
getWifiState 获取当前的WiFi连接状态
实战效果如下 会输出所连接Wifi的相关信息
代码如下
package com.example.location; import com.example.location.util.IPv4Util; import com.example.location.util.NetUtil; import android.annotation.SuppressLint; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import androidx.appcompat.app.AppCompatActivity; import android.os.Looper; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.widget.TextView; @SuppressLint("DefaultLocale") public class WifiInfoActivity extends AppCompatActivity { private static final String TAG = "WifiInfoActivity"; private TextView tv_info; // 声明一个文本视图对象 private Handler mHandler = new Handler(Looper.myLooper()); // 声明一个处理器对象 private String[] mWifiStateArray = {"正在断开", "已断开", "正在连接", "已连接", "未知"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wifi_info); tv_info = findViewById(R.id.tv_info); mHandler.postDelayed(mRefresh, 50); // 延迟50毫秒后启动网络刷新任务 } // 定义一个网络刷新任务 private Runnable mRefresh = new Runnable() { @Override public void run() { getAvailableNet(); // 获取可用的网络信息 // 延迟1秒后再次启动网络刷新任务 mHandler.postDelayed(this, 1000); } }; // 获取可用的网络信息 private void getAvailableNet() { String desc = ""; // 从系统服务中获取电话管理器 TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); // 从系统服务中获取连接管理器 ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); // 通过连接管理器获得可用的网络信息 NetworkInfo info = cm.getActiveNetworkInfo(); if (info != null && info.getState() == NetworkInfo.State.CONNECTED) { // 有网络连接 if (info.getType() == ConnectivityManager.TYPE_WIFI) { // WiFi网络(无线热点) // 从系统服务中获取无线网络管理器 WifiManager wm = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); int state = wm.getWifiState(); // 获得无线网络的状态 WifiInfo wifiInfo = wm.getConnectionInfo(); // 获得无线网络信息 String SSID = wifiInfo.getSSID(); // 获得无线网络的名称 if (TextUtils.isEmpty(SSID) || SSID.contains("unknown")) { desc = "\n当前联网的网络类型是WiFi,但未成功连接已知的WiFi信号"; } else { desc = String.format("当前联网的网络类型是WiFi,状态是%s。\nWiFi名称是:%s\n路由器MAC是:%s\nWiFi信号强度是:%d\n连接速率是:%s\n手机的IP地址是:%s\n手机的MAC地址是:%s\n网络编号是:%s\n", mWifiStateArray[state], SSID, wifiInfo.getBSSID(), wifiInfo.getRssi(), wifiInfo.getLinkSpeed(), IPv4Util.intToIp(wifiInfo.getIpAddress()), wifiInfo.getMacAddress(), wifiInfo.getNetworkId()); } } else if (info.getType() == ConnectivityManager.TYPE_MOBILE) { // 移动网络(数据连接) int net_type = info.getSubtype(); desc = String.format("\n当前联网的网络类型是%s %s", NetUtil.getNetworkTypeName(tm, net_type), NetUtil.getClassName(tm, net_type)); } else { desc = String.format("\n当前联网的网络类型是%d", info.getType()); } } else { // 无网络连接 desc = "\n当前无上网连接"; } tv_info.setText(desc); } }
二、扫描周围的无线网络
扫描周边Wifi主要用到WiFi滚力气的startScan方法和getScanResults方法,前者表示开始扫描周围的网络,后者表示获取扫描的结果列表,它们两个方法不能紧跟着,因为扫描动作是异步进行的,必须等到收到扫描结束的广播,然后在广播接收器中才能获取扫描结果
点击开始扫描后结果如下 会输出附近的WiFi信息
代码如下
package com.example.location; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; import android.os.Build; import android.os.Bundle; import android.widget.ListView; import android.widget.TextView; import com.example.location.adapter.ScanListAdapter; import java.util.HashMap; import java.util.List; import java.util.Map; @RequiresApi(api = Build.VERSION_CODES.M) public class WifiScanActivity extends AppCompatActivity { private final static String TAG = "WifiScanActivity"; private TextView tv_result; // 声明一个文本视图对象 private ListView lv_scan; // 声明一个列表视图对象 private WifiManager mWifiManager; // 声明一个WiFi管理器对象 private WifiScanReceiver mWifiScanReceiver = new WifiScanReceiver(); // 声明一个WiFi扫描接收器对象 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wifi_scan); tv_result = findViewById(R.id.tv_result); lv_scan = findViewById(R.id.lv_scan); findViewById(R.id.btn_scan).setOnClickListener(v -> mWifiManager.startScan()); // 从系统服务中获取WiFi管理器 mWifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); } @Override protected void onResume() { super.onResume(); IntentFilter filter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); registerReceiver(mWifiScanReceiver, filter); // 注册WiFi扫描的广播接收器 } @Override protected void onPause() { super.onPause(); unregisterReceiver(mWifiScanReceiver); // 注销WiFi扫描的广播接收器 } // 定义一个扫描周边WiFi的广播接收器 private class WifiScanReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 获取WiFi扫描的结果列表 List<ScanResult> scanList = mWifiManager.getScanResults(); if (scanList != null) { // 查找符合80211标准的WiFi路由器集合 Map<String, ScanResult> m80211mcMap = find80211mcResults(scanList); runOnUiThread(() -> showScanResult(scanList, m80211mcMap)); } } } // 查找符合80211标准的WiFi路由器集合 private Map<String, ScanResult> find80211mcResults(List<ScanResult> originList) { Map<String, ScanResult> resultMap = new HashMap<>(); for (ScanResult scanResult : originList) { // 遍历扫描发现的WiFi列表 if (scanResult.is80211mcResponder()) { // 符合80211标准 resultMap.put(scanResult.BSSID, scanResult); // BSSID表示MAC地址 } } return resultMap; } // 显示过滤后的WiFi扫描结果 private void showScanResult(List<ScanResult> list, Map<String, ScanResult> map) { tv_result.setText(String.format("找到%d个WiFi热点,其中有%d个支持RTT。", list.size(), map.size())); lv_scan.setAdapter(new ScanListAdapter(this, list, map)); } }
三、计算往返时延RTT
这个要求路由器具备RTT功能,还要求手机支持室内wifi定位 效果如下
代码如下
package com.example.location; import android.Manifest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; import android.net.wifi.rtt.RangingRequest; import android.net.wifi.rtt.RangingResult; import android.net.wifi.rtt.RangingResultCallback; import android.net.wifi.rtt.WifiRttManager; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.widget.TextView; import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import java.util.ArrayList; import java.util.List; @RequiresApi(api = Build.VERSION_CODES.P) public class WifiRttActivity extends AppCompatActivity { private final static String TAG = "WifiRttActivity"; private TextView tv_result; // 声明一个文本视图对象 private WifiManager mWifiManager; // 声明一个WiFi管理器对象 private WifiScanReceiver mWifiScanReceiver = new WifiScanReceiver(); // 声明一个WiFi扫描接收器对象 private WifiRttManager mRttManager; // 声明一个RTT管理器对象 private WifiRttReceiver mWifiRttReceiver = new WifiRttReceiver(); // 声明一个RTT接收器对象 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wifi_rtt); tv_result = findViewById(R.id.tv_result); findViewById(R.id.btn_indoor_rtt).setOnClickListener(v -> mWifiManager.startScan()); // 从系统服务中获取WiFi管理器 mWifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE); // 从系统服务中获取RTT管理器 mRttManager = (WifiRttManager) getSystemService(Context.WIFI_RTT_RANGING_SERVICE); if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)) { tv_result.setText("当前设备支持室内WiFi定位"); } else { tv_result.setText("当前设备不支持室内WiFi定位"); } } @Override protected void onResume() { super.onResume(); IntentFilter filterScan = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); registerReceiver(mWifiScanReceiver, filterScan); // 注册Wifi扫描的广播接收器 IntentFilter filterRtt = new IntentFilter(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED); registerReceiver(mWifiRttReceiver, filterRtt); // 注册RTT状态变更的广播接收器 } @Override protected void onPause() { super.onPause(); unregisterReceiver(mWifiScanReceiver); // 注销Wifi扫描的广播接收器 unregisterReceiver(mWifiRttReceiver); // 注销RTT状态变更的广播接收器 } // 定义一个扫描周边WiFi的广播接收器 private class WifiScanReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 获取WiFi扫描的结果列表 List<ScanResult> scanList = mWifiManager.getScanResults(); if (scanList != null) { // 查找符合80211标准的WiFi路由器集合 List<ScanResult> m80211mcList = find80211mcResults(scanList); runOnUiThread(() -> { String desc = String.format("找到%d个Wifi热点,其中有%d个支持RTT。", scanList.size(), m80211mcList.size()); tv_result.setText(desc); }); if (m80211mcList.size() > 0) { rangingRtt(m80211mcList.get(0)); // 测量与RTT节点之间的距离 } } } } // 查找符合80211标准的WiFi路由器集合 private List<ScanResult> find80211mcResults(List<ScanResult> originList) { List<ScanResult> resultList = new ArrayList<>(); for (ScanResult scanResult : originList) { // 遍历扫描发现的WiFi列表 if (scanResult.is80211mcResponder()) { // 符合80211标准 resultList.add(scanResult); } } return resultList; } // 测量与RTT节点之间的距离 private void rangingRtt(ScanResult scanResult) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { tv_result.setText("请先授予定位权限"); return; } RangingRequest.Builder builder = new RangingRequest.Builder(); builder.addAccessPoint(scanResult); // 添加测距入口,参数为ScanResult类型 // builder.addWifiAwarePeer(); // MacAddress类型 RangingRequest request = builder.build(); // 开始测量当前设备与指定RTT节点(路由器)之间的距离 mRttManager.startRanging(request, getMainExecutor(), new RangingResultCallback() { // 测距失败时触发 @Override public void onRangingFailure(int code) { Log.d(TAG, "RTT扫描失败,错误代码为"+code); } // 测距成功时触发 @Override public void onRangingResults(List<RangingResult> results) { for (RangingResult result : results) { if (result.getStatus() == RangingResult.STATUS_SUCCESS && scanResult.BSSID.equals(result.getMacAddress().toString())) { result.getDistanceMm(); // 获取当前设备与路由器之间的距离(单位毫米) result.getDistanceStdDevMm(); // 获取测量偏差(单位毫米) result.getRangingTimestampMillis(); // 获取测量耗时(单位毫秒) result.getRssi(); // 获取路由器的信号 } } } }); } private class WifiRttReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (mRttManager.isAvailable()) { runOnUiThread(() -> tv_result.setText("室内WiFi定位功能可以使用")); } else { runOnUiThread(() -> tv_result.setText("室内WiFi定位功能不可使用")); } } } }
创作不易 觉得有帮助请点赞关注收藏~~~