需要全部代码请点赞关注收藏后评论区留言私信~~~
一、检查是否连接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定位功能不可使用"));
}
}
}
}
创作不易 觉得有帮助请点赞关注收藏~~~