2021年如何使用FusedLocationProviderClient快速单次获取设备位置
安卓单次快速定位优化方案
现有代码核心问题
- 调用
requestLocationUpdates()后立刻执行removeLocationUpdates(),等于直接取消了定位请求,新的定位结果根本没机会返回,只能等用户多次点击重试,这是耗时久的核心原因 - 不必要的
flushLocations()调用会清空已有定位缓存,反而会增加获取位置的等待时间 - 定位成功后的地址解析(Geocoder)直接在主线程执行,可能引发卡顿
TextWatcher的afterTextChanged回调中直接写入Firebase,只要文本发生变化就会触发,会出现多次无效提交,甚至可能写入空的经纬度- 网络状态判断使用的
getNetworkInfoAPI在Android 9及以上版本已经废弃,判断结果不准确
优化实现方案
1. 全局变量补充
首先在Activity中新增两个全局变量,保存定位回调和请求对象:
private LocationCallback locationCallback; private LocationRequest locationRequest;
2. 改造getCurrentLocation()方法
@SuppressLint("MissingPermission") private void getCurrentLocation() { // 先获取缓存位置,有结果直接用 fusedLocationProviderClient.getLastLocation().addOnCompleteListener(task -> { if (task.isSuccessful() && task.getResult() != null) { Location location = task.getResult(); handleLocationResult(location); return; } // 缓存为空,请求单次新鲜定位 locationRequest = LocationRequest.create() .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) .setNumUpdates(1) // 只请求一次定位 .setInterval(0) .setFastestInterval(0); locationCallback = new LocationCallback() { @Override public void onLocationResult(@NonNull LocationResult locationResult) { super.onLocationResult(locationResult); Location newLocation = locationResult.getLastLocation(); if (newLocation != null) { handleLocationResult(newLocation); // 拿到结果后立刻移除回调,避免多余消耗 fusedLocationProviderClient.removeLocationUpdates(this); } } }; fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()); Toast.makeText(getApplicationContext(), "正在获取位置,请稍候", Toast.LENGTH_SHORT).show(); }); }
3. 新增定位结果处理方法,地址解析放子线程
private void handleLocationResult(Location location) { BuyerLatitude = String.valueOf(location.getLatitude()); BuyerLongitude = String.valueOf(location.getLongitude()); // 地址解析放子线程执行,避免阻塞主线程 new Thread(() -> { Geocoder geocoder = new Geocoder(BuyerAreaFinderActivity.this, Locale.getDefault()); try { List<Address> addresses = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1); if (addresses != null && !addresses.isEmpty()) { String address = addresses.get(0).getAddressLine(0); // 切回主线程更新UI runOnUiThread(() -> { BuyerArea.setText(address); BuyerAreaFetchBtn.setVisibility(View.INVISIBLE); // 拿到正确结果后直接写入Firebase,删除原来的TextWatcher监听逻辑 saveLocationToFirebase(); }); } } catch (IOException e) { e.printStackTrace(); // 地址解析失败直接用经纬度显示 runOnUiThread(() -> { BuyerArea.setText("纬度:" + BuyerLatitude + ",经度:" + BuyerLongitude); BuyerAreaFetchBtn.setVisibility(View.INVISIBLE); saveLocationToFirebase(); }); } }).start(); }
4. 新增Firebase写入方法
private void saveLocationToFirebase() { HashMap<String, Object> buyerLocationMap = new HashMap<>(); buyerLocationMap.put("buyerLatitude", BuyerLatitude); buyerLocationMap.put("buyerLongitude", BuyerLongitude); RootRef.child("Users").child(currentUserId).updateChildren(buyerLocationMap) .addOnCompleteListener(task -> { if (task.isSuccessful()) { continueBtn.setVisibility(View.VISIBLE); continueBtn.setEnabled(true); } else { String ErrorMessage = task.getException().toString(); Toast.makeText(BuyerAreaFinderActivity.this, "Error : " + ErrorMessage, Toast.LENGTH_SHORT).show(); } }); }
5. 其他优化点
- 删除
BuyerArea的TextChangedListener监听,避免重复提交 - 删除
getCurrentLocation()方法中的fusedLocationProviderClient.flushLocations()调用 - 网络判断如果适配Android 10以上,可以替换为
ConnectivityManager.NetworkCallback实现,适配更高系统版本 - Activity销毁时如果还没拿到定位结果,主动移除回调避免内存泄漏:
@Override protected void onDestroy() { super.onDestroy(); if (fusedLocationProviderClient != null && locationCallback != null) { fusedLocationProviderClient.removeLocationUpdates(locationCallback); } }
优化后首次获取定位的耗时可以控制在3秒以内,刚开启定位开关的场景也不会出现长时间等待的问题。
内容的提问来源于stack exchange,提问作者user16881253
相关产品推荐
相关产品推荐

