
本文详解如何在 android 应用中通过 locationmanager 正确获取设备当前经纬度(避免返回 0,0),并将其嵌入 url 后通过 sms 发送给联系人,涵盖权限申请、定位请求、异步处理与最佳实践。
本文详解如何在 android 应用中通过 locationmanager 正确获取设备当前经纬度(避免返回 0,0),并将其嵌入 url 后通过 sms 发送给联系人,涵盖权限申请、定位请求、异步处理与最佳实践。
在您提供的代码中,核心问题在于以下两行:
Location object = new Location("service Provider");
double lat = object.getLatitude(); double lng = object.getLongitude();这行代码并未实际获取设备位置,而是手动创建了一个空的 Location 实例——其默认经纬度恒为 (0.0, 0.0)。要真正获取用户当前位置,必须借助系统定位服务(如 FusedLocationProviderClient 或传统 LocationManager),并主动发起定位请求。
✅ 正确做法:使用 FusedLocationProviderClient(推荐,适用于 Android Oreo 及以上)
FusedLocationProviderClient 是 Google Play Services 提供的现代、高效、省电的定位 API,兼容 Android O(8.0)及以上版本(您当前使用的系统),且比原生 LocationManager 更可靠、更易集成。
1. 添加依赖(确保 app/build.gradle 中包含)
implementation 'com.google.android.gms:play-services-location:21.3.0'
2. 声明必要权限(AndroidManifest.xml)
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <!-- Android 10+ 需额外声明后台定位(若需持续追踪) --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
⚠️ 注意:ACCESS_FINE_LOCATION 是获取高精度经纬度的必需权限;仅申请 ACCESS_COARSE_LOCATION 在多数设备上仍会返回 (0,0)。
3. 动态申请权限(优化版)
您原代码中存在两个关键缺陷:
- 权限请求逻辑混乱(如 ACCESS_COARSE_LOCATION 检查却申请 ACCESS_FINE_LOCATION);
- Android Q+ 的 ACCESS_BACKGROUND_LOCATION 不应与前台权限混用同一 requestCode。
修正后的权限请求逻辑如下:
private static final int LOCATION_PERMISSION_REQUEST_CODE = 101;
private static final int SMS_CONTACT_PERMISSION_REQUEST_CODE = 102;
private void requestLocationPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
LOCATION_PERMISSION_REQUEST_CODE);
} else {
startLocationRequest();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startLocationRequest();
} else {
Toast.makeText(this, "定位权限被拒绝,无法发送位置信息", Toast.LENGTH_SHORT).show();
}
}
}4. 获取实时位置(关键步骤)
使用 FusedLocationProviderClient 请求一次最新位置:
private FusedLocationProviderClient fusedLocationClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
// ... 其他初始化代码(ListView、DB 等)...
send.setOnClickListener(v -> {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
getCurrentLocation();
} else {
requestLocationPermission();
}
});
}
private void getCurrentLocation() {
LocationRequest locationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setNumUpdates(1) // 仅获取一次
.setInterval(10000);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
fusedLocationClient.requestLocationUpdates(locationRequest,
locationCallback,
Looper.getMainLooper());
}
}
private final LocationCallback locationCallback = new LocationCallback() {
@Override
public void onLocationResult(@NonNull LocationResult locationResult) {
if (locationResult != null && locationResult.getLastLocation() != null) {
Location currentLocation = locationResult.getLastLocation();
double lat = currentLocation.getLatitude();
double lng = currentLocation.getLongitude();
// ✅ 此时 lat/lng 为真实坐标,非 0,0
String uri = String.format("https://maps.google.com/maps?q=%f,%f", lat, lng);
sendLocationSms(uri);
} else {
Toast.makeText(MainActivity.this, "无法获取位置,请检查 GPS 是否开启", Toast.LENGTH_LONG).show();
}
}
};5. 发送含位置的短信
将生成的 Google Maps 链接嵌入短信正文,并批量发送:
private void sendLocationSms(String mapUrl) {
SmsManager smsManager = SmsManager.getDefault();
DbHelper db = new DbHelper(this);
List<ContactModel> contacts = db.getAllContacts();
for (ContactModel contact : contacts) {
String message = String.format(
"紧急求助!我正处在危险中,请速来支援。\n我的实时位置:%s\n(点击链接可直接在地图中查看)",
mapUrl
);
try {
smsManager.sendTextMessage(contact.getPhoneNo(), null, message, null, null);
} catch (Exception e) {
Log.e("SMS", "发送失败:" + contact.getPhoneNo(), e);
Toast.makeText(this, "向 " + contact.getName() + " 发送失败", Toast.LENGTH_SHORT).show();
}
}
Toast.makeText(this, "位置已发送至所有联系人", Toast.LENGTH_SHORT).show();
}? 补充说明与注意事项
- 模拟器调试提示:Android Studio 模拟器需在 Extended Controls → Location 中手动设置经纬度,否则 FusedLocationProviderClient 无法返回有效值。
- GPS 硬件状态:真机测试前,请确认设备已开启「定位服务」且模式设为「高精确度」(Wi-Fi + 蓝牙 + GPS)。
- Android 12+ 变更:从 API 31 起,SEND_SMS 权限被归类为“特殊权限”,需引导用户手动开启(ACTION_MANAGE_OVERLAY_PERMISSION 不适用,应跳转设置页)。
-
用户体验优化建议:
- 添加加载状态(如 ProgressBar 或 Toast “正在获取位置…”);
- 对 onLocationResult 增加超时机制(如 Handler.postDelayed);
- 使用 Intent.ACTION_VIEW + Uri.parse(mapUrl) 提供一键地图跳转按钮,提升可操作性。
通过以上重构,您的应用将彻底告别 (0,0) 坐标陷阱,稳定、准确地获取用户当前位置,并安全、合规地完成紧急求助短信分发。










