Android中点击Button通过Geofire向Firebase上传位置的代码问题求解
现有代码问题梳理
- 缺少动态位置权限申请逻辑,未获得权限无法获取用户位置
- 没有实现位置获取逻辑,代码中直接调用的
location变量未初始化,运行会崩溃 - 覆盖场景不全:仅在用户手动开启GPS后触发上传,若用户之前已经打开GPS,不会执行位置上传逻辑
onActivityResult中case未加break,会出现逻辑穿透,执行完RESULT_OK的代码后还会执行RESULT_CANCELED的提示- GeoFire存储路径重复:已经获取
Location根节点后重复调用child("Location"),会生成多余的嵌套路径
代码修改方案
1. 先添加AndroidManifest权限声明
<!-- 基础位置权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 安卓10及以上后台位置权限(如果需要后台上传的话加) --> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
2. 修改Activity代码
首先在Activity全局声明变量:
// 位置服务客户端 private FusedLocationProviderClient fusedLocationClient; // 位置更新回调 private LocationCallback locationCallback; // 权限请求码 private static final int LOCATION_PERMISSION_CODE = 100;
然后修改onCreate中的点击事件逻辑,增加权限校验、位置获取逻辑:
sendLocation = findViewById(R.id.sendButton); fusedLocationClient = LocationServices.getFusedLocationProviderClient(this); // 初始化位置回调 locationCallback = new LocationCallback() { @Override public void onLocationResult(@NonNull LocationResult locationResult) { super.onLocationResult(locationResult); if (locationResult == null) return; Location location = locationResult.getLastLocation(); // 上传位置到Geofire uploadLocationToGeoFire(location); // 如果只需要单次上传,拿到位置后就停止更新 fusedLocationClient.removeLocationUpdates(locationCallback); } }; sendLocation.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 先校验位置权限 if (ContextCompat.checkSelfPermission(SendLocation.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // 没有权限就申请权限 ActivityCompat.requestPermissions(SendLocation.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_CODE); return; } locationRequest = LocationRequest.create(); locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); locationRequest.setInterval(5000); locationRequest.setFastestInterval(2000); LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder() .addLocationRequest(locationRequest); builder.setAlwaysShow(true); Task<LocationSettingsResponse> result = LocationServices.getSettingsClient(getApplicationContext()) .checkLocationSettings(builder.build()); result.addOnCompleteListener(new OnCompleteListener<LocationSettingsResponse>() { @Override public void onComplete(@NonNull Task<LocationSettingsResponse> task) { try { LocationSettingsResponse response = task.getResult(ApiException.class); // GPS已经开启,直接获取位置 getCurrentLocation(); Toast.makeText(SendLocation.this, "GPS已开启", Toast.LENGTH_SHORT).show(); } catch (ApiException e) { switch (e.getStatusCode()) { case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: try { ResolvableApiException resolvableApiException = (ResolvableApiException)e; resolvableApiException.startResolutionForResult(SendLocation.this,REQUEST_CHECK_SETTINGS); } catch (IntentSender.SendIntentException ex) { ex.printStackTrace(); } break; case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: Toast.makeText(SendLocation.this, "设备不支持位置功能", Toast.LENGTH_SHORT).show(); break; } } } }); } });
新增权限申请回调、获取位置方法、上传位置方法:
// 权限申请结果回调 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == LOCATION_PERMISSION_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限通过,触发点击事件逻辑 sendLocation.performClick(); } else { Toast.makeText(this, "需要位置权限才能上传位置", Toast.LENGTH_SHORT).show(); } } } // 获取当前位置 private void getCurrentLocation() { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return; } // 先尝试拿最后一次缓存的位置,如果有就直接上传 fusedLocationClient.getLastLocation().addOnSuccessListener(location -> { if (location != null) { uploadLocationToGeoFire(location); } else { // 没有缓存位置就请求实时更新 fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()); } }); } // 上传位置到Geofire private void uploadLocationToGeoFire(Location location) { DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Location"); GeoFire geoFire = new GeoFire(reference.child(phoneNum.getUid())); geoFire.setLocation("location", new GeoLocation(location.getLatitude(), location.getLongitude()), (key, error) -> { if (error == null) { Toast.makeText(SendLocation.this, "位置上传成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(SendLocation.this, "位置上传失败:" + error.getMessage(), Toast.LENGTH_SHORT).show(); } }); }
修改onActivityResult,添加break修复逻辑穿透:
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CHECK_SETTINGS) { switch (resultCode) { case Activity.RESULT_OK: // 用户同意开启GPS,获取位置上传 getCurrentLocation(); Toast.makeText(this, "GPS已开启", Toast.LENGTH_SHORT).show(); break; // 必须加break,否则会穿透到下一个case case Activity.RESULT_CANCELED: Toast.makeText(this, "需要开启GPS功能", Toast.LENGTH_SHORT).show(); break; } } }
其他实现方式
- 单次定位上传:如果不需要持续获取位置,可以不用设置位置更新回调,直接使用
fusedLocationClient.getCurrentLocation()方法获取一次位置即可,代码更简洁 - 定期上传位置:使用WorkManager启动定时任务,在后台持续同步位置到Firebase,适合需要实时位置同步的场景,不会因为Activity关闭就停止
- 直接使用Firestore存储位置:Firestore原生支持
GeoPoint类型,自带地理范围查询能力,不需要额外集成Geofire库,维护成本更低 - 前台服务绑定位置更新:如果需要长时间持续获取位置,使用前台服务可以降低被系统回收的概率,稳定性更高
内容的提问来源于stack exchange,提问作者Rafael Campos
相关产品推荐
相关产品推荐

