第20章 传感器与定位:让App感知世界

说实话,传感器和定位这块,是我当年从入门到进阶时觉得最「酷」的部分。你想啊,手机能知道你朝哪个方向走、知道你海拔多少、甚至能在地图上画出你的运动轨迹——这背后就是传感器和定位技术在干活。

这一章,我会带你走一遍Android传感器框架的核心用法,再聊聊GPS定位和地图SDK的集成。嗯,都是实战中高频用到的技能。

20.1 Android传感器框架

Android的传感器系统,说白了就是一套「硬件抽象层+事件回调」的架构。你不需要关心底层芯片怎么采样,只需要注册监听器,就能拿到数据。

核心类: SensorManager、Sensor、SensorEventListener

我习惯把传感器分为三类:

  • 运动传感器:加速度计、陀螺仪、重力传感器
  • 环境传感器:温度、湿度、气压、光照
  • 位置传感器:磁力计、方向传感器(已废弃,推荐用旋转矢量)

获取传感器实例的代码很简单:

SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

注册监听时,我建议你注意采样频率。别一上来就用SENSOR_DELAY_FASTEST,那会疯狂耗电。我在项目里踩过这个坑——一个计步器App,用最快频率跑了半小时,手机直接烫手。后来改成SENSOR_DELAY_NORMAL,效果完全够用。

我的习惯:游戏用SENSOR_DELAY_GAME,普通UI更新用SENSOR_DELAY_UI,省电场景用SENSOR_DELAY_NORMAL。

20.2 加速度传感器与陀螺仪

加速度传感器测量的是三个轴上的「力」,包括重力。陀螺仪测量的是角速度。两者经常配合使用,比如做姿态检测。

先看加速度计的数据格式:

@Override
public void onSensorChanged(SensorEvent event) {
    float x = event.values[0]; // 左右倾斜
    float y = event.values[1]; // 前后倾斜
    float z = event.values[2]; // 垂直方向
    // 单位:m/s²
}

陀螺仪的数据类似,但单位是弧度/秒:

float rotX = event.values[0]; // 绕X轴旋转速度
float rotY = event.values[1];
float rotZ = event.values[2];

我曾经做过一个「手机水平仪」的小工具。一开始直接用加速度计的原始数据,发现手机平放时Z轴读数不是9.8,而是9.7左右。为什么?因为传感器有零偏误差。解决办法是做个校准:让用户把手机平放5秒,取平均值作为偏移量。

注意:陀螺仪有漂移问题。长时间积分会导致角度误差累积。我建议用「互补滤波」或「卡尔曼滤波」把加速度计和陀螺仪的数据融合起来。

20.3 GPS定位(LocationManager)

Android的定位服务,核心是LocationManager。它支持三种Provider:

Provider 精度 耗电 适用场景
GPS_PROVIDER 高(3-10米) 户外导航
NETWORK_PROVIDER 中(10-100米) 城市粗略定位
PASSIVE_PROVIDER 取决于其他应用 极低 被动监听

获取位置的基本流程:

LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// 检查权限(Android 6.0+ 需要运行时权限)
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) 
    == PackageManager.PERMISSION_GRANTED) {
    Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
    lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, locationListener);
}

这里有个坑:getLastKnownLocation可能返回null。我遇到过用户第一次打开App,直接崩溃——因为没做空判断。所以一定要加判空:

if (location != null) {
    double lat = location.getLatitude();
    double lng = location.getLongitude();
} else {
    // 等待第一次定位回调
}

我的建议:优先用NETWORK_PROVIDER获取一个粗略位置,等GPS锁定后再更新。用户体验会好很多——不用干等十几秒。

20.4 百度/高德地图SDK集成

地图SDK的集成,说白了就是三步:申请Key、配置依赖、写代码。但每一步都有细节。

以高德地图为例,申请Key时需要填写SHA1和包名。我当年第一次集成时,SHA1填错了,地图一直显示空白。折腾了两小时才发现是Key不匹配。

Gradle依赖配置:

implementation 'com.amap.api:3dmap:latest.version'
implementation 'com.amap.api:location:latest.version'

在布局中添加地图:

<com.amap.api.maps.MapView
    android:id="@+id/map"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Activity中初始化:

public class MapActivity extends AppCompatActivity {
    private MapView mapView;
    private AMap aMap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_map);
        mapView = findViewById(R.id.map);
        mapView.onCreate(savedInstanceState);
        aMap = mapView.getMap();
    }
}

百度地图的流程类似,只是类名从AMap换成了BaiduMap。我个人觉得高德的文档更清晰,但百度在室内定位上更强。选哪个?看你项目需求。

注意:地图SDK的so文件很大。如果App包体积敏感,可以考虑只保留arm64-v8a架构,或者用动态下载。

20.5 地理编码与反编码

地理编码:把地址文字转成经纬度。反编码:把经纬度转成地址文字。这两个功能,地图SDK都提供了。

高德的反编码示例:

GeocodeSearch geocodeSearch = new GeocodeSearch(this);
geocodeSearch.setOnGeocodeSearchListener(new GeocodeSearch.OnGeocodeSearchListener() {
    @Override
    public void onRegeocodeSearched(RegeocodeResult result, int rCode) {
        if (rCode == 1000) {
            String address = result.getRegeocodeAddress().getFormatAddress();
            // 显示地址
        }
    }
    // 忽略编码回调
});
// 发起反编码请求
RegeocodeQuery query = new RegeocodeQuery(latLng, 200, GeocodeSearch.AMAP);
geocodeSearch.getFromLocationAsyn(query);

百度也类似,只是用GeoCoder类。我建议你注意一下「周边POI」的获取——反编码结果里通常包含附近的餐馆、地铁站等信息,做LBS应用时非常有用。

避坑:反编码有调用频率限制。高德免费版每天30万次,百度是每天30万次。如果用户频繁拖动地图,记得做节流处理。我一般用RxJava的throttleLast操作符,每500ms只发一次请求。

知识体系总览

下面这张图,是我梳理的本章核心脉络。你可以把它当作学习地图:

第20章 传感器与定位 知识体系 传感器与定位 Android传感器框架 SensorManager SensorEventListener 加速度计 & 陀螺仪 姿态检测 互补滤波 GPS定位 (LocationManager) GPS_PROVIDER NETWORK_PROVIDER 百度/高德地图SDK Key申请 & 配置 MapView & AMap 地理编码 & 反编码 地址→经纬度 经纬度→地址

这张图把本章的五个核心模块串起来了。你可以看到,传感器框架是底层基础,加速度计和陀螺仪是具体应用,GPS定位和地图SDK是上层服务,地理编码则是连接位置和文字信息的桥梁。

好了,这一章的内容就到这里。传感器和定位这块,上手不难,但要做好细节处理——权限、空判断、频率控制、Key配置,每一个都是坑。多写几次,自然就熟了。

公众号:蓝海资料掘金营,微信deep3321